1
0
forked from 0ad/0ad
0ad/source/simulation/Entity.cpp

997 lines
24 KiB
C++
Raw Normal View History

#include "precompiled.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "graphics/GameView.h"
#include "graphics/Model.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "graphics/Sprite.h"
#include "graphics/Terrain.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "graphics/Unit.h"
#include "graphics/UnitManager.h"
#include "maths/MathUtil.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "maths/scripting/JSInterface_Vector3D.h"
#include "ps/Game.h"
#include "ps/Interact.h"
#include "ps/Profile.h"
#include "renderer/Renderer.h"
#include "renderer/WaterManager.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "scripting/ScriptableComplex.inl"
#include "Aura.h"
#include "Collision.h"
#include "Entity.h"
#include "EntityFormation.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "EntityManager.h"
#include "EntityTemplate.h"
#include "EntityTemplateCollection.h"
#include "EventHandlers.h"
#include "Formation.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "FormationManager.h"
#include "PathfindEngine.h"
#include "ProductionQueue.h"
#include "Stance.h"
#include "TechnologyCollection.h"
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
#include "TerritoryManager.h"
extern int g_xres, g_yres;
#include <algorithm>
using namespace std;
CEntity::CEntity( CEntityTemplate* base, CVector3D position, float orientation, const std::set<CStr8>& actorSelections, const CStrW* building )
{
ent_flags = 0;
m_position = position;
m_orientation.X = 0;
m_orientation.Y = orientation;
m_orientation.Z = 0;
m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation.Y );
m_position_previous = m_position;
m_orientation_previous = m_orientation;
m_player = NULL;
m_productionQueue = new CProductionQueue( this );
m_stance = new CHoldStance( this );
for( int t = 0; t < EVENT_LAST; t++ )
{
AddProperty( EventNames[t], &m_EventHandlers[t], false );
AddHandler( t, &m_EventHandlers[t] );
}
m_collisionPatch = NULL;
// Set our parent unit and build us an actor.
m_actor = NULL;
m_bounds = NULL;
m_lastState = -1;
entf_set(ENTF_TRANSITION);
m_fsm_cyclepos = NOT_IN_CYCLE;
m_base = base;
m_actorSelections = actorSelections;
loadBase();
if( m_bounds )
m_bounds->setPosition( m_position.X, m_position.Z );
m_graphics_position = m_position;
m_graphics_orientation = m_orientation;
m_actor_transform_valid = false;
entf_clear(ENTF_HAS_RALLY_POINT);
entf_clear(ENTF_DESTROYED);
m_selected = false;
entf_clear(ENTF_IS_RUNNING);
entf_clear(ENTF_SHOULD_RUN);
entf_clear(ENTF_TRIGGER_RUN);
entf_clear(ENTF_HEALTH_DECAY);
m_frameCheck = 0;
m_lastCombatTime = -100;
m_lastRunTime = -100;
2006-02-13 04:32:15 +01:00
m_currentNotification = 0;
m_currentRequest = 0;
entf_set(ENTF_DESTROY_NOTIFIERS);
m_formationSlot = -1;
m_formation = -1;
m_grouped = -1;
if( building )
m_building = *building;
m_extant = true;
m_visible = true;
m_rallyPoint = m_position;
m_associatedTerritory = NULL;
m_player = g_Game->GetPlayer( 0 );
}
CEntity::~CEntity()
{
if( m_actor )
{
g_UnitMan.RemoveUnit( m_actor );
delete( m_actor );
}
if( m_bounds )
{
delete( m_bounds );
}
delete m_productionQueue;
delete m_stance;
for( AuraTable::iterator it = m_auras.begin(); it != m_auras.end(); it++ )
{
delete it->second;
}
m_auras.clear();
entf_set(ENTF_DESTROY_NOTIFIERS);
for ( size_t i=0; i<m_listeners.size(); i++ )
m_listeners[i].m_sender->DestroyNotifier( this );
DestroyAllNotifiers();
CEntity* remove = this;
g_FormationManager.RemoveUnit(remove);
}
void CEntity::loadBase()
{
if( m_actor )
{
g_UnitMan.RemoveUnit( m_actor );
delete( m_actor );
m_actor = NULL;
}
if( m_bounds )
{
delete( m_bounds );
m_bounds = NULL;
}
CStr actorName ( m_base->m_actorName ); // convert CStrW->CStr8
m_actor = g_UnitMan.CreateUnit( actorName, this, m_actorSelections );
// Set up our instance data
SetBase( m_base );
m_classes.SetParent( &( m_base->m_classes ) );
SetNextObject( m_base );
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
{
m_bounds = new CBoundingCircle( m_position.X, m_position.Z, m_base->m_bound_circle );
}
else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB )
{
m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box );
}
m_actor_transform_valid = false;
if( m_player )
{
// Make sure the actor has the right player colour
m_actor->SetPlayerID( m_player->GetPlayerID() );
}
// Re-enter all our auras so they can take into account our new traits
for( AuraSet::iterator it = m_aurasInfluencingMe.begin(); it != m_aurasInfluencingMe.end(); it++ )
{
(*it)->Remove( this );
}
// Resize sectors array
m_sectorValues.resize(m_base->m_sectorDivs);
for ( int i=0; i<m_base->m_sectorDivs; ++i )
m_sectorValues[i] = false;
}
void CEntity::initAuraData()
{
if ( m_auras.empty() )
return;
m_unsnappedPoints.resize(m_auras.size());
size_t i=0;
for ( AuraTable::iterator it=m_auras.begin(); it!=m_auras.end(); ++it, ++i )
{
m_unsnappedPoints[i].resize(SELECTION_CIRCLE_POINTS);
float radius = it->second->m_radius;
for ( int j=0; j<SELECTION_CIRCLE_POINTS; ++j )
{
float val = j * 2*PI / (float)SELECTION_CIRCLE_POINTS;
m_unsnappedPoints[i][j] = CVector2D( cosf(val)*radius,
sinf(val)*radius );
}
}
}
void CEntity::kill()
{
g_Selection.removeAll( me );
CEntity* remove = this;
g_FormationManager.RemoveUnit(remove);
entf_set(ENTF_DESTROY_NOTIFIERS);
for ( size_t i=0; i<m_listeners.size(); i++ )
m_listeners[i].m_sender->DestroyNotifier( this );
DestroyAllNotifiers();
SAFE_DELETE(m_bounds);
m_extant = false;
entf_set(ENTF_DESTROYED);
g_EntityManager.m_refd[me.m_handle] = false;
//Shutdown(); // PT: tentatively removed - this seems to be called by ~CJSComplex, and we don't want to do it twice
if( m_actor )
{
g_UnitMan.RemoveUnit( m_actor );
delete( m_actor );
m_actor = NULL;
}
updateCollisionPatch();
me = HEntity(); // will deallocate the entity, assuming nobody else has a reference to it
}
2005-01-23 23:38:13 +01:00
void CEntity::SetPlayer(CPlayer *pPlayer)
{
m_player = pPlayer;
2005-01-23 23:38:13 +01:00
// This should usually be called CUnit::SetPlayerID, so we don't need to
// update the actor here.
// If we're a territory centre, change the territory's owner
if( m_associatedTerritory )
m_associatedTerritory->owner = pPlayer;
2005-01-23 23:38:13 +01:00
}
void CEntity::updateActorTransforms()
{
CMatrix3D m;
CMatrix3D mXZ;
float Cos = cosf( m_graphics_orientation.Y );
float Sin = sinf( m_graphics_orientation.Y );
m._11=-Cos; m._12=0.0f; m._13=-Sin; m._14=0.0f;
m._21=0.0f; m._22=1.0f; m._23=0.0f; m._24=0.0f;
m._31=Sin; m._32=0.0f; m._33=-Cos; m._34=0.0f;
m._41=0.0f; m._42=0.0f; m._43=0.0f; m._44=1.0f;
mXZ.SetXRotation( m_graphics_orientation.X );
mXZ.RotateZ( m_graphics_orientation.Z );
mXZ = m*mXZ;
mXZ.Translate(m_graphics_position);
if( m_actor )
m_actor->GetModel()->SetTransform( mXZ );
}
void CEntity::snapToGround()
{
m_graphics_position.Y = getAnchorLevel( m_graphics_position.X, m_graphics_position.Z );
}
void CEntity::updateXZOrientation()
{
// Make sure m_ahead is correct
m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation.Y );
CVector2D targetXZ = g_Game->GetWorld()->GetTerrain()->getSlopeAngleFace(this);
m_orientation.X = clamp( targetXZ.x, -m_base->m_anchorConformX, m_base->m_anchorConformX );
m_orientation.Z = clamp( targetXZ.y, -m_base->m_anchorConformZ, m_base->m_anchorConformZ );
m_orientation_unclamped.x = targetXZ.x;
m_orientation_unclamped.y = targetXZ.y;
}
jsval CEntity::getClassSet()
{
CStrW result = m_classes.getMemberList();
return( ToJSVal( result ) );
}
void CEntity::setClassSet( jsval value )
{
CStr memberCmdList = ToPrimitive<CStrW>( value );
m_classes.setFromMemberList(memberCmdList);
rebuildClassSet();
}
void CEntity::rebuildClassSet()
{
m_classes.Rebuild();
InheritorsList::iterator it;
for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ )
(*it)->rebuildClassSet();
}
void CEntity::update( size_t timestep )
{
if( !m_extant ) return;
m_position_previous = m_position;
m_orientation_previous = m_orientation;
CalculateRegen( timestep );
if ( entf_get(ENTF_TRIGGER_RUN) )
m_frameCheck++;
if ( m_frameCheck != 0 )
{
entf_set(ENTF_SHOULD_RUN);
entf_clear(ENTF_TRIGGER_RUN);
m_frameCheck = 0;
}
m_productionQueue->Update( timestep );
// Note: aura processing is done before state processing because the state
// processing code is filled with all kinds of returns
PROFILE_START( "aura processing" );
for( AuraTable::iterator it = m_auras.begin(); it != m_auras.end(); it++ )
{
it->second->Update( timestep );
}
PROFILE_END( "aura processing" );
// The process[...] functions return 'true' if the order at the top of the stack
// still needs to be (re-)evaluated; else 'false' to terminate the processing of
// this entity in this timestep.
PROFILE_START( "state processing" );
if( entf_get(ENTF_IS_RUNNING) )
{
m_lastRunTime = g_Game->GetTime();
}
if( m_orderQueue.empty() )
{
// We are idle. Tell our stance in case it wants us to do something.
PROFILE( "unit ai" );
m_stance->onIdle();
}
while( !m_orderQueue.empty() )
{
CEntityOrder* current = &m_orderQueue.front();
if( current->m_type != m_lastState )
{
entf_set(ENTF_TRANSITION);
m_fsm_cyclepos = NOT_IN_CYCLE;
PROFILE( "state transition / order" );
CEntity* target = NULL;
if( current->m_data[0].entity )
target = &( *( current->m_data[0].entity ) );
CVector3D worldPosition = (CVector3D)current->m_data[0].location;
CEventOrderTransition evt( m_lastState, current->m_type, target, worldPosition );
if( !DispatchEvent( &evt ) )
{
m_orderQueue.pop_front();
continue;
}
else if( target )
{
current->m_data[0].location = worldPosition;
current->m_data[0].entity = target->me;
}
m_lastState = current->m_type;
}
else
2005-12-29 10:12:54 +01:00
{
entf_clear(ENTF_TRANSITION);
2005-12-29 10:12:54 +01:00
}
switch( current->m_type )
{
case CEntityOrder::ORDER_GOTO_NOPATHING:
case CEntityOrder::ORDER_GOTO_COLLISION:
case CEntityOrder::ORDER_GOTO_SMOOTHED:
2005-12-29 10:12:54 +01:00
if( processGotoNoPathing( current, timestep ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GENERIC:
2005-12-29 10:12:54 +01:00
if( processGeneric( current, timestep ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_START_CONSTRUCTION:
{
CEventStartConstruction evt( current->m_data[0].entity );
m_orderQueue.pop_front();
DispatchEvent( &evt );
}
break;
case CEntityOrder::ORDER_PRODUCE:
debug_printf("calling processProduce once\n");
processProduce( current );
m_orderQueue.pop_front();
break;
case CEntityOrder::ORDER_GENERIC_NOPATHING:
2005-12-29 10:12:54 +01:00
if( processGenericNoPathing( current, timestep ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GOTO_WAYPOINT:
if ( processGotoWaypoint( current, timestep, false ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GOTO_WAYPOINT_CONTACT:
if ( processGotoWaypoint( current, timestep, true ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GOTO:
case CEntityOrder::ORDER_RUN:
2005-12-29 10:12:54 +01:00
if( processGoto( current, timestep ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_PATROL:
2005-12-29 10:12:54 +01:00
if( processPatrol( current, timestep ) )
break;
updateCollisionPatch();
return;
case CEntityOrder::ORDER_PATH_END_MARKER:
2005-12-29 10:12:54 +01:00
m_orderQueue.pop_front();
break;
default:
debug_warn( "Invalid entity order" );
}
}
if( m_orderQueue.empty() )
{
// If we have no orders, stop running
entf_clear(ENTF_IS_RUNNING);
entf_clear(ENTF_SHOULD_RUN);
}
PROFILE_END( "state processing" );
// If we get to here, it means we're idle or dead (no orders); update the animation
if( m_actor )
{
PROFILE( "animation updates" );
if( m_extant )
{
if( ( m_lastState != -1 ) || !m_actor->GetModel()->GetAnimation() )
{
# Added tool for viewing models and animations outside the game. Atlas: Added ActorViewer. Moved GL canvas into separate class for shared use. Disabled message-handling callback while blocked on the game, and stopped creating dialog boxes inside the game thread in order to avoid deadlocks (hopefully). Support multiple Views (for independent sets of camera/update/render code). Recalculate territory boundaries when necessary. Changed default list of animations to match those currently used by actors. # Tidied up more code. Moved some more #includes out of .h files, to minimise unnecessary compilation. MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2 (use PI/2), MAX3, ABS (use abs)). ObjectManager: Removed some ScEd-specific things. Unit: Moved creation out of UnitManager, so units can be created without adding to the manager. Changed CStr8 to the more conventional CStr. app_hooks: Removed warning for setting multiple times. win: Restored SEH catcher. GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do what it says it does ("force renderer to load everything") since we're loading-on-demand most stuff and it doesn't seem especially useful since we'd prefer to minimise loading times (but feel free to correct me if I'm wrong). (And because it crashes when things need to be initialised in a different order, so it's easier to remove than to understand and fix it.) PatchRData, Renderer: Work sensibly when there's no game (hence no LOS manager, water, etc). LOSManager: Use entity position instead of actor position when possible. TerritoryManager: Allow delayed recalculations (so Atlas can issue lots of move+recalculate commands per frame). Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to be deleted manually. This was SVN commit r4261.
2006-08-28 19:36:42 +02:00
m_actor->SetEntitySelection( "idle" );
m_actor->SetRandomAnimation( "idle" );
}
}
}
if( m_lastState != -1 )
{
PROFILE( "state transition event" );
CEntity* d0;
CVector3D d1;
CEventOrderTransition evt( m_lastState, -1, d0, d1 );
DispatchEvent( &evt );
m_lastState = -1;
}
}
void CEntity::updateCollisionPatch()
{
vector<CEntity*>* newPatch = g_EntityManager.getCollisionPatch( this );
if( newPatch != m_collisionPatch )
{
if( m_collisionPatch )
{
// remove ourselves from old patch
vector<CEntity*>& old = *m_collisionPatch;
if( old.size() == 1 )
{
// we were the only ones there, just pop us
old.pop_back();
}
else
{
// find our location and put in the last guy in the patch, then pop back
for( size_t i=0; i < old.size(); i++ )
{
if( old[i] == this )
{
old[i] = old.back();
old.pop_back();
break;
}
}
}
}
if( m_extant )
{
// add ourselves to new patch
newPatch->push_back( this );
m_collisionPatch = newPatch;
}
}
}
#if AURA_TEST
void CEntity::UpdateAuras( size_t timestep_millis )
{
std::vector<SAura>::iterator it_a;
for( it_a = m_Auras.begin(); it_a != m_Auras.end(); it_a++ )
{
SAuraData& d = it_a->m_Data;
std::set<CEntity*>
& inRange = GetEntitiesWithinRange( m_position, d.m_Radius );
std::vector<CEntity*>::iterator it1 = inRange.begin();
std::vector<SAuraInstance>::iterator it2 = it_a->m_Influenced.begin();
while( WORLD_IS_ROUND )
{
if( it1 == inRange.end() )
{
// No more in range => anything else in the influenced set must have gone
// out of range.
for( ; it2 != it_a->m_Influenced.end(); it2++ )
UpdateAuras_LeaveRange( *it_a, it2->GetEntity() );
break;
}
if( it2 == it_a->m_Influenced.end() )
{
// Everything else in the in-range set has only just come into range
for( ; it1 != inRange.end(); it1++ )
UpdateAuras_EnterRange( *it_a, *it );
break;
}
CEntity* e1 = *it1, e2 = it2->GetEntity();
if( e1 < e2 )
{
// A new entity e1 has just come into range.
// Check to see if it can be affected by the aura.
UpdateAuras_EnterRange( *it_a, e1 );
++it1;
}
else if( e1 == e2 )
{
// The entity e1/e2 was previously in range, and still is.
UpdateAuras_Normal( *it_a, e1 );
++it1;
++it2;
}
else
{
// The entity e2 was previously in range, but is no longer.
UpdateAuras_LeaveRange( *it_a, e2 );
++it2;
}
}
}
}
void UpdateAuras_EnterRange( SAura& aura, CEntity* e )
{
if( aura.m_Recharge )
return( false );
// Check to see if the entity is eligable
if( !UpdateAuras_IsEligable( aura.m_Data, e ) )
return; // Do nothing.
SAuraInstance ai;
ai.m_Influenced = e;
ai.m_EnteredRange = ai.m_LastInRange = 0;
ai.m_Applied = -1;
// If there's no timer, apply the effect now.
if( aura.m_Data.m_Time == 0 )
{
e->ApplyAuraEffect( aura.m_Data );
ai.m_Applied = 0;
aura.m_Recharge = aura.m_Data.m_Cooldown;
}
aura.m_Influenced.push_back( ai );
}
void UpdateAuras_Normal( SAura& aura, CEntity* e )
{
// Is the entity no longer eligable?
if( !UpdateAuras_IsEligable( aura.m_Data, e ) )
{
//}
}
bool UpdateAuras_IsEligable( SAuraData& aura, CEntity* e )
{
if( e == this )
{
if( !( aura.m_Allegiance & SAuraData::SELF ) )
return( false );
}
else if( e->m_player == GetGaiaPlayer() )
{
if( !( aura.m_Allegiance & SAuraData::GAIA ) )
return( false );
}
else if( e->m_player == m_player )
{
if( !( aura.m_Allegiance & SAuraData::PLAYER ) )
return( false );
}
// TODO: Allied players
else
{
if( !( aura.m_Allegiance & SAuraData::ENEMY ) )
return( false );
}
if( e->m_hp > e->m_hp_max * aura.m_Hitpoints )
return( false );
return( true );
}
#endif
bool CEntity::Initialize()
2005-12-29 10:12:54 +01:00
{
// Apply our player's active techs to ourselves (we do this here since m_player isn't yet set in the constructor)
const std::vector<CTechnology*>& techs = m_player->GetActiveTechs();
for( size_t i=0; i<techs.size(); i++ )
{
techs[i]->apply( this );
}
// Dispatch the initialize script event
2005-12-29 10:12:54 +01:00
CEventInitialize evt;
if( !DispatchEvent( &evt ) )
{
kill();
return false;
}
return true;
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::Tick()
{
CEventTick evt;
DispatchEvent( &evt );
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::clearOrders()
{
if ( m_orderQueue.empty() )
return;
CIdleEvent evt( m_orderQueue.front(), m_currentNotification );
DispatchEvent(&evt);
m_orderQueue.clear();
2005-12-29 10:12:54 +01:00
}
void CEntity::popOrder()
{
if ( m_orderQueue.empty() )
return;
CIdleEvent evt( m_orderQueue.front(), m_currentNotification );
DispatchEvent(&evt);
2005-12-29 10:12:54 +01:00
m_orderQueue.pop_front();
}
2005-12-29 10:12:54 +01:00
void CEntity::pushOrder( CEntityOrder& order )
{
CEventPrepareOrder evt( order.m_data[0].entity, order.m_type, order.m_data[1].data, order.m_data[0].string );
2006-02-13 04:32:15 +01:00
if( DispatchEvent(&evt) )
{
m_orderQueue.push_back( order );
if(evt.m_notifyType != CEntityListener::NOTIFY_NONE)
{
CheckListeners( evt.m_notifyType, evt.m_notifySource );
}
}
2005-12-29 10:12:54 +01:00
}
2006-02-13 04:32:15 +01:00
void CEntity::DispatchNotification( CEntityOrder order, int type )
{
CEventNotification evt( order, type );
DispatchEvent( &evt );
}
struct isListenerSender
{
CEntity* sender;
isListenerSender(CEntity* sender) : sender(sender) {}
bool operator()(CEntityListener& listener)
{
return listener.m_sender == sender;
}
};
int CEntity::DestroyNotifier( CEntity* target )
2006-02-13 04:32:15 +01:00
{
if ( target->m_listeners.empty() )
return 0;
//Stop listening
// (Don't just loop and use 'erase', because modifying the deque while
// looping over it is a bit dangerous)
std::deque<CEntityListener>::iterator newEnd = std::remove_if(
target->m_listeners.begin(), target->m_listeners.end(),
isListenerSender(this));
target->m_listeners.erase(newEnd, target->m_listeners.end());
//Get rid of our copy
std::vector<CEntity*>::iterator newEnd2 = std::remove_if(
m_notifiers.begin(), m_notifiers.end(),
bind2nd(std::equal_to<CEntity*>(), target));
int removed = std::distance(newEnd2, m_notifiers.end());
m_notifiers.erase(newEnd2, m_notifiers.end());
return removed;
}
void CEntity::DestroyAllNotifiers()
{
debug_assert(entf_get(ENTF_DESTROY_NOTIFIERS));
//Make them stop listening to us
while ( ! m_notifiers.empty() )
DestroyNotifier( m_notifiers[0] );
2006-02-13 04:32:15 +01:00
}
CEntityFormation* CEntity::GetFormation()
{
if ( m_formation < 0 )
return NULL;
return g_FormationManager.GetFormation(m_formation);
}
void CEntity::DispatchFormationEvent( int type )
{
CFormationEvent evt( type );
DispatchEvent( &evt );
}
2005-12-29 10:12:54 +01:00
void CEntity::repath()
{
CVector2D destination;
CEntityOrder::EOrderSource orderSource = CEntityOrder::SOURCE_PLAYER;
if( m_orderQueue.empty() )
return;
while( !m_orderQueue.empty() &&
( ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_COLLISION )
|| ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_NOPATHING )
|| ( m_orderQueue.front().m_type == CEntityOrder::ORDER_GOTO_SMOOTHED ) ) )
{
destination = m_orderQueue.front().m_data[0].location;
orderSource = m_orderQueue.front().m_source;
m_orderQueue.pop_front();
}
g_Pathfinder.requestPath( me, destination, orderSource );
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::reorient()
{
m_orientation = m_graphics_orientation;
m_ahead.x = sin( m_orientation.Y );
m_ahead.y = cos( m_orientation.Y );
if( m_bounds->m_type == CBoundingObject::BOUND_OABB )
((CBoundingBox*)m_bounds)->setOrientation( m_ahead );
updateActorTransforms();
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::teleport()
{
m_position = m_graphics_position;
m_bounds->setPosition( m_position.X, m_position.Z );
updateActorTransforms();
updateCollisionPatch();
repath();
2005-12-29 10:12:54 +01:00
}
void CEntity::stanceChanged()
{
delete m_stance;
m_stance = 0;
if( m_stanceName == "aggress" )
{
m_stance = new CAggressStance( this );
}
else if( m_stanceName == "defend" )
{
m_stance = new CDefendStance( this );
}
else if( m_stanceName == "stand" )
{
m_stance = new CStandStance( this );
}
else // m_stanceName == "hold" or undefined stance
{
m_stance = new CHoldStance( this );
}
}
2005-12-29 10:12:54 +01:00
void CEntity::checkSelection()
{
if( m_selected )
{
if( !g_Selection.isSelected( me ) )
g_Selection.addSelection( me );
}
else
{
if( g_Selection.isSelected( me ) )
g_Selection.removeSelection( me );
}
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::checkGroup()
{
g_Selection.changeGroup( me, -1 ); // Ungroup
if( ( m_grouped >= 0 ) && ( m_grouped < MAX_GROUPS ) )
g_Selection.changeGroup( me, m_grouped );
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::interpolate( float relativeoffset )
{
CVector3D old_graphics_position = m_graphics_position;
CVector3D old_graphics_orientation = m_graphics_orientation;
m_graphics_position = Interpolate<CVector3D>( m_position_previous, m_position, relativeoffset );
2005-12-29 10:12:54 +01:00
// Avoid wraparound glitches for interpolating angles.
m_orientation.X = fmodf(m_orientation.X, 2*PI); // (ensure the following loops can't take forever)
m_orientation.Y = fmodf(m_orientation.Y, 2*PI);
m_orientation.Z = fmodf(m_orientation.Z, 2*PI);
while( m_orientation.Y < m_orientation_previous.Y - PI )
m_orientation_previous.Y -= 2 * PI;
while( m_orientation.Y > m_orientation_previous.Y + PI )
m_orientation_previous.Y += 2 * PI;
while( m_orientation.X < m_orientation_previous.X - PI )
m_orientation_previous.X -= 2 * PI;
while( m_orientation.X > m_orientation_previous.X + PI )
m_orientation_previous.X += 2 * PI;
2005-12-29 10:12:54 +01:00
while( m_orientation.Z < m_orientation_previous.Z - PI )
m_orientation_previous.Z -= 2 * PI;
while( m_orientation.Z > m_orientation_previous.Z + PI )
m_orientation_previous.Z += 2 * PI;
updateXZOrientation();
m_graphics_orientation = Interpolate<CVector3D>( m_orientation_previous, m_orientation, relativeoffset );
// Mark the actor transform data as invalid if the entity has moved since
// the last call to 'interpolate'.
// position.Y is ignored because we can't determine the new value without
// calling snapToGround, which is slow. TODO: This may need to be adjusted to
// handle flying units or moving terrain.
if( m_graphics_orientation != old_graphics_orientation ||
m_graphics_position.X != old_graphics_position.X ||
m_graphics_position.Z != old_graphics_position.Z
)
{
m_actor_transform_valid = false;
}
// Update the actor transform data when necessary.
if( !m_actor_transform_valid )
{
snapToGround();
updateActorTransforms();
m_actor_transform_valid = true;
}
2005-12-29 10:12:54 +01:00
}
2005-12-29 10:12:54 +01:00
void CEntity::invalidateActor()
{
m_actor_transform_valid = false;
2005-12-29 10:12:54 +01:00
}
float CEntity::getAnchorLevel( float x, float z )
{
CTerrain *pTerrain = g_Game->GetWorld()->GetTerrain();
float groundLevel = pTerrain->getExactGroundLevel( x, z );
if( m_base->m_anchorType==L"Ground" )
{
return groundLevel;
}
else
{
return max( groundLevel, g_Renderer.GetWaterManager()->m_WaterHeight );
}
2005-12-29 10:12:54 +01:00
}
int CEntity::findSector( int divs, float angle, float maxAngle, bool negative )
{
float step=maxAngle/divs;
if ( negative )
{
float tracker;
int i=1, sectorRemainder;
for ( tracker=-maxAngle/2.0f; tracker+step<0.0f; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return i;
}
sectorRemainder = i;
i=divs;
for ( tracker=maxAngle/2.0f; tracker-step>0.0f; tracker-=step, --i )
{
if ( angle < tracker && angle >= tracker-step )
return i;
}
return sectorRemainder;
}
else
{
int i=1;
for ( float tracker=0.0f; tracker<maxAngle; tracker+=step, ++i )
{
if ( angle > tracker && angle <= tracker+step )
return i;
}
}
debug_warn("CEntity::findSector() - invalid parameters passed.");
return -1;
}
static inline float regen(float cur, float limit, float timestep, float regen_rate)
{
if(regen_rate <= 0)
return cur;
return std::min(limit, cur + timestep / 1000.0f * regen_rate * limit );
}
static inline float decay(float cur, float limit, float timestep, float decay_rate)
{
if(decay_rate <= 0)
return cur;
return std::max(0.0f, cur - timestep / 1000.0f * decay_rate * limit);
}
void CEntity::CalculateRegen(float timestep)
{
// Health regen
if(entf_get(ENTF_HEALTH_DECAY))
m_healthCurr = decay(m_healthCurr, m_healthMax, timestep, m_healthDecayRate);
else if(g_Game->GetTime() - m_lastCombatTime > m_healthRegenStart)
m_healthCurr = regen(m_healthCurr, m_healthMax, timestep, m_healthRegenRate);
// Stamina regen
if( m_staminaMax > 0 )
{
if(entf_get(ENTF_IS_RUNNING))
m_staminaCurr = decay(m_staminaCurr, m_staminaMax, timestep, m_runDecayRate);
else if(m_orderQueue.empty())
m_staminaCurr = regen(m_staminaCurr, m_staminaMax, timestep, m_runRegenRate);
}
}