2010-05-06 00:08:41 +00:00
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////
// INCLUDE //
/////////////
# include "stdpch.h"
// Client
# include "entities.h"
# include "entity_cl.h"
# include "fx_cl.h"
# include "forage_source_cl.h"
# include "item_cl.h"
# include "pacs_client.h"
# include "time_client.h"
# include "view.h"
# include "user_entity.h"
# include "sheet_manager.h"
# include "motion/user_controls.h"
# include "net_manager.h"
# include "debug_client.h"
# include "ingame_database_manager.h"
# include "interface_v3/interface_manager.h"
# include "door_manager.h"
# include "projectile_manager.h"
# include "client_chat_manager.h"
# include "interface_v3/people_interraction.h"
# include "interface_v3/bar_manager.h"
# include "interface_v3/group_compas.h"
// 3D
# include "nel/3d/quad_tree.h"
// Interface 3D
# include "nel/3d/u_driver.h"
# include "nel/3d/u_scene.h"
# include "nel/3d/u_camera.h"
# include "nel/3d/u_text_context.h"
# include "nel/3d/u_material.h"
// Misc
# include "nel/misc/stream.h"
# include "nel/misc/common.h"
// Game share
# include "game_share/mission_desc.h"
# include "game_share/inventories.h"
// PACS
# include "nel/pacs/u_collision_desc.h"
// UI
# include "interface_v3/group_compas.h"
# include "player_r2_cl.h"
# include "r2/editor.h"
///////////
// USING //
///////////
using namespace NLMISC ;
using namespace NL3D ;
using namespace std ;
////////////
// EXTERN //
////////////
extern UDriver * Driver ;
extern UScene * Scene ;
extern UTextContext * TextContext ;
extern UCamera MainCam ;
extern CLFECOMMON : : TCLEntityId SlotUnderCursor ;
////////////
// GLOBAL //
////////////
CEntityManager EntitiesMngr ;
// Hierarchical timer
H_AUTO_DECL ( RZ_Client_Entity_Mngr_Update )
H_AUTO_DECL ( RZ_Client_Update_Post_Render )
H_AUTO_DECL ( RZ_Client_Entity_Mngr_Update_Apply_Motion )
H_AUTO_DECL ( RZ_Client_Entity_Mngr_Update_Count )
/////////////
// METHODS //
/////////////
//---------//
// PRIVATE //
//---------//
// ***************************************************************************
class CMissionTargetObserver : public ICDBNode : : IPropertyObserver
{
public :
// From ICDBNode::IPropertyObserver
virtual void update ( ICDBNode * node )
{
CCDBNodeLeaf * leaf = dynamic_cast < CCDBNodeLeaf * > ( node ) ;
if ( leaf )
{
// Get the target
uint32 oldTarget = leaf - > getOldValue32 ( ) ;
uint32 target = leaf - > getValue32 ( ) ;
// Scan all entities
CEntityCL * entity = NULL ;
if ( oldTarget )
entity = EntitiesMngr . getEntityByName ( oldTarget ) ;
if ( entity )
entity - > updateMissionTarget ( ) ;
entity = NULL ;
if ( target )
entity = EntitiesMngr . getEntityByName ( target ) ;
if ( entity )
entity - > updateMissionTarget ( ) ;
CInterfaceManager * im = CInterfaceManager : : getInstance ( ) ;
CGroupCompas * gc = dynamic_cast < CGroupCompas * > ( im - > getElementFromId ( " ui:interface:compass " ) ) ;
// if new target title is not NULL, then show the compass and make it blink to indicate new location
// please note that the first time the player login, a target has not been saved in his config file, so
// we permit the first (and only one) mission that is received to become the new compass direction (chiang the strong ...)
if ( ! IngameDbMngr . initInProgress ( ) | | ( gc & & ! gc - > isSavedTargetValid ( ) ) )
{
if ( target )
{
_PendingMissionTitle . push_back ( leaf ) ;
}
}
}
}
// When a mission name has been retrieved, update the compass to point it
void update ( )
{
std : : list < CCDBNodeLeaf * > : : iterator it = _PendingMissionTitle . begin ( ) ;
while ( it ! = _PendingMissionTitle . end ( ) )
{
std : : list < CCDBNodeLeaf * > : : iterator tmpIt = it ;
+ + it ;
CCDBNodeLeaf * leaf = * tmpIt ;
// If the previous title is not empty we probably have to clear the compass
if ( leaf - > getOldValue32 ( ) ! = 0 )
{
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
CGroupCompas * pGC = dynamic_cast < CGroupCompas * > ( pIM - > getElementFromId ( " ui:interface:compass " ) ) ;
if ( pGC = = NULL )
{
nlwarning ( " Can't retrieve compass group " ) ;
return ;
}
CCompassTarget ct = pGC - > getTarget ( ) ;
STRING_MANAGER : : CStringManagerClient * pSMC = STRING_MANAGER : : CStringManagerClient : : instance ( ) ;
ucstring oldName ;
if ( ! pSMC - > getDynString ( leaf - > getOldValue32 ( ) , oldName ) )
{
nlwarning ( " Can't get compass target name " ) ;
return ;
}
if ( ct . Name = = oldName )
{
CCompassTarget north ;
pGC - > setTarget ( north ) ;
}
}
// see if mission name has been retrieved
if ( ( * tmpIt ) - > getValue32 ( ) = = 0 )
{
_PendingMissionTitle . erase ( tmpIt ) ;
}
else
{
// TODO : maybe the following code could be include in CGroupMap::checkCoords, but it is not called when the map is not visible...
STRING_MANAGER : : CStringManagerClient * pSMC = STRING_MANAGER : : CStringManagerClient : : instance ( ) ;
ucstring name ;
if ( pSMC - > getDynString ( ( * tmpIt ) - > getValue32 ( ) , name ) )
{
// if (_AlreadyReceived.count(name) == 0)
// {
// _AlreadyReceived.insert(name);
CInterfaceManager * im = CInterfaceManager : : getInstance ( ) ;
CGroupCompas * gc = dynamic_cast < CGroupCompas * > ( im - > getElementFromId ( " ui:interface:compass " ) ) ;
if ( ! gc )
{
nlwarning ( " Can't retrieve compass group " ) ;
}
else
{
CCompassTarget ct ;
CCDBNodeLeaf * leaf = * tmpIt ;
CCDBNodeBranch * parent = leaf - > getParent ( ) ;
if ( parent )
{
CCDBNodeLeaf * x = dynamic_cast < CCDBNodeLeaf * > ( parent - > getNode ( ICDBNode : : CTextId ( " X " ) ) ) ;
CCDBNodeLeaf * y = dynamic_cast < CCDBNodeLeaf * > ( parent - > getNode ( ICDBNode : : CTextId ( " Y " ) ) ) ;
if ( x & & y )
{
CSmartPtr < CNamedEntityPositionState > tracker = new CNamedEntityPositionState ;
tracker - > build ( * tmpIt , x , y ) ;
ct . setPositionState ( tracker ) ;
ct . Name = name ;
// make the compass appear and blink
gc - > setActive ( true ) ;
gc - > setTarget ( ct ) ;
gc - > blink ( ) ;
gc - > enableBlink ( 2 ) ;
im - > setTopWindow ( gc ) ;
}
}
}
// }
_PendingMissionTitle . erase ( tmpIt ) ;
}
}
}
}
private :
std : : list < CCDBNodeLeaf * > _PendingMissionTitle ;
// std::set<ucstring> _AlreadyReceived;
} ;
//-----------------------------------------------
CMissionTargetObserver MissionTargetObserver ;
// ***************************************************************************
class CTeamUIDObserver : public ICDBNode : : IPropertyObserver
{
public :
// From ICDBNode::IPropertyObserver
virtual void update ( ICDBNode * node )
{
CCDBNodeLeaf * leaf = dynamic_cast < CCDBNodeLeaf * > ( node ) ;
if ( leaf )
{
// Get the uid
CLFECOMMON : : TClientDataSetIndex oldEntityId = leaf - > getOldValue32 ( ) ;
CLFECOMMON : : TClientDataSetIndex entityId = leaf - > getValue32 ( ) ;
// Scan all entities.
// check if removed from team
CEntityCL * entity = EntitiesMngr . getEntityByCompressedIndex ( oldEntityId ) ;
if ( entity )
entity - > updateIsInTeam ( ) ;
// check if added in team
entity = EntitiesMngr . getEntityByCompressedIndex ( entityId ) ;
if ( entity )
entity - > updateIsInTeam ( ) ;
}
}
} ;
//-----------------------------------------------
CTeamUIDObserver TeamUIDObserver ;
// ***************************************************************************
class CTeamPresentObserver : public ICDBNode : : IPropertyObserver
{
public :
// From ICDBNode::IPropertyObserver
virtual void update ( ICDBNode * node )
{
CCDBNodeLeaf * leaf = dynamic_cast < CCDBNodeLeaf * > ( node ) ;
if ( leaf )
{
// Must get the NAME leaf
CCDBNodeBranch * parent = leaf - > getParent ( ) ;
if ( parent )
{
leaf = dynamic_cast < CCDBNodeLeaf * > ( parent - > getNode ( ICDBNode : : CTextId ( " UID " ) , false ) ) ;
// Get the name id
CLFECOMMON : : TClientDataSetIndex entityId = CLFECOMMON : : INVALID_CLIENT_DATASET_INDEX ;
if ( leaf )
entityId = leaf - > getValue32 ( ) ;
// Scan all entities.
// check if added/removed in team
CEntityCL * entity = EntitiesMngr . getEntityByCompressedIndex ( entityId ) ;
if ( entity )
entity - > updateIsInTeam ( ) ;
}
}
}
} ;
//-----------------------------------------------
CTeamPresentObserver TeamPresentObserver ;
// ***************************************************************************
class CAnimalUIDObserver : public ICDBNode : : IPropertyObserver
{
public :
// From ICDBNode::IPropertyObserver
virtual void update ( ICDBNode * node )
{
CCDBNodeLeaf * leaf = dynamic_cast < CCDBNodeLeaf * > ( node ) ;
if ( leaf )
{
// Get the uid
CLFECOMMON : : TClientDataSetIndex oldEntityId = leaf - > getOldValue32 ( ) ;
CLFECOMMON : : TClientDataSetIndex entityId = leaf - > getValue32 ( ) ;
// Scan all entities.
// check if removed from animal list
CEntityCL * entity = EntitiesMngr . getEntityByCompressedIndex ( oldEntityId ) ;
if ( entity )
entity - > updateIsUserAnimal ( ) ;
// check if added in animal list
entity = EntitiesMngr . getEntityByCompressedIndex ( entityId ) ;
if ( entity )
entity - > updateIsUserAnimal ( ) ;
}
}
} ;
//-----------------------------------------------
CAnimalUIDObserver AnimalUIDObserver ;
// ***************************************************************************
class CAnimalStatusObserver : public ICDBNode : : IPropertyObserver
{
public :
// From ICDBNode::IPropertyObserver
virtual void update ( ICDBNode * node )
{
CCDBNodeLeaf * leaf = dynamic_cast < CCDBNodeLeaf * > ( node ) ;
if ( leaf )
{
// Must get the NAME leaf
CCDBNodeBranch * parent = leaf - > getParent ( ) ;
if ( parent )
{
leaf = dynamic_cast < CCDBNodeLeaf * > ( parent - > getNode ( ICDBNode : : CTextId ( " UID " ) , false ) ) ;
// Get the name id
CLFECOMMON : : TClientDataSetIndex entityId = CLFECOMMON : : INVALID_CLIENT_DATASET_INDEX ;
if ( leaf )
entityId = leaf - > getValue32 ( ) ;
// Scan all entities.
// check if added/removed in animal list
CEntityCL * entity = EntitiesMngr . getEntityByCompressedIndex ( entityId ) ;
if ( entity )
entity - > updateIsUserAnimal ( ) ;
}
}
}
} ;
//-----------------------------------------------
CAnimalStatusObserver AnimalStatusObserver ;
// ***************************************************************************
//--------//
// PUBLIC //
//--------//
//-----------------------------------------------
// CEntityManager :
// Constructor.
//-----------------------------------------------
CEntityManager : : CEntityManager ( )
{
_NbMaxEntity = 0 ;
_EntitiesAllocated = 0 ;
_NbUser = 0 ;
_NbPlayer = 0 ;
_NbChar = 0 ;
_LastEntityUnderPos = NULL ;
} // CEntityManager //
//-----------------------------------------------
// ~CEntityManager :
// Destructor.
//-----------------------------------------------
CEntityManager : : ~ CEntityManager ( )
{
release ( ) ;
} // ~CEntityManager //
//-----------------------------------------------
// initialize :
//
//-----------------------------------------------
void CEntityManager : : initialize ( uint nbMaxEntity )
{
// Set the maximum number of entities.
_NbMaxEntity = nbMaxEntity ;
// if
if ( _NbMaxEntity )
{
_Entities . resize ( _NbMaxEntity , 0 ) ;
_EntityGroundFXHandle . resize ( _NbMaxEntity ) ;
}
// Add an observer on the mission database
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
uint i , j ;
for ( i = 0 ; i < MAX_NUM_MISSIONS ; i + + )
for ( j = 0 ; j < MAX_NUM_MISSION_TARGETS ; j + + )
{
pIM - > addDBObserver ( & MissionTargetObserver , " SERVER:MISSIONS: " + toString ( i ) + " :TARGET " + toString ( j ) + " :TITLE " ) ;
}
// Add an Observer to the Team database
for ( i = 0 ; i < MaxNumPeopleInTeam ; i + + )
{
pIM - > addDBObserver ( & TeamUIDObserver , toString ( TEAM_DB_PATH " :%d:UID " , i ) ) ;
pIM - > addDBObserver ( & TeamPresentObserver , toString ( TEAM_DB_PATH " :%d:NAME " , i ) ) ;
}
// Add an Observer to the Animal database
for ( i = 0 ; i < MAX_INVENTORY_ANIMAL ; i + + )
{
pIM - > addDBObserver ( & AnimalUIDObserver , toString ( " SERVER:PACK_ANIMAL:BEAST%d:UID " , i ) ) ;
pIM - > addDBObserver ( & AnimalStatusObserver , toString ( " SERVER:PACK_ANIMAL:BEAST%d:STATUS " , i ) ) ;
}
} // initialize //
//-----------------------------------------------
// release :
// Free the class and all the components.
//-----------------------------------------------
void CEntityManager : : release ( )
{
_LastEntityUnderPos = NULL ;
// Remove all entities.
for ( uint i = 0 ; i < _Entities . size ( ) ; + + i )
{
if ( _Entities [ i ] )
{
// remove from fx manager
if ( _Entities [ i ] - > supportGroundFX ( ) )
{
_GroundFXManager . remove ( _EntityGroundFXHandle [ i ] ) ;
}
delete _Entities [ i ] ;
_Entities [ i ] = 0 ;
}
}
UserEntity = NULL ;
// Clear the list.
_Entities . clear ( ) ;
// Clean the backuped list.
_BackupedChanges . clear ( ) ;
_GroundFXManager . reset ( ) ;
} // release //
//-----------------------------------------------
void CEntityManager : : reinit ( )
{
release ( ) ;
initialize ( _NbMaxEntity ) ;
}
//-----------------------------------------------
// Create an entity according to the slot and the form.
// \param uint slot : slot for the entity.
// \param uint32 form : form to create the entity.
// \param TClientDataSetIndex : persitent id while the entity is connected.
// \return CEntityCL * : pointer on the new entity.
//-----------------------------------------------
CEntityCL * CEntityManager : : create ( uint slot , uint32 form , const TNewEntityInfo & newEntityInfo )
{
// DEBUG
if ( verboseVP ( NULL , form ) )
nlinfo ( " (%05d,%03d) EM:create: slot '%u': %s " , sint32 ( T1 % 100000 ) , NetMngr . getCurrentServerTick ( ) , slot , CSheetId ( form ) . toString ( ) . c_str ( ) ) ;
// Check parameter : slot.
if ( slot > = _NbMaxEntity )
{
nlwarning ( " EM:create: Cannot create the entity, the slot '%u' is invalid. " , slot ) ;
return 0 ;
}
else
{
// Slot 0 is for the user and so should be allocated only once (at beginning of main loop).
if ( slot = = 0 & & _Entities [ 0 ] )
{
if ( newEntityInfo . DataSetIndex ! = CLFECOMMON : : INVALID_CLIENT_DATASET_INDEX )
{
// Store the dataSetId received
_Entities [ 0 ] - > dataSetId ( newEntityInfo . DataSetIndex ) ;
}
// Store the alias (although there should not be one for the slot 0!)
_Entities [ 0 ] - > npcAlias ( newEntityInfo . Alias ) ;
return 0 ;
}
}
// Remove the old one (except the user).
if ( _Entities [ slot ] )
{
nlwarning ( " EM:create: There is already an entity in the slot '%u' ! Old entity will be removed. " , slot ) ;
// remove from ground fx manager
// TODO : test if entity has ground fxs
if ( _Entities [ slot ] - > supportGroundFX ( ) )
{
_GroundFXManager . remove ( _EntityGroundFXHandle [ slot ] ) ;
}
delete _Entities [ slot ] ;
_Entities [ slot ] = 0 ;
}
// Check parameter : form.
CEntitySheet * entitySheet = SheetMngr . get ( ( CSheetId ) form ) ;
if ( entitySheet = = 0 )
{
nlwarning ( " EM:create: Attempt on create an entity with a bad form number %d (%s) for the slot '%d' trying to compute the default one. " , form , ( ( CSheetId ) form ) . toString ( ) . c_str ( ) , slot ) ;
CSheetId defaultEntity ;
if ( defaultEntity . buildSheetId ( ClientCfg . DefaultEntity ) = = false )
{
nlwarning ( " EM:create: The default entity (%s) is not in the sheetid.bin. " , ClientCfg . DefaultEntity . c_str ( ) ) ;
return 0 ;
}
entitySheet = SheetMngr . get ( defaultEntity ) ;
if ( entitySheet = = 0 )
{
nlwarning ( " EM:create: The default entity (%s) is not in the sheet manager. " , ClientCfg . DefaultEntity . c_str ( ) ) ;
return 0 ;
}
}
// Create the entity according to the type.
switch ( entitySheet - > type ( ) )
{
case CEntitySheet : : RACE_STATS :
case CEntitySheet : : CHAR :
if ( slot = = 0 )
{
nlassert ( UserEntity = = NULL ) ;
UserEntity = new CUserEntity ;
_Entities [ slot ] = UserEntity ;
}
else
{
_Entities [ slot ] = new CPlayerCL ;
}
break ;
case CEntitySheet : : FAUNA :
{
CCharacterSheet * sheet = NLMISC : : safe_cast < CCharacterSheet * > ( entitySheet ) ;
if ( ! sheet - > R2Npc ) _Entities [ slot ] = new CCharacterCL ;
else _Entities [ slot ] = new CPlayerR2CL ;
}
break ;
case CEntitySheet : : FLORA :
_Entities [ slot ] = new CCharacterCL ;
break ;
case CEntitySheet : : FX :
_Entities [ slot ] = new CFxCL ;
break ;
case CEntitySheet : : ITEM :
_Entities [ slot ] = new CItemCL ;
break ;
case CEntitySheet : : FORAGE_SOURCE :
_Entities [ slot ] = new CForageSourceCL ;
break ;
default :
pushDebugStr ( NLMISC : : toString ( " Unknown Form Type '%d' -> entity not created. " , entitySheet - > type ( ) ) ) ;
break ;
}
// If the entity has been right created.
if ( _Entities [ slot ] )
{
// Set the sheet Id.
_Entities [ slot ] - > sheetId ( ( CSheetId ) form ) ;
// Set the slot.
_Entities [ slot ] - > slot ( slot ) ;
// Set the DataSet Index. AFTER slot(), so bar manager is correctly init
_Entities [ slot ] - > dataSetId ( newEntityInfo . DataSetIndex ) ;
// Set the Mission Giver Alias
_Entities [ slot ] - > npcAlias ( newEntityInfo . Alias ) ;
// Build the entity from a sheet.
if ( _Entities [ slot ] - > build ( entitySheet ) )
{
// Apply properties backuped;
applyBackupedProperties ( slot ) ;
// register to the ground fx manager
if ( _Entities [ slot ] - > supportGroundFX ( ) )
{
_EntityGroundFXHandle [ slot ] = _GroundFXManager . add ( _Entities [ slot ] ) ;
}
}
// Entity is not valid -> REMOVE IT
else
{
// Everyone except the User
if ( slot ! = 0 )
{
nlwarning ( " EM:%d: Cannot build the Entity -> REMOVE IT " , slot ) ;
delete _Entities [ slot ] ;
_Entities [ slot ] = 0 ;
}
// The User
else
nlerror ( " EM: Cannot build the User " ) ;
}
}
// Entity Not Allocated
else
pushDebugStr ( NLMISC : : toString ( " Cannot Allocated the Entity in the slot '%d'. " , slot ) ) ;
// Log problems about the entity creation.
flushDebugStack ( NLMISC : : toString ( " Create Entity in slot '%d' with Form '%s' : " , slot , ( ( CSheetId ) form ) . toString ( ) . c_str ( ) ) ) ;
// Return a pointer on the entity created.
return _Entities [ slot ] ;
} // create //
//-----------------------------------------------
// remove :
// Delete an entity.
// \todo GUIGUI : rename into free.
// \todo GUIGUI : finish the function.
//-----------------------------------------------
bool CEntityManager : : remove ( uint slot , bool warning )
{
// DEBUG
if ( verboseVP ( NULL ) )
nlinfo ( " EM:remove: slot '%u'. " , slot ) ;
// Check parameter : slot.
if ( slot > = _NbMaxEntity )
{
nlwarning ( " CEntityManager::remove : Attempt on delete a bad slot (slot %d) " , slot ) ;
return false ;
}
// Do not delete the user.
if ( slot = = 0 )
{
nlwarning ( " CEntityManager::remove : Cannot remove the entity in the slot 0 (user slot). " ) ;
return false ;
}
// Slot not allocated.
if ( _Entities [ slot ] = = 0 )
{
if ( warning )
{
nlwarning ( " CEntityManager::remove : Attempt on delete the slot '%d' that is not allocated. " , slot ) ;
return false ;
}
}
// Remove the entity from others target.
for ( uint i = 0 ; i < _Entities . size ( ) ; + + i )
{
// This entity is not allocated.
if ( _Entities [ i ] = = 0 )
continue ;
// Inform about the slot of the entity that will be removed.
_Entities [ i ] - > slotRemoved ( slot ) ;
}
// remove ground fx
if ( _Entities [ slot ] ! = 0 )
{
if ( _Entities [ slot ] - > supportGroundFX ( ) )
{
_GroundFXManager . remove ( _EntityGroundFXHandle [ slot ] ) ;
}
}
// notify the projectile manager that entity has been removed
CProjectileManager : : getInstance ( ) . entityRemoved ( slot ) ;
// notify the Bar Manager
if ( _Entities [ slot ] )
CBarManager : : getInstance ( ) - > delEntity ( _Entities [ slot ] - > slot ( ) ) ;
// previous UnderPos?
if ( _LastEntityUnderPos = = _Entities [ slot ] )
_LastEntityUnderPos = NULL ;
// Free the slot.
delete _Entities [ slot ] ;
_Entities [ slot ] = 0 ;
// Done.
return true ;
} // remove //
//-----------------------------------------------
// removeCollision :
// Remove the collision for all entities.
//-----------------------------------------------
void CEntityManager : : removeCollision ( )
{
2010-05-13 20:45:24 +00:00
const uint nbEntities = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( uint i = 0 ; i < nbEntities ; + + i )
{
// Is the entity allocated.
if ( _Entities [ i ] = = 0 )
continue ;
// Remove the entity primitive.
_Entities [ i ] - > removePrimitive ( ) ;
// Remove the collision entity.
_Entities [ i ] - > removeCollisionEntity ( ) ;
}
} // removeCollision //
//-----------------------------------------------
// reloadAnims :
// Re-load animations (remove and load).
//-----------------------------------------------
void CEntityManager : : reloadAnims ( )
{
for ( uint i = 0 ; i < _Entities . size ( ) ; + + i )
{
if ( _Entities [ i ] )
{
// Get a reference on the current entity.
CEntityCL & entity = * ( _Entities [ i ] ) ;
// Change the playlist
entity . buildPlaylist ( ) ;
}
}
} // reloadAnims //
//-----------------------------------------------
// entity :
// Get a pointer on an entity according to the asked slot.
// \param uint slot : the asked slot.
// \return CEntityCL * : pointer on the entity or 0.
//-----------------------------------------------
CEntityCL * CEntityManager : : entity ( uint slot )
{
// Return 0 if the slot is the INVALID_SLOT
if ( slot = = CLFECOMMON : : INVALID_SLOT )
return 0 ;
// Check parameter : slot.
if ( slot > = _Entities . size ( ) )
{
nlwarning ( " EM:entity: slot '%u' is invalid. " , slot ) ;
if ( ClientCfg . Check )
nlstop ;
return 0 ;
}
// Return the entity pointer.
return _Entities [ slot ] ;
} // entity //
//-----------------------------------------------
// entitiesNearDoors :
// Return if there is an entity near a door.
// \param float openingDist : near is when you are under the 'openingDist'.
// \param const CVector& posDoor1 : first door position.
// \param const CVector& posDoor2 : second door position.
// \return bool ; 'true' if any entity is near one of the door.
//-----------------------------------------------
bool CEntityManager : : entitiesNearDoors ( float openingDist , const CVector & posDoor1 , const CVector & posDoor2 )
{
for ( uint i = 0 ; i < _NbMaxEntity ; + + i )
{
// Is the entity allocated.
if ( _Entities [ i ] = = 0 )
continue ;
// Get a reference on the current entity.
CEntityCL & entity = * ( _Entities [ i ] ) ;
// If the entity is close enough from the door -> return true.
if ( ( ( entity . pos ( ) - posDoor1 ) . sqrnorm ( ) < openingDist )
| | ( ( entity . pos ( ) - posDoor2 ) . sqrnorm ( ) < openingDist ) )
return true ;
}
// No Entity near the door.
return false ;
} // entitiesNearDoors //
//-----------------------------------------------
// getEntityListForSelection
//-----------------------------------------------
void CEntityManager : : getEntityListForSelection ( std : : vector < CEntityCL * > & entities , uint flags )
{
// According to the view (first or third person), the user can or cannot be selected.
entities . clear ( ) ;
uint firstEntity = ( flags & CEntityFilterFlag : : NotUser ) ? 1 : 0 ;
for ( uint i = firstEntity ; i < _NbMaxEntity ; + + i )
{
// Is the entity allocated and not user mount.
if ( _Entities [ i ] = = 0 | | i = = UserEntity - > mount ( ) )
continue ;
// If entity unselectable, skip
if ( ! _Entities [ i ] - > properties ( ) . selectable ( ) )
continue ;
// Apply each filter
if ( ( flags & CEntityFilterFlag : : Friend ) & & ! _Entities [ i ] - > isFriend ( ) )
continue ;
if ( ( flags & CEntityFilterFlag : : Enemy ) & & ! _Entities [ i ] - > isEnemy ( ) )
continue ;
if ( ( flags & CEntityFilterFlag : : Alive ) & & _Entities [ i ] - > isReallyDead ( ) )
continue ;
if ( ( flags & CEntityFilterFlag : : Dead ) & & ! _Entities [ i ] - > isReallyDead ( ) )
continue ;
if ( ( flags & CEntityFilterFlag : : Player ) & & ! _Entities [ i ] - > isPlayer ( ) )
continue ;
if ( ( flags & CEntityFilterFlag : : NonPlayer ) & & _Entities [ i ] - > isPlayer ( ) )
continue ;
// Insert every entity in the valid list.
entities . push_back ( _Entities [ i ] ) ;
}
}
//-----------------------------------------------
// getEntityUnderPos :
// Get the entity under the (2d) position. Return NULL if not entity under this position.
//-----------------------------------------------
struct CSortEntity
{
CEntityCL * Entity ;
float Depth ;
bool operator < ( const CSortEntity & o ) const
{
return Depth < o . Depth ;
}
} ;
CEntityCL * CEntityManager : : getEntityUnderPos ( float x , float y , float distSelection , bool & isPlayerUnderCursor )
{
H_AUTO ( RZ_Client_getEntityUnderPos )
uint i ;
// valid only if bbox still intersect
CEntityCL * precEntityUnderPos = _LastEntityUnderPos ;
bool precEntityUnderPosValid = false ;
// reset result
isPlayerUnderCursor = false ;
_LastEntityUnderPos = NULL ;
// If not initialised, return
if ( _Entities . empty ( ) )
return NULL ;
// **** list of valid entities to test
static vector < CEntityCL * > validEntities ;
uint filterFlags = CEntityFilterFlag : : NoFilter ;
getEntityListForSelection ( validEntities , filterFlags ) ;
// build the ray
CMatrix camMatrix = MainCam . getMatrix ( ) ;
CFrustum camFrust = MainCam . getFrustum ( ) ;
CViewport viewport = Driver - > getViewport ( ) ;
// Get the Ray made by the mouse.
CVector pos , dir ;
viewport . getRayWithPoint ( x , y , pos , dir , camMatrix , camFrust ) ;
// Normalize the direction.
dir . normalize ( ) ;
// **** Get entities with box intersecting the ray.
static vector < CSortEntity > intersectedEntities ;
intersectedEntities . clear ( ) ;
for ( i = 0 ; i < validEntities . size ( ) ; i + + )
{
H_AUTO ( RZ_Client_GEUP_box_intersect )
CEntityCL * entity = validEntities [ i ] ;
// if entity not visible, skip
if ( entity - > getLastClip ( ) )
continue ;
// if intersect the bbox
NLMISC : : CAABBox bbox = entity - > selectBox ( ) ;
if ( bbox . intersect ( pos , pos + dir * distSelection ) )
{
// add this entity to the list of possible entities
CSortEntity e ;
e . Entity = entity ;
e . Depth = ( bbox . getCenter ( ) - pos ) . norm ( ) ;
intersectedEntities . push_back ( e ) ;
// is it the last entity under pos?
if ( entity = = precEntityUnderPos )
precEntityUnderPosValid = true ;
}
}
// if no intersected entities, quit
if ( intersectedEntities . empty ( ) )
return NULL ;
// Compute startDistBox: nearest entity distance, but the user
float startDistBox ;
if ( intersectedEntities [ 0 ] . Entity = = UserEntity )
{
// if the nearest entity is the user, set res
isPlayerUnderCursor = true ;
// if only player intersected, return NULL!
if ( intersectedEntities . size ( ) = = 1 )
return NULL ;
// so take the second for startDistBox
startDistBox = intersectedEntities [ 1 ] . Depth ;
}
else
{
// ok, take it.
startDistBox = intersectedEntities [ 0 ] . Depth ;
}
// **** get best entity according to distance face-camera or box-ray if no face intersection
CEntityCL * entitySelected = NULL ;
float bestDistBox = FLT_MAX ;
float bestDistZ = FLT_MAX ;
for ( i = 0 ; i < intersectedEntities . size ( ) ; i + + )
{
CEntityCL * entity = intersectedEntities [ i ] . Entity ;
const NLMISC : : CAABBox & bbox = entity - > selectBox ( ) ;
// If this entity is the UserEntity, skip!!
if ( entity = = UserEntity )
continue ;
// if entity skeleton model was clipped, skip
USkeleton * skeleton = entity - > skeleton ( ) ;
if ( ! ClientCfg . Light & & skeleton & & ! skeleton - > getLastClippedState ( ) )
continue ;
H_AUTO ( RZ_Client_GEUP_face_intersect )
// *** Try get face-intersection, result in distZ
// if the entity support fast and precise intersection (and if it succeeds)
bool trueIntersectComputed = false ;
float dist2D , distZ ;
if ( ! ClientCfg . Light )
{
if ( skeleton )
{
if ( skeleton - > supportFastIntersect ( ) & & skeleton - > fastIntersect ( pos , dir , dist2D , distZ , false ) )
trueIntersectComputed = true ;
}
// get the intersection with the instance (bot object)
else if ( ! entity - > instances ( ) . empty ( ) & & ! entity - > instances ( ) [ 0 ] . Current . empty ( ) )
{
UInstance inst = entity - > instances ( ) [ 0 ] . Current ;
if ( inst . supportFastIntersect ( ) & & inst . fastIntersect ( pos , dir , dist2D , distZ , false ) )
trueIntersectComputed = true ;
}
}
// if true face-intersection not found
if ( ! trueIntersectComputed )
{
/*
this happens especially for Forage Source . but could happens for anyhting else
In this case , estimate face - instersection , with box :
Suppose full intersection , if the ray is in the 1 / 3 of the bbox
*/
// clip the ray with the box
CVector a = pos , b = pos + dir * distSelection ;
if ( ! bbox . clipSegment ( a , b ) )
continue ;
// take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
// This is false, but gives better results.
CVector m = ( a + b ) / 2 ;
// Suppose full intersection, if the ray is in the 1/3 of the bbox
CVector itToCenter = m - bbox . getCenter ( ) ;
itToCenter . maxof ( itToCenter , - itToCenter ) ;
CVector smallBoxHS = bbox . getHalfSize ( ) * 0.3f ;
smallBoxHS . maxof ( smallBoxHS , - smallBoxHS ) ;
if ( itToCenter . x < = smallBoxHS . x & & itToCenter . y < = smallBoxHS . y & & itToCenter . z < = smallBoxHS . z )
{
dist2D = 0 ;
distZ = ( m - pos ) . norm ( ) ;
}
else
{
// no intersection
dist2D = FLT_MAX ;
distZ = 0 ;
}
}
// else it's ok, dist2D and distZ are computed
// *** if intersect face, then take the best face-intersection, else use box-ray cost
// true face-col found?
if ( dist2D = = 0 )
{
// yes, get the nearest
if ( distZ < bestDistZ )
{
bestDistBox = 0 ;
bestDistZ = distZ ;
entitySelected = entity ;
}
}
// else
else
{
// if a true face-intersection has not been found for others entities
if ( bestDistZ = = FLT_MAX )
{
// get the "distance to camera" contribution.
CVector c = bbox . getCenter ( ) ;
float distCamCost = intersectedEntities [ i ] . Depth ;
// get relative to the nearest intersected entity
distCamCost - = startDistBox ;
// get the ratio "how many the ray is in the bbox"
CVector a = pos , b = pos + dir * distSelection ;
bbox . clipSegment ( a , b ) ;
// take the middle of the clipped segment. suppose that this middle is the "nearest ray point to center"
// This is false, but gives better results.
CVector m = ( a + b ) / 2 ;
// get the distance to center. NB: small entities are preferred since smaller mean lower cost
float outBBoxCost = ( m - c ) . norm ( ) ;
// the final cost is a weighted sum of the both. NB: distCamCost is in meter,
// and outBBBoxCost is meters. Hence ClientCfg.SelectionOutBBoxWeight is a factor
float boxCost = distCamCost + outBBoxCost * ClientCfg . SelectionOutBBoxWeight ;
// take the lowest cost
if ( boxCost < bestDistBox )
{
entitySelected = entity ;
bestDistBox = boxCost ;
}
}
}
}
// If precise intersection not found
if ( bestDistZ = = FLT_MAX )
{
// if the last entity under pos is valid, prefer it among all other approximate ones
if ( precEntityUnderPos & & precEntityUnderPosValid )
entitySelected = precEntityUnderPos ;
}
// return the best entity
_LastEntityUnderPos = entitySelected ;
return entitySelected ;
} // getEntityUnderPos //
//-----------------------------------------------
// getEntityInCamera
//-----------------------------------------------
CEntityCL * CEntityManager : : getEntityInCamera ( uint flags , float distSelection , CLFECOMMON : : TCLEntityId precEntity )
{
H_AUTO ( RZ_Client_getEntityInCamera )
// If not initialised, return
if ( _Entities . empty ( ) )
return NULL ;
// list of valid entities
static vector < CEntityCL * > validEntitiesTmp , validEntities ;
getEntityListForSelection ( validEntitiesTmp , flags ) ;
// Remove entities not selectable by space key
uint i ;
validEntities . clear ( ) ;
for ( i = 0 ; i < validEntitiesTmp . size ( ) ; i + + )
{
CCharacterCL * entity = dynamic_cast < CCharacterCL * > ( validEntitiesTmp [ i ] ) ;
if ( ( entity = = NULL ) | | ( entity & & entity - > isSelectableBySpace ( ) ) )
validEntities . push_back ( entity ) ;
}
// Build the camera pyramid
CMatrix camMatrix = MainCam . getMatrix ( ) ;
CFrustum camFrust = MainCam . getFrustum ( ) ;
static vector < CPlane > camPyramid ;
// No need to use worldMatrix. NB: not setuped if ClientLight.
MainCam . buildCameraPyramid ( camPyramid , false ) ;
// list of entities in screen
static vector < CSortEntity > screenEntities ;
screenEntities . clear ( ) ;
// compute distance related to the user pos (not camera one).
CVector userPos = UserEntity - > pos ( ) ;
// prefer take the direction of the camera (can select backward for instance with camera rotation)
CVector userDir = View . currentView ( ) . normed ( ) ;
// Get all entity in this pyramid, and in the dist selection
for ( i = 0 ; i < validEntities . size ( ) ; i + + )
{
CEntityCL * entity = validEntities [ i ] ;
const NLMISC : : CAABBox & b = entity - > selectBox ( ) ;
bool isIn = true ;
for ( uint j = 0 ; j < camPyramid . size ( ) ; j + + )
{
if ( ! b . clipBack ( camPyramid [ j ] ) )
{
isIn = false ;
break ;
}
}
// if In the pyramid
if ( isIn )
{
CVector dirToEntity = b . getCenter ( ) - userPos ;
CSortEntity eSelect ;
eSelect . Entity = entity ;
eSelect . Depth = dirToEntity . norm ( ) ;
// if in max distance
if ( eSelect . Depth < distSelection )
{
// The lower, the more the influence of direction (minimum should be 1)
const float dirInfluence = 1.1f ;
// modulate the depth with dot3: force take the most in front of user.
if ( eSelect . Depth > 0 )
dirToEntity / = eSelect . Depth ;
eSelect . Depth * = dirInfluence - dirToEntity * userDir ;
// append to sort list
screenEntities . push_back ( eSelect ) ;
}
}
}
// No one in screen?
if ( screenEntities . empty ( ) )
return NULL ;
// sort them increasingly
sort ( screenEntities . begin ( ) , screenEntities . end ( ) ) ;
// Try to find the precEntity in this list
uint entitySelected = 0 ;
if ( precEntity ! = CLFECOMMON : : INVALID_SLOT )
{
for ( i = 0 ; i < screenEntities . size ( ) ; i + + )
{
// if found the precEntity, get the farther one
if ( screenEntities [ i ] . Entity - > slot ( ) = = precEntity )
{
entitySelected = i + 1 ;
break ;
}
}
// reset to 0 if: no more entities, or if the max cycle is reached
if ( entitySelected > = screenEntities . size ( ) | | entitySelected > = ClientCfg . SpaceSelectionMaxCycle )
entitySelected = 0 ;
}
// found!
return screenEntities [ entitySelected ] . Entity ;
}
//-----------------------------------------------
// changeContinent :
// Continent has changed.
//-----------------------------------------------
void CEntityManager : : changeContinent ( )
{
// Re-create entities primitive.
for ( uint i = 0 ; i < _NbMaxEntity ; + + i )
{
// Is the entity allocated.
if ( _Entities [ i ] = = 0 )
continue ;
// Compute the new primitive.
_Entities [ i ] - > computePrimitive ( ) ;
// Compute the new collision entity.
_Entities [ i ] - > computeCollisionEntity ( ) ;
}
} // changeContinent //
//-----------------------------------------------
// updatePreCamera :
// Update entites before the camera position is computed.
// This update the entites position. Evaluate collisions. Compte final world position.
//-----------------------------------------------
void CEntityManager : : updatePreCamera ( )
{
H_AUTO ( RZ_Client_Entity_Mngr_Update_Pre_Cam )
uint i ;
// Build an entity list..
_ActiveEntities . reserve ( _Entities . size ( ) ) ;
_ActiveEntities . clear ( ) ;
// Reset Counters
resetCounters ( ) ;
// Update entities position.
for ( i = 0 ; i < _NbMaxEntity ; + + i )
{
// Is the entity allocated.
CEntityCL * entity = _Entities [ i ] ;
if ( entity = = 0 )
continue ;
// Count Entities
+ + _EntitiesAllocated ;
switch ( entity - > Type )
{
case CEntityCL : : User :
+ + _NbUser ; break ;
case CEntityCL : : Player :
+ + _NbPlayer ; break ;
/*case CEntityCL::NPC:
case CEntityCL : : Fauna :
case CEntityCL : : Entity :
case CEntityCL : : ForageSource : */
default :
+ + _NbChar ; break ;
}
// Update the list of Active Entities
_ActiveEntities . push_back ( CEntityReference ( i , entity ) ) ;
}
// Adjust the orientation of the NPC in trade with the user.
if ( UserEntity - > trader ( ) ! = CLFECOMMON : : INVALID_SLOT )
{
CEntityCL * trader = _Entities [ UserEntity - > trader ( ) ] ;
if ( trader )
trader - > front ( UserEntity - > pos ( ) - trader - > pos ( ) ) ;
}
// Adjust the orientation of the NPC in dyn chat with the user.
if ( UserEntity - > interlocutor ( ) ! = CLFECOMMON : : INVALID_SLOT )
{
CEntityCL * interlocutor = _Entities [ UserEntity - > interlocutor ( ) ] ;
if ( interlocutor )
interlocutor - > front ( UserEntity - > pos ( ) - interlocutor - > pos ( ) ) ;
}
// Update entities position except the User
for ( i = 1 ; i < _EntitiesAllocated ; + + i )
{
CEntityReference & activeEntity = _ActiveEntities [ i ] ;
// Get a poiner on the entity target
CEntityCL * target = entity ( activeEntity . Entity - > targetSlot ( ) ) ;
// Update the entity.
activeEntity . Entity - > updatePreCollision ( T1 , target ) ;
}
// USER
{
// Get a poiner on the entity target
CEntityCL * target = entity ( UserEntity - > targetSlot ( ) ) ;
// update user behaviour/speed/heading/vectorUp/position/bodyHeading
UserEntity - > applyMotion ( target ) ;
// Update the entity.
UserEntity - > updatePreCollision ( T1 , target ) ;
}
// Update PACS
if ( PACS )
{
// Time since last Frame
double DTEval = ( ( float ) ( T1 - T0 ) ) * 0.001f ;
PACS - > evalCollision ( DTEval , staticWI ) ; // Eval the static world.
PACS - > evalCollision ( DTEval , dynamicWI ) ; // Eval the dynamic world.
getDoorManager ( ) . getPACSTriggers ( ) ; // Copy triggers to be used in update
managePACSTriggers ( ) ;
UserEntity - > checkPos ( ) ;
}
// Update entities position.
for ( i = 0 ; i < _EntitiesAllocated ; + + i )
{
CEntityReference & activeEntity = _ActiveEntities [ i ] ;
// Get a poiner on the entity target
CEntityCL * target = entity ( activeEntity . Entity - > targetSlot ( ) ) ;
// Update the entity.
activeEntity . Entity - > updatePostCollision ( T1 , target ) ;
}
// User Orientation
UserEntity - > applyForceLook ( ) ;
getDoorManager ( ) . update ( ) ; // Check for trigger to open/close doors
MissionTargetObserver . update ( ) ;
} // updatePreCamera //
//-----------------------------------------------
// updatePostCamera :
// Update the entity (position\animation).
// Clip the primitives
// Update visual entites parameters for clipped and non-clipped primitives
// This update the entites position.
//-----------------------------------------------
void CEntityManager : : updatePostCamera ( uint clippedUpdateTime , const std : : vector < CPlane > & clippingPlanes , const CVector & camPos )
{
H_AUTO ( RZ_Client_Entity_Mngr_Update_Post_Cam )
// Build a non clipped entity list..
_VisibleEntities . reserve ( _Entities . size ( ) ) ;
_VisibleEntities . clear ( ) ;
static bool firstTime = true ;
// Clip entities position.
uint i ;
for ( i = 0 ; i < _EntitiesAllocated ; + + i )
{
CEntityReference & activeEntity = _ActiveEntities [ i ] ;
// Get a poiner on the entity target
CEntityCL * target = entity ( activeEntity . Entity - > targetSlot ( ) ) ;
// Clip it
if ( ! activeEntity . Entity - > clipped ( clippingPlanes , camPos )
| | ( R2 : : getEditor ( ) . getSelectedInstance ( ) & & R2 : : getEditor ( ) . getSelectedInstance ( ) - > getEntity ( ) = = activeEntity . Entity ) )
{
// Add to visible primitives
_VisibleEntities . push_back ( activeEntity ) ;
activeEntity . Entity - > setLastClip ( false ) ;
activeEntity . Entity - > updateVisible ( T1 , target ) ;
}
else
{
activeEntity . Entity - > setLastClip ( true ) ;
if ( firstTime )
{
// Update texture Async Loading
activeEntity . Entity - > updateAsyncTexture ( ) ;
// Update lod Texture
activeEntity . Entity - > updateLodTexture ( ) ;
}
// Update this clipped primitive at this time ?
if ( ( activeEntity . Slot & RZ_CLIPPED_UPDATE_TIME_MASK ) = = clippedUpdateTime )
{
activeEntity . Entity - > updateSomeClipped ( T1 , target ) ;
}
// Update clipped primitives
activeEntity . Entity - > updateClipped ( T1 , target ) ;
}
}
// Update visible entities post positions.
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _VisibleEntities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( i = 0 ; i < count ; + + i )
{
CEntityReference & visibleEntity = _VisibleEntities [ i ] ;
// Get a poiner on the entity target
CEntityCL * target = entity ( visibleEntity . Entity - > targetSlot ( ) ) ;
//
visibleEntity . Entity - > updateVisiblePostPos ( T1 , target ) ;
}
// update ground fx
_GroundFXManager . update ( NLMISC : : CVectorD ( camPos ) ) ;
firstTime = false ;
} // updatePostCamera //
//-----------------------------------------------
// updatePostRender :
// Update entites after the render 3D.
//-----------------------------------------------
void CEntityManager : : updatePostRender ( )
{
H_AUTO_USE ( RZ_Client_Update_Post_Render )
TextContext - > setHotSpot ( UTextContext : : MiddleMiddle ) ;
TextContext - > setFontSize ( ClientCfg . NameFontSize ) ;
CRGBA color ;
2010-05-13 20:45:24 +00:00
const uint activeCount = ( uint ) _ActiveEntities . size ( ) ;
2010-05-06 00:08:41 +00:00
uint i ;
for ( i = 0 ; i < activeCount ; i + + )
{
CEntityReference & visibleEntity = _ActiveEntities [ i ] ;
// Update in-scene interface
visibleEntity . Entity - > updateAllPostRender ( ) ;
}
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _VisibleEntities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( i = 0 ; i < count ; + + i )
{
CEntityReference & visibleEntity = _VisibleEntities [ i ] ;
// Update Visible Entities after the render.
visibleEntity . Entity - > updateVisiblePostRender ( ) ;
// Draw the entity Path.
if ( ClientCfg . ShowPath )
visibleEntity . Entity - > drawPath ( ) ;
// Draw the selection box.
if ( ClientCfg . DrawBoxes )
visibleEntity . Entity - > drawBox ( ) ;
// Display Modifiers (Dmgs/heals).
if ( 1 )
visibleEntity . Entity - > displayModifiers ( ) ;
}
// Flush any no more used Flying text. Must do it before interface display (to be sure texts are hid)
CInterfaceManager * pIM = CInterfaceManager : : getInstance ( ) ;
pIM - > FlyingTextManager . releaseNotUsedFlyingText ( ) ;
} // updatePostRender //
//-----------------------------------------------
// updateVisualProperty :
// Method to update the visual property 'prop' for the entity in 'slot'.
// \param uint slot : slot of the entity to update.
// \param uint prop : the property to udapte.
//-----------------------------------------------
void CEntityManager : : updateVisualProperty ( const NLMISC : : TGameCycle & gameCycle , const uint & slot , const uint & prop , const NLMISC : : TGameCycle & predictedInterval )
{
// INFO : log some debug informations about visual properties.
if ( verboseVP ( NULL ) )
nlinfo ( " EM:updateVP: received prop '%d' for the slot '%d'. " , prop , slot ) ;
// Check parameter : slot.
if ( slot > = _NbMaxEntity )
{
nlwarning ( " CEntityManager::updateVisualProperty : Slot '%d' is not valid. " , slot ) ;
return ;
}
// Entity still not allocated -> backup values received for the entity.
if ( _Entities [ slot ] = = 0 )
{
// INFO : log some debug informations about visual properties.
if ( verboseVP ( NULL ) )
nlinfo ( " EM:updateVP: backup the property as long as the entity is not allocated. " , prop , slot ) ;
string propName = toString ( " SERVER:Entities:E%d:P%d " , slot , prop ) ;
TProperty propty ;
propty . GC = gameCycle ;
// propty.Value = IngameDbMngr.getProp(propName);
TBackupedChanges : : iterator it = _BackupedChanges . find ( slot ) ;
// Entity does not have any changes backuped for the time.
if ( it = = _BackupedChanges . end ( ) )
{
TProperties propMap ;
propMap . insert ( make_pair ( prop , propty ) ) ;
_BackupedChanges . insert ( make_pair ( slot , propMap ) ) ;
}
// Entity already have some changes backuped.
else
{
TProperties & properties = ( * it ) . second ;
TProperties : : iterator itProp = properties . find ( prop ) ;
// This properties is still not backuped for this entity.
if ( itProp = = properties . end ( ) )
properties . insert ( make_pair ( prop , propty ) ) ;
// There is already a backuped value
else
{
nlwarning ( " EM:updateVP:%d: property '%d' already backuped. " , slot , prop ) ;
( * itProp ) . second = propty ;
}
}
}
// Entity already allocated -> apply values.
else
{
// Call the method from the entity to update the visual property.
_Entities [ slot ] - > updateVisualProperty ( gameCycle , prop , predictedInterval ) ;
}
} // updateVisualProperty //
//-----------------------------------------------
// applyBackupedProperties :
//-----------------------------------------------
void CEntityManager : : applyBackupedProperties ( uint slot )
{
TBackupedChanges : : iterator it = _BackupedChanges . find ( slot ) ;
if ( it ! = _BackupedChanges . end ( ) )
{
TProperties & properties = ( * it ) . second ;
TProperties : : iterator itProp = properties . begin ( ) ;
while ( itProp ! = properties . end ( ) )
{
_Entities [ slot ] - > updateVisualProperty ( ( * itProp ) . second . GC , ( * itProp ) . first , 0 ) ;
+ + itProp ;
}
_BackupedChanges . erase ( it ) ;
}
} // applyBackupedProperties //
//-----------------------------------------------
// writeEntities :
// Write a file with the position of all entities.
//-----------------------------------------------
void CEntityManager : : writeEntities ( )
{
COFile f ;
if ( ! f . open ( " entities.txt " , false , true ) )
return ;
string strTmp = " StartCommands = { \n " ;
2010-05-13 20:45:24 +00:00
f . serialBuffer ( ( uint8 * ) strTmp . c_str ( ) , ( uint ) strTmp . size ( ) ) ;
2010-05-06 00:08:41 +00:00
2010-05-13 20:45:24 +00:00
const uint nb = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( uint i = 1 ; i < nb ; + + i )
{
if ( _Entities [ i ] )
{
strTmp = toString ( " \" %s \" , \t \" %f \" , \" %f \" , \" %f \" , \" %f \" , \" %f \" , \" %f \" , \t // %3d \n " , _Entities [ i ] - > sheetId ( ) . toString ( ) . c_str ( ) , _Entities [ i ] - > pos ( ) . x , _Entities [ i ] - > pos ( ) . y , _Entities [ i ] - > pos ( ) . z , _Entities [ i ] - > front ( ) . x , _Entities [ i ] - > front ( ) . y , _Entities [ i ] - > front ( ) . z , i ) ;
2010-05-13 20:45:24 +00:00
f . serialBuffer ( ( uint8 * ) strTmp . c_str ( ) , ( uint ) strTmp . size ( ) ) ;
2010-05-06 00:08:41 +00:00
}
}
strTmp = " }; \n " ;
2010-05-13 20:45:24 +00:00
f . serialBuffer ( ( uint8 * ) strTmp . c_str ( ) , ( uint ) strTmp . size ( ) ) ;
2010-05-06 00:08:41 +00:00
// Close the File.
f . close ( ) ;
} // writeEntities //
//-----------------------------------------------
// serial
// Serialize entities.
//-----------------------------------------------
void CEntityManager : : serial ( class NLMISC : : IStream & f ) throw ( NLMISC : : EStream )
{
// Get nb max entities possible.
f . serial ( _NbMaxEntity ) ;
if ( f . isReading ( ) )
{
release ( ) ;
initialize ( _NbMaxEntity ) ;
}
// f.serial(_EntitiesAllocated); no need to serialize this one except maybe to check.
// Serialize each entity.
2010-05-13 20:45:24 +00:00
const uint nb = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( uint i = 0 ; i < nb ; + + i )
{
NLMISC : : CSheetId si ;
if ( ! f . isReading ( ) )
{
if ( _Entities [ i ] )
si = _Entities [ i ] - > sheetId ( ) ;
else
si = NLMISC : : CSheetId : : Unknown ;
}
// ...
f . serial ( si ) ;
// Create the entity.
if ( f . isReading ( ) & & ( si ! = CSheetId : : Unknown ) )
{
TNewEntityInfo emptyEntityInfo ;
emptyEntityInfo . reset ( ) ;
create ( i , si . asInt ( ) , emptyEntityInfo ) ;
}
// Get/Set entity state.
if ( _Entities [ i ] )
_Entities [ i ] - > serial ( f ) ;
}
} // serial //
//-----------------------------------------------
// dump :
// // Dump entities state.
//-----------------------------------------------
void CEntityManager : : dump ( class NLMISC : : IStream & f )
{
// Serialize the class.
serial ( f ) ;
} // dump //
//-----------------------------------------------
// dumpXML :
// Dump entities state (XML Format).
//-----------------------------------------------
void CEntityManager : : dumpXML ( class NLMISC : : IStream & f )
{
// Start the opening of a new node named Identity
f . xmlPush ( " Entities " ) ;
2010-05-13 20:45:24 +00:00
const uint nb = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( uint i = 0 ; i < nb ; + + i )
{
// Add a comment
// f.xmlComment();//toString("Describ the entity in the slot %d.", i).c_str());
// Start the opening of a new node named Identity
f . xmlPush ( toString ( " Entity%d " , i ) . c_str ( ) ) ;
if ( _Entities [ i ] )
{
// Open a new node header named Address
f . xmlPushBegin ( " Name " ) ;
// Set a property name
f . xmlSetAttrib ( " string " ) ;
ucstring n = _Entities [ i ] - > getEntityName ( ) ;
f . serial ( n ) ;
// Close the new node header
f . xmlPushEnd ( ) ;
// Close the address node
f . xmlPop ( ) ;
// Open a new node header named Address
f . xmlPushBegin ( " Sheet " ) ;
// Set a property name
f . xmlSetAttrib ( " name " ) ;
string sheetName = _Entities [ i ] - > sheetId ( ) . toString ( ) ;
f . serial ( sheetName ) ;
// Close the new node header
f . xmlPushEnd ( ) ;
// Close the address node
f . xmlPop ( ) ;
// Open a new node header named Address
f . xmlPushBegin ( " Position " ) ;
// Close the new node header
f . xmlPushEnd ( ) ;
f . serial ( _Entities [ i ] - > pos ( ) ) ;
// Close the address node
f . xmlPop ( ) ;
// Open a new node header named Address
f . xmlPushBegin ( " Front " ) ;
// Close the new node header
f . xmlPushEnd ( ) ;
NLMISC : : CVector front = _Entities [ i ] - > front ( ) ;
f . serial ( front ) ;
// Close the address node
f . xmlPop ( ) ;
// Open a new node header named Address
f . xmlPushBegin ( " Mode " ) ;
// Set a property name
f . xmlSetAttrib ( " name " ) ;
string mode = MBEHAV : : modeToString ( _Entities [ i ] - > mode ( ) ) ;
f . serial ( mode ) ;
// Set a property name
f . xmlSetAttrib ( " num " ) ;
uint8 m = _Entities [ i ] - > mode ( ) ;
f . serial ( m ) ;
// Close the new node header
f . xmlPushEnd ( ) ;
// Close the address node
f . xmlPop ( ) ;
// Open a new node header named Address
f . xmlPushBegin ( " LastBehaviourPlayed " ) ;
// Set a property name
f . xmlSetAttrib ( " name " ) ;
string beh = MBEHAV : : behaviourToString ( _Entities [ i ] - > behaviour ( ) ) ;
f . serial ( beh ) ;
// Set a property name
f . xmlSetAttrib ( " num " ) ;
uint8 b = _Entities [ i ] - > behaviour ( ) ;
f . serial ( b ) ;
// Close the new node header
f . xmlPushEnd ( ) ;
// Close the address node
f . xmlPop ( ) ;
}
// Close the address node
f . xmlPop ( ) ;
}
// Close the identity node
f . xmlPop ( ) ;
} // dumpXML //
//-----------------------------------------------
CEntityCL * CEntityManager : : getEntityByName ( uint32 stringId ) const
{
if ( stringId )
{
uint i ;
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( i = 0 ; i < count ; i + + )
{
if ( _Entities [ i ] )
if ( _Entities [ i ] - > getNameId ( ) = = stringId )
return _Entities [ i ] ;
}
}
return NULL ;
}
//-----------------------------------------------
CEntityCL * CEntityManager : : getEntityByName ( const ucstring & name , bool caseSensitive , bool complete ) const
{
ucstring source = name ;
2010-05-13 20:45:24 +00:00
const uint size = ( uint ) source . size ( ) ;
2010-05-06 00:08:41 +00:00
if ( ! caseSensitive )
{
uint j ;
for ( j = 0 ; j < size ; j + + )
source [ j ] = tolower ( source [ j ] ) ;
}
uint i ;
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
uint selectedEntityId ;
float selectedEntityDist = FLT_MAX ; // No selected Entity
for ( i = 0 ; i < count ; i + + )
{
if ( _Entities [ i ] )
{
ucstring value = _Entities [ i ] - > getDisplayName ( ) ;
bool foundEntity = false ;
uint j ;
if ( ! caseSensitive )
{
for ( j = 0 ; j < value . size ( ) ; j + + )
value [ j ] = tolower ( value [ j ] ) ;
}
// Complete test ?
if ( complete )
{
if ( value = = source )
foundEntity = true ;
}
else
{
if ( value . size ( ) > = size )
{
if ( std : : operator = = ( source , value . substr ( 0 , size ) ) )
foundEntity = true ;
}
}
if ( foundEntity )
{
const NLMISC : : CVectorD & targetPosD = _Entities [ i ] - > pos ( ) ;
const NLMISC : : CVectorD & userPosD = UserEntity - > pos ( ) ;
float deltaX = ( float ) targetPosD . x - ( float ) userPosD . x ;
float deltaY = ( float ) targetPosD . y - ( float ) userPosD . y ;
float dist = ( float ) sqrt ( deltaX * deltaX + deltaY * deltaY ) ;
if ( dist < selectedEntityDist )
{
selectedEntityDist = dist ;
selectedEntityId = i ;
}
}
}
}
if ( selectedEntityDist ! = FLT_MAX ) // Entity found
return _Entities [ selectedEntityId ] ;
else
return NULL ;
}
//-----------------------------------------------
CEntityCL * CEntityManager : : getEntityByCompressedIndex ( TDataSetIndex compressedIndex ) const
{
if ( compressedIndex ! = INVALID_DATASET_ROW )
{
uint i ;
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( i = 0 ; i < count ; i + + )
{
if ( _Entities [ i ] )
if ( _Entities [ i ] - > dataSetId ( ) = = compressedIndex )
return _Entities [ i ] ;
}
}
return NULL ;
}
//-----------------------------------------------
// managePACSTriggers :
// Manage PACS Triggers.
//-----------------------------------------------
void CEntityManager : : managePACSTriggers ( )
{
uint i ;
const uint nNbTrig = PACS - > getNumTriggerInfo ( ) ;
for ( i = 0 ; i < nNbTrig ; + + i )
{
const NLPACS : : UTriggerInfo & rTI = PACS - > getTriggerInfo ( i ) ;
// Detect collisions between user and other entities, to not be block (only the user is a trigger so no need to check).
if ( ( ( rTI . Object0 & 0xFFFF ) = = UserDataEntity )
& & ( ( rTI . Object1 & 0xFFFF ) = = UserDataEntity ) )
{
UserEntity - > startColTimer ( ) ;
break ;
}
}
// Stop Collision.
if ( i > = nNbTrig )
UserEntity - > stopColTimer ( ) ;
} // managePACSTriggers //
//-----------------------------------------------
// removeColUserOther :
//
//-----------------------------------------------
void CEntityManager : : removeColUserOther ( )
{
uint i ;
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( i = 1 ; i < count ; i + + )
{
if ( _Entities [ i ] )
{
if ( _Entities [ i ] - > getPrimitive ( ) )
{
// remove collision only if the entity is Traversable (bot objects may not)
if ( _Entities [ i ] - > getTraversable ( ) )
_Entities [ i ] - > getPrimitive ( ) - > setObstacle ( false ) ;
}
}
}
} // removeColUserOther //
//-----------------------------------------------
// restoreColUserOther :
//
//-----------------------------------------------
void CEntityManager : : restoreColUserOther ( )
{
uint i ;
2010-05-13 20:45:24 +00:00
const uint count = ( uint ) _Entities . size ( ) ;
2010-05-06 00:08:41 +00:00
for ( i = 1 ; i < count ; i + + )
{
if ( _Entities [ i ] )
{
if ( _Entities [ i ] - > getPrimitive ( ) )
_Entities [ i ] - > getPrimitive ( ) - > setObstacle ( true ) ;
}
}
} // restoreColUserOther //
//-----------------------------------------------
void CEntityManager : : removeAllAttachedFX ( )
{
for ( TEntities : : iterator it = _Entities . begin ( ) ; it ! = _Entities . end ( ) ; + + it )
{
if ( * it ) ( * it ) - > removeAllAttachedFX ( ) ;
}
}
// ***************************************************************************
void CEntityManager : : resetAllSoundAnimId ( )
{
for ( uint i = 0 ; i < _Entities . size ( ) ; i + + )
{
CEntityCL * ent = _Entities [ i ] ;
if ( ent )
{
ent - > resetAllSoundAnimId ( ) ;
}
}
}
// ***************************************************************************
# define nldebugraw NLMISC::createDebug(), NLMISC::DebugLog->displayRawNL
// ***************************************************************************
void CEntityManager : : startLogStageChange ( sint32 currentGameCycle , sint64 currentLocalTime )
{
// first stop
stopLogStageChange ( ) ;
// enable
_LogStageChange . Enabled = true ;
_LogStageChange . StartGameCycle = currentGameCycle ;
_LogStageChange . StartLocalTime = currentLocalTime ;
_LogStageChange . LastEntityLoged = CLFECOMMON : : INVALID_SLOT ;
_LogStageChange . StageSet . clear ( ) ;
nldebugraw ( " *** Start Loging Stage changes " ) ;
}
// ***************************************************************************
void CEntityManager : : logStageChange ( sint64 currentLocalTime )
{
if ( ! _LogStageChange . Enabled )
return ;
// if still exist
CCharacterCL * ent = dynamic_cast < CCharacterCL * > ( entity ( WatchedEntitySlot ) ) ;
if ( ent )
{
// if the watched entity has been changed
if ( WatchedEntitySlot ! = _LogStageChange . LastEntityLoged )
{
_LogStageChange . LastEntityLoged = WatchedEntitySlot ;
// backup set
_LogStageChange . StageSet = ent - > _Stages . _StageSet ;
nldebugraw ( " *** Start Loging Stage changes for Entity %d " , WatchedEntitySlot ) ;
}
else
{
// can log it
sint32 recGcRef = _LogStageChange . StartGameCycle ;
sint64 recTimeRef = _LogStageChange . StartLocalTime ;
// compare 2 logs and display differences
CStageSet : : TStageSet & oldStageSet = _LogStageChange . StageSet ;
const CStageSet : : TStageSet & newStageSet = ent - > _Stages . _StageSet ;
// for pos log detail
CVectorD precNewPos = ent - > pos ( ) ;
CVectorD precOldPos = ent - > pos ( ) ;
// compare each new/old stage
CStageSet : : TStageSet : : const_iterator itOld = oldStageSet . begin ( ) ;
CStageSet : : TStageSet : : const_iterator itNew = newStageSet . begin ( ) ;
while ( itOld ! = oldStageSet . end ( ) | | itNew ! = newStageSet . end ( ) )
{
// compare 2 iterators
sint signNewMinusOld ;
if ( itNew = = newStageSet . end ( ) )
signNewMinusOld = + 1 ;
else if ( itOld = = oldStageSet . end ( ) )
signNewMinusOld = - 1 ;
else
{
if ( itNew - > first > itOld - > first )
signNewMinusOld = + 1 ;
else if ( itNew - > first < itOld - > first )
signNewMinusOld = - 1 ;
else
signNewMinusOld = 0 ;
}
// if signNewMinusOld= +1, it means an old exist, without a new (=> the stage has been removed)
if ( signNewMinusOld = = + 1 )
{
logPropertyChange ( WatchedEntitySlot , itOld - > second , CStage ( ) , precOldPos , precNewPos , ( sint32 ) itOld - > first - recGcRef , currentLocalTime - recTimeRef ) ;
// new prec pos (if any)
itOld - > second . getPos ( precOldPos ) ;
itOld + + ;
}
// if signNewMinusOld= -1, it means an new exist, without an old (=> the stage has been added)
else if ( signNewMinusOld = = - 1 )
{
logPropertyChange ( WatchedEntitySlot , CStage ( ) , itNew - > second , precOldPos , precNewPos , ( sint32 ) itNew - > first - recGcRef , currentLocalTime - recTimeRef ) ;
// new prec pos (if any)
itNew - > second . getPos ( precNewPos ) ;
itNew + + ;
}
// if ==0, means the stage exist in both, but properties set may be different
else
{
logPropertyChange ( WatchedEntitySlot , itOld - > second , itNew - > second , precOldPos , precNewPos , ( sint32 ) itNew - > first - recGcRef , currentLocalTime - recTimeRef ) ;
// new prec pos (if any)
itOld - > second . getPos ( precOldPos ) ;
itNew - > second . getPos ( precNewPos ) ;
itOld + + ;
itNew + + ;
}
}
// bkup the new stage set
oldStageSet = newStageSet ;
}
}
// this entity might have been deleted, stop its log
else
{
_LogStageChange . LastEntityLoged = CLFECOMMON : : INVALID_SLOT ;
_LogStageChange . StageSet . clear ( ) ;
}
}
// ***************************************************************************
void CEntityManager : : logPropertyChange ( CLFECOMMON : : TCLEntityId who , const CStage & oldStage , const CStage & newStage ,
const CVectorD & precOldPos , const CVectorD & precNewPos , sint32 relGameCycle , sint64 relLocalTime )
{
// For all properties of interest
CLFECOMMON : : TPropIndex propLoged [ ] = { CLFECOMMON : : PROPERTY_POSITION , CLFECOMMON : : PROPERTY_ORIENTATION ,
CLFECOMMON : : PROPERTY_MODE , CLFECOMMON : : PROPERTY_ENTITY_MOUNTED_ID , CLFECOMMON : : PROPERTY_RIDER_ENTITY_ID ,
CLFECOMMON : : PROPERTY_BEHAVIOUR , CLFECOMMON : : PROPERTY_TARGET_ID ,
/*CLFECOMMON::PROPERTY_VISUAL_FX,
CLFECOMMON : : PROPERTY_TARGET_LIST_0 , CLFECOMMON : : PROPERTY_TARGET_LIST_1 ,
CLFECOMMON : : PROPERTY_TARGET_LIST_2 , CLFECOMMON : : PROPERTY_TARGET_LIST_3 */ } ;
uint32 numProps = sizeof ( propLoged ) / sizeof ( propLoged [ 0 ] ) ;
for ( uint i = 0 ; i < numProps ; i + + )
{
pair < bool , sint64 > oldProp = oldStage . property ( propLoged [ i ] ) ;
pair < bool , sint64 > newProp = newStage . property ( propLoged [ i ] ) ;
// if change of the prop, log it
if ( ( oldProp . first | | newProp . first ) & & oldProp ! = newProp )
{
// get the change reason
string reason ;
if ( ! oldProp . first )
reason = " ADD " ;
else if ( ! newProp . first )
reason = " DEL " ;
else
reason = " CHG " ;
// get the value
sint64 value = newProp . second ;
if ( ! newProp . first ) value = oldProp . second ;
string valStr ;
// mode?
if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_MODE )
{
valStr = MBEHAV : : TMode ( ( uint64 ) value ) . toString ( ) ;
}
// behaviour
else if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_BEHAVIOUR )
{
valStr = MBEHAV : : CBehaviour ( ( uint64 ) value ) . toString ( ) ;
}
// mount
else if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_ENTITY_MOUNTED_ID )
{
valStr = NLMISC : : toString ( value ) ;
}
else if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_RIDER_ENTITY_ID )
{
valStr = NLMISC : : toString ( value ) ;
}
// Target
else if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_TARGET_ID )
{
valStr = NLMISC : : toString ( value ) ;
}
// Position
else if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_POSITION )
{
// get the delta of move from previous pos stage
CVectorD pos ;
float dist = 0.f ;
if ( newProp . first )
{
if ( newStage . getPos ( pos ) )
dist = float ( CVectorD ( pos . x - precNewPos . x , pos . y - precNewPos . y , 0 ) . norm ( ) ) ;
valStr = toString ( " dst=%.1f pi=%d " , dist , newStage . predictedInterval ( ) ) ;
}
else
{
if ( oldStage . getPos ( pos ) )
dist = float ( CVectorD ( pos . x - precOldPos . x , pos . y - precOldPos . y , 0 ) . norm ( ) ) ;
valStr = toString ( " dst=%.1f pi=%d " , dist , oldStage . predictedInterval ( ) ) ;
}
}
// Orientation
else if ( propLoged [ i ] = = CLFECOMMON : : PROPERTY_ORIENTATION )
{
float rot = * ( float * ) ( & value ) ;
valStr = toString ( " %d " , sint32 ( rot * 180 / Pi ) ) ;
}
// display log
nldebugraw ( " ** Entity %d: (gc=%3d,t=%3d) %s: %s %s " , ( sint32 ) who , relGameCycle , ( sint32 ) relLocalTime , reason . c_str ( ) , CLFECOMMON : : getPropShortText ( propLoged [ i ] ) , valStr . c_str ( ) ) ;
}
}
}
// ***************************************************************************
void CEntityManager : : stopLogStageChange ( )
{
_LogStageChange . Enabled = false ;
nldebugraw ( " *** Stop Loging Stage changes " ) ;
}
// ***************************************************************************
bool CEntityManager : : isLogingStageChange ( ) const
{
return _LogStageChange . Enabled ;
}
// ***************************************************************************
sint32 CEntityManager : : getLogStageChangeStartCycle ( ) const
{
if ( isLogingStageChange ( ) )
return _LogStageChange . StartGameCycle ;
else
return 0 ;
}
// ***************************************************************************
sint64 CEntityManager : : getLogStageChangeStartLocalTime ( ) const
{
if ( isLogingStageChange ( ) )
return _LogStageChange . StartLocalTime ;
else
return 0 ;
}
// ***************************************************************************
void CEntityManager : : refreshInsceneInterfaceOfFriendNPC ( uint slot )
{
CCharacterCL * entity = dynamic_cast < CCharacterCL * > ( _Entities [ slot ] ) ;
if ( ! entity )
return ;
if ( entity - > canHaveMissionIcon ( )
& & entity - > isFriend ( ) // only valid once the Contextual property is received
)
{
entity - > releaseInSceneInterfaces ( ) ;
entity - > buildInSceneInterface ( ) ;
}
}