forked from mirrors/0ad
Jason's animation events. Also reworked entity-types system.
This was SVN commit r2209.
This commit is contained in:
@@ -181,6 +181,11 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
||||
transform.SetIdentity();
|
||||
SetTransform(transform);
|
||||
|
||||
// Following seems to stomp over the current animation time - which, unsurprisingly,
|
||||
// introduces artefacts in the currently playing animation. Save it here and restore it
|
||||
// at the end.
|
||||
float AnimTime = m_AnimTime;
|
||||
|
||||
// iterate through every frame of the animation
|
||||
for (uint j=0;j<anim->GetNumFrames();j++) {
|
||||
// extend bounds by vertex positions at the frame
|
||||
@@ -194,12 +199,13 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
||||
}
|
||||
|
||||
SetTransform(oldtransform);
|
||||
m_AnimTime = AnimTime;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildAnimation: load raw animation frame animation from given file, and build a
|
||||
// animation specific to this model
|
||||
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,size_t actionpos)
|
||||
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,double actionpos)
|
||||
{
|
||||
CSkeletonAnimDef* def=g_SkelAnimMan.GetAnimation(filename);
|
||||
if (!def) return 0;
|
||||
@@ -207,9 +213,7 @@ CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,size_t ac
|
||||
CSkeletonAnim* anim=new CSkeletonAnim;
|
||||
anim->m_AnimDef=def;
|
||||
anim->m_Speed=speed;
|
||||
anim->m_ActionPos=actionpos;
|
||||
if( actionpos > anim->m_AnimDef->GetDuration() )
|
||||
anim->m_ActionPos = anim->m_AnimDef->GetDuration();
|
||||
anim->m_ActionPos=(size_t)( actionpos * anim->m_AnimDef->GetDuration() );
|
||||
anim->m_ObjectBounds.SetEmpty();
|
||||
InvalidateBounds();
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ public:
|
||||
|
||||
// load raw animation frame animation from given file, and build a
|
||||
// animation specific to this model
|
||||
CSkeletonAnim* BuildAnimation(const char* filename,float speed,size_t actionpos);
|
||||
CSkeletonAnim* BuildAnimation(const char* filename,float speed, double actionpos);
|
||||
|
||||
// add a prop to the model on the given point
|
||||
void AddProp(SPropPoint* point,CModel* model);
|
||||
|
||||
@@ -159,7 +159,7 @@ bool CObjectBase::Load(const char* filename)
|
||||
AT(file);
|
||||
AT(name);
|
||||
AT(speed);
|
||||
AT(actionpos);
|
||||
AT(event);
|
||||
AT(attachpoint);
|
||||
AT(actor);
|
||||
AT(frequency);
|
||||
@@ -220,10 +220,12 @@ bool CObjectBase::Load(const char* filename)
|
||||
anim.m_Speed = CStr(ae.Value).ToInt() / 100.f;
|
||||
if (anim.m_Speed <= 0.0) anim.m_Speed = 1.0f;
|
||||
}
|
||||
else if (ae.Name == at_actionpos)
|
||||
else if (ae.Name == at_event)
|
||||
{
|
||||
anim.m_ActionPos = CStr(ae.Value).ToInt();
|
||||
if (anim.m_ActionPos < 0) anim.m_ActionPos = 0;
|
||||
anim.m_ActionPos = CStr(ae.Value).ToDouble();
|
||||
if (anim.m_ActionPos < 0.0) anim.m_ActionPos = 0.0;
|
||||
else if (anim.m_ActionPos > 100.0) anim.m_ActionPos = 1.0;
|
||||
else if (anim.m_ActionPos > 1.0) anim.m_ActionPos /= 100.0;
|
||||
}
|
||||
else
|
||||
; // unrecognised element
|
||||
|
||||
@@ -13,7 +13,7 @@ public:
|
||||
|
||||
struct Anim {
|
||||
// constructor
|
||||
Anim() : m_Speed(1), m_ActionPos( 0 ), m_AnimData(0) {}
|
||||
Anim() : m_Speed(1), m_ActionPos( 0.0 ), m_AnimData(0) {}
|
||||
|
||||
// name of the animation - "Idle", "Run", etc
|
||||
CStr m_AnimName;
|
||||
@@ -21,8 +21,10 @@ public:
|
||||
CStr m_FileName;
|
||||
// animation speed, as specified in XML actor file
|
||||
float m_Speed;
|
||||
// time during the animation at which the interesting bit happens (msec)
|
||||
size_t m_ActionPos;
|
||||
// fraction of the way through the animation that the interesting bit
|
||||
// happens (this is converted to an absolute time when the animation
|
||||
// data is loaded)
|
||||
double m_ActionPos;
|
||||
// the animation data, specific to the this model
|
||||
CSkeletonAnim* m_AnimData;
|
||||
};
|
||||
|
||||
@@ -62,12 +62,15 @@ extern float fmaxf(float a, float b);
|
||||
|
||||
// C++ linkage
|
||||
|
||||
// STL_HASH_MAP, STL_HASH_MULTIMAP
|
||||
// STL_HASH_MAP, STL_HASH_MULTIMAP, STL_HASH_SET
|
||||
#ifdef __GNUC__
|
||||
// GCC
|
||||
# include <ext/hash_map>
|
||||
# include <ext/hash_set> // Probably?
|
||||
|
||||
# define STL_HASH_MAP __gnu_cxx::hash_map
|
||||
# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap
|
||||
# define STL_HASH_SET __gnu_cxx::hash_set
|
||||
|
||||
// Hack: GCC Doesn't have a hash instance for std::string included (and it looks
|
||||
// like they won't add it - marked resolved/wontfix in the gcc bugzilla)
|
||||
@@ -84,14 +87,17 @@ namespace __gnu_cxx
|
||||
|
||||
#else // !__GNUC__
|
||||
# include <hash_map>
|
||||
# include <hash_set>
|
||||
# if defined(_MSC_VER) && (_MSC_VER >= 1300)
|
||||
// VC7 or above
|
||||
# define STL_HASH_MAP stdext::hash_map
|
||||
# define STL_HASH_MULTIMAP stdext::hash_multimap
|
||||
# define STL_HASH_SET stdext::hash_set
|
||||
# else
|
||||
// VC6 and anything else (most likely name)
|
||||
# define STL_HASH_MAP std::hash_map
|
||||
# define STL_HASH_MULTIMAP std::hash_multimap
|
||||
# define STL_HASH_SET std::hash_set
|
||||
# endif // defined(_MSC_VER) && (_MSC_VER >= 1300)
|
||||
#endif // !__GNUC__
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ private:
|
||||
static JSBool Remove( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
|
||||
static JSBool GetLength( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool IsEmpty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool Clear( JSContext* cx, JSObject* obj, uintN argc, jsval* agv, jsval* rval );
|
||||
static JSBool Clear( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
|
||||
static JSBool Equals( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval );
|
||||
static JSBool AddProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool RemoveProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
static JSBool GetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp );
|
||||
@@ -77,12 +78,13 @@ template<typename T, JSClass* ScriptType> JSFunctionSpec CJSCollection<T, Script
|
||||
{ "pop", Pop, 0, 0, 0 },
|
||||
{ "remove", Remove, 1, 0, 0 },
|
||||
{ "clear", Clear, 0, 0, 0 },
|
||||
{ "equals", Equals, 1, 0, 0 },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
template<typename T, JSClass* ScriptType> std::vector<T>* CJSCollection<T, ScriptType>::RetrieveSet( JSContext* cx, JSObject* obj )
|
||||
{
|
||||
CJSCollectionData* Info = (CJSCollectionData*)JS_GetPrivate( cx, obj );
|
||||
CJSCollectionData* Info = (CJSCollectionData*)JS_GetInstancePrivate( cx, obj, &JSI_class, NULL );
|
||||
if( !Info ) return( NULL );
|
||||
return( Info->m_Data );
|
||||
}
|
||||
@@ -272,6 +274,43 @@ template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::I
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Equals( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
std::vector<T>* a = RetrieveSet( cx, obj );
|
||||
if( !a )
|
||||
return( JS_FALSE );
|
||||
if( ( argc == 0 ) || ( !JSVAL_IS_OBJECT( argv[0] ) ) ) return( JS_FALSE );
|
||||
std::vector<T>* b = RetrieveSet( cx, JSVAL_TO_OBJECT( argv[0] ) );
|
||||
if( !b )
|
||||
return( JS_FALSE );
|
||||
std::vector<T>::iterator ita, itb;
|
||||
|
||||
size_t seek = a->size();
|
||||
for( ita = a->begin(); ita != a->end(); ita++ )
|
||||
for( itb = b->begin(); itb != b->end(); itb++ )
|
||||
if( *ita == *itb ) { seek--; break; }
|
||||
|
||||
if( seek )
|
||||
{
|
||||
*rval = JSVAL_FALSE;
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
seek = b->size();
|
||||
for( itb = b->begin(); itb != b->end(); itb++ )
|
||||
for( ita = a->begin(); ita != a->end(); ita++ )
|
||||
if( *ita == *itb ) { seek--; break; }
|
||||
|
||||
if( seek )
|
||||
{
|
||||
*rval = JSVAL_FALSE;
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
*rval = JSVAL_TRUE;
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
template<typename T, JSClass* ScriptType> JSBool CJSCollection<T, ScriptType>::Subset( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
assert( argc > 0 );
|
||||
|
||||
@@ -78,6 +78,9 @@ public:
|
||||
// Rebuild any intrinsic (mapped-to-C++-variable) properties
|
||||
virtual void Rebuild() = 0;
|
||||
|
||||
// HACK: Doesn't belong here.
|
||||
virtual void rebuildClassSet() = 0;
|
||||
|
||||
// Check for a property
|
||||
virtual IJSComplexProperty* HasProperty( CStrW PropertyName ) = 0;
|
||||
|
||||
|
||||
@@ -17,8 +17,12 @@ CBaseEntity::CBaseEntity()
|
||||
AddProperty( L"parent", &m_base, false );
|
||||
AddProperty( L"actions.move.speed", &m_speed );
|
||||
AddProperty( L"actions.move.turningradius", &m_turningRadius );
|
||||
AddProperty( L"actions.attack.range", &m_meleeRange );
|
||||
AddProperty( L"actions.attack.rangemin", &m_meleeRangeMin );
|
||||
AddProperty( L"actions.attack.range", &( m_melee.m_MaxRange ) );
|
||||
AddProperty( L"actions.attack.rangemin", &( m_melee.m_MinRange ) );
|
||||
AddProperty( L"actions.attack.speed", &( m_melee.m_Speed ) );
|
||||
AddProperty( L"actions.gather.range", &( m_gather.m_MaxRange ) );
|
||||
AddProperty( L"actions.gather.rangemin", &( m_gather.m_MinRange ) );
|
||||
AddProperty( L"actions.gather.speed", &( m_gather.m_Speed ) );
|
||||
AddProperty( L"actor", &m_actorName );
|
||||
AddProperty( L"traits.extant", &m_extant );
|
||||
AddProperty( L"traits.corpse", &m_corpse );
|
||||
@@ -30,7 +34,7 @@ CBaseEntity::CBaseEntity()
|
||||
}
|
||||
|
||||
// Initialize, make life a little easier on the scriptors
|
||||
m_speed = m_turningRadius = m_meleeRange = m_meleeRangeMin = 0.0f;
|
||||
m_speed = m_turningRadius = 0.0f;
|
||||
m_extant = true; m_corpse = CStrW();
|
||||
|
||||
m_bound_type = CBoundingObject::BOUND_NONE;
|
||||
@@ -65,9 +69,76 @@ void CBaseEntity::loadBase()
|
||||
}
|
||||
|
||||
SetBase( m_base );
|
||||
m_classes.SetParent( &( m_base->m_classes ) );
|
||||
SetNextObject( m_base );
|
||||
}
|
||||
|
||||
jsval CBaseEntity::getClassSet()
|
||||
{
|
||||
STL_HASH_SET<CStrW, CStrW_hash_compare>::iterator it;
|
||||
it = m_classes.m_Set.begin();
|
||||
CStrW result = *( it++ );
|
||||
for( ; it != m_classes.m_Set.end(); it++ )
|
||||
result += L" " + *it;
|
||||
return( ToJSVal( result ) );
|
||||
}
|
||||
|
||||
void CBaseEntity::setClassSet( jsval value )
|
||||
{
|
||||
// Get the set that was passed in.
|
||||
CStr temp = ToPrimitive<CStrW>( value );
|
||||
CStr entry;
|
||||
|
||||
m_classes.m_Added.clear();
|
||||
m_classes.m_Removed.clear();
|
||||
|
||||
while( true )
|
||||
{
|
||||
long brk_sp = temp.Find( ' ' );
|
||||
long brk_cm = temp.Find( ',' );
|
||||
long brk = ( brk_sp == -1 ) ? brk_cm : ( brk_cm == -1 ) ? brk_sp : ( brk_sp < brk_cm ) ? brk_sp : brk_cm;
|
||||
|
||||
if( brk == -1 )
|
||||
{
|
||||
entry = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = temp.GetSubstring( 0, brk );
|
||||
temp = temp.GetSubstring( brk + 1, temp.Length() );
|
||||
}
|
||||
|
||||
if( brk != 0 )
|
||||
{
|
||||
|
||||
if( entry[0] == '-' )
|
||||
{
|
||||
entry = entry.GetSubstring( 1, entry.Length() );
|
||||
if( entry.Length() )
|
||||
m_classes.m_Removed.push_back( entry );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( entry[0] == '+' )
|
||||
entry = entry.GetSubstring( 1, entry.Length() );
|
||||
if( entry.Length() )
|
||||
m_classes.m_Added.push_back( entry );
|
||||
}
|
||||
}
|
||||
if( brk == -1 ) break;
|
||||
}
|
||||
|
||||
rebuildClassSet();
|
||||
}
|
||||
|
||||
void CBaseEntity::rebuildClassSet()
|
||||
{
|
||||
m_classes.Rebuild();
|
||||
InheritorsList::iterator it;
|
||||
for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ )
|
||||
(*it)->rebuildClassSet();
|
||||
}
|
||||
|
||||
bool CBaseEntity::loadXML( CStr filename )
|
||||
{
|
||||
CXeromyces XeroFile;
|
||||
@@ -267,6 +338,7 @@ void CBaseEntity::XMLLoadProperty( const CXeromyces& XeroFile, const XMBElement&
|
||||
void CBaseEntity::ScriptingInit()
|
||||
{
|
||||
AddMethod<jsval, &CBaseEntity::ToString>( "toString", 0 );
|
||||
AddClassProperty( L"traits.id.classes", (GetFn)getClassSet, (SetFn)setClassSet );
|
||||
|
||||
CJSComplex<CBaseEntity>::ScriptingInit( "EntityTemplate" );
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// properties such as position, current HP, etc. cannot be inherited from
|
||||
// a template in this way.
|
||||
//
|
||||
// Note: Data-inheritance is not currently implemented.
|
||||
|
||||
#ifndef BASE_ENTITY_INCLUDED
|
||||
#define BASE_ENTITY_INCLUDED
|
||||
@@ -24,6 +23,7 @@
|
||||
#include "scripting/ScriptableComplex.h"
|
||||
#include "BoundingObjects.h"
|
||||
#include "EventHandlers.h"
|
||||
#include "EntitySupport.h"
|
||||
#include "ScriptObject.h"
|
||||
#include "Xeromyces.h"
|
||||
|
||||
@@ -43,6 +43,9 @@ public:
|
||||
CStrW m_corpse;
|
||||
bool m_extant;
|
||||
|
||||
// The class types this entity has
|
||||
SClassSet m_classes;
|
||||
|
||||
CStrW m_Base_Name; // <- We don't guarantee the order XML files will be loaded in, so we'll store the name of the
|
||||
// parent entity referenced, then, after all files are loaded, attempt to match names to objects.
|
||||
|
||||
@@ -55,13 +58,16 @@ public:
|
||||
CBoundingObject::EBoundingType m_bound_type;
|
||||
|
||||
float m_speed;
|
||||
float m_meleeRange;
|
||||
float m_meleeRangeMin;
|
||||
SEntityAction m_melee;
|
||||
SEntityAction m_gather;
|
||||
|
||||
float m_turningRadius;
|
||||
CScriptObject m_EventHandlers[EVENT_LAST];
|
||||
|
||||
void loadBase();
|
||||
jsval getClassSet();
|
||||
void setClassSet( jsval value );
|
||||
void rebuildClassSet();
|
||||
|
||||
// Script-bound functions
|
||||
|
||||
|
||||
@@ -34,8 +34,12 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
|
||||
AddProperty( L"traits.extant", &m_extant );
|
||||
AddProperty( L"traits.corpse", &m_corpse );
|
||||
AddProperty( L"actions.move.turningradius", &m_turningRadius );
|
||||
AddProperty( L"actions.attack.range", &m_meleeRange );
|
||||
AddProperty( L"actions.attack.rangemin", &m_meleeRangeMin );
|
||||
AddProperty( L"actions.attack.range", &( m_melee.m_MaxRange ) );
|
||||
AddProperty( L"actions.attack.rangemin", &( m_melee.m_MinRange ) );
|
||||
AddProperty( L"actions.attack.speed", &( m_melee.m_Speed ) );
|
||||
AddProperty( L"actions.gather.range", &( m_gather.m_MaxRange ) );
|
||||
AddProperty( L"actions.gather.rangemin", &( m_gather.m_MinRange ) );
|
||||
AddProperty( L"actions.gather.speed", &( m_gather.m_Speed ) );
|
||||
AddProperty( L"position", &m_graphics_position, false, (NotifyFn)&CEntity::teleport );
|
||||
AddProperty( L"orientation", &m_graphics_orientation, false, (NotifyFn)&CEntity::reorient );
|
||||
AddProperty( L"player", &m_player );
|
||||
@@ -52,6 +56,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
|
||||
|
||||
m_lastState = -1;
|
||||
m_transition = true;
|
||||
m_fsm_cyclepos = NOT_IN_CYCLE;
|
||||
|
||||
m_base = base;
|
||||
|
||||
@@ -105,6 +110,7 @@ void CEntity::loadBase()
|
||||
// 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 )
|
||||
@@ -168,6 +174,76 @@ void CEntity::snapToGround()
|
||||
m_graphics_position.Y = pTerrain->getExactGroundLevel( m_graphics_position.X, m_graphics_position.Z );
|
||||
}
|
||||
|
||||
jsval CEntity::getClassSet()
|
||||
{
|
||||
STL_HASH_SET<CStrW, CStrW_hash_compare>::iterator it;
|
||||
it = m_classes.m_Set.begin();
|
||||
CStrW result = L"";
|
||||
if( it != m_classes.m_Set.end() )
|
||||
{
|
||||
result = *( it++ );
|
||||
for( ; it != m_classes.m_Set.end(); it++ )
|
||||
result += L" " + *it;
|
||||
}
|
||||
return( ToJSVal( result ) );
|
||||
}
|
||||
|
||||
void CEntity::setClassSet( jsval value )
|
||||
{
|
||||
// Get the set that was passed in.
|
||||
CStr temp = ToPrimitive<CStrW>( value );
|
||||
CStr entry;
|
||||
|
||||
m_classes.m_Added.clear();
|
||||
m_classes.m_Removed.clear();
|
||||
|
||||
while( true )
|
||||
{
|
||||
long brk_sp = temp.Find( ' ' );
|
||||
long brk_cm = temp.Find( ',' );
|
||||
long brk = ( brk_sp == -1 ) ? brk_cm : ( brk_cm == -1 ) ? brk_sp : ( brk_sp < brk_cm ) ? brk_sp : brk_cm;
|
||||
|
||||
if( brk == -1 )
|
||||
{
|
||||
entry = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = temp.GetSubstring( 0, brk );
|
||||
temp = temp.GetSubstring( brk + 1, temp.Length() );
|
||||
}
|
||||
|
||||
if( brk != 0 )
|
||||
{
|
||||
|
||||
if( entry[0] == '-' )
|
||||
{
|
||||
entry = entry.GetSubstring( 1, entry.Length() );
|
||||
if( entry.Length() )
|
||||
m_classes.m_Removed.push_back( entry );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( entry[0] == '+' )
|
||||
entry = entry.GetSubstring( 1, entry.Length() );
|
||||
if( entry.Length() )
|
||||
m_classes.m_Added.push_back( entry );
|
||||
}
|
||||
}
|
||||
if( brk == -1 ) break;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
m_position_previous = m_position;
|
||||
@@ -183,10 +259,11 @@ void CEntity::update( size_t timestep )
|
||||
{
|
||||
CEntityOrder* current = &m_orderQueue.front();
|
||||
|
||||
m_transition = ( current->m_type != m_lastState );
|
||||
|
||||
if( m_transition )
|
||||
if( current->m_type != m_lastState )
|
||||
{
|
||||
m_transition = true;
|
||||
m_fsm_cyclepos = NOT_IN_CYCLE;
|
||||
|
||||
PROFILE( "state transition / order" );
|
||||
|
||||
CEntity* target = NULL;
|
||||
@@ -210,6 +287,8 @@ void CEntity::update( size_t timestep )
|
||||
|
||||
m_lastState = current->m_type;
|
||||
}
|
||||
else
|
||||
m_transition = false;
|
||||
|
||||
switch( current->m_type )
|
||||
{
|
||||
@@ -288,48 +367,6 @@ void CEntity::Damage( CDamageType& damage, CEntity* inflictor )
|
||||
DispatchEvent( &evt );
|
||||
}
|
||||
|
||||
/*
|
||||
void CEntity::dispatch( const CMessage* msg )
|
||||
{
|
||||
|
||||
switch( msg->type )
|
||||
{
|
||||
case CMessage::EMSG_TICK:
|
||||
{
|
||||
CEventTick Tick;
|
||||
DispatchEvent( &Tick );
|
||||
break;
|
||||
}
|
||||
case CMessage::EMSG_INIT:
|
||||
{
|
||||
CEventInitialize Init;
|
||||
if( !DispatchEvent( &Init ) )
|
||||
break;
|
||||
|
||||
if( m_base->m_Tag == CStrW( L"Prometheus Dude" ) )
|
||||
{
|
||||
if( getCollisionObject( this ) )
|
||||
{
|
||||
// Prometheus telefragging. (Appeared inside another object)
|
||||
kill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMessage::EMSG_ORDER:
|
||||
CMessageOrder* m;
|
||||
m = (CMessageOrder*)msg;
|
||||
if( !m->queue )
|
||||
clearOrders();
|
||||
pushOrder( m->order );
|
||||
break;
|
||||
case CMessage::EMSG_DAMAGE:
|
||||
CEntityOrder* o;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void CEntity::clearOrders()
|
||||
{
|
||||
m_orderQueue.clear();
|
||||
@@ -607,9 +644,11 @@ void CEntity::ScriptingInit()
|
||||
AddMethod<bool, &CEntity::Kill>( "kill", 0 );
|
||||
AddMethod<bool, &CEntity::Damage>( "damage", 1 );
|
||||
AddMethod<bool, &CEntity::IsIdle>( "isIdle", 0 );
|
||||
AddMethod<bool, &CEntity::HasClass>( "hasClass", 1 );
|
||||
AddMethod<jsval, &CEntity::GetSpawnPoint>( "getSpawnPoint", 1 );
|
||||
|
||||
AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase );
|
||||
AddClassProperty( L"traits.id.classes", (GetFn)getClassSet, (SetFn)setClassSet );
|
||||
|
||||
CJSComplex<CEntity>::ScriptingInit( "Entity", Construct, 2 );
|
||||
}
|
||||
|
||||
@@ -66,10 +66,13 @@ public:
|
||||
// The entity to switch to when this dies.
|
||||
CStrW m_corpse;
|
||||
|
||||
// The class types this entity has
|
||||
SClassSet m_classes;
|
||||
|
||||
float m_speed;
|
||||
float m_turningRadius;
|
||||
float m_meleeRange;
|
||||
float m_meleeRangeMin;
|
||||
SEntityAction m_melee;
|
||||
SEntityAction m_gather;
|
||||
bool m_selected;
|
||||
i32 m_grouped;
|
||||
|
||||
@@ -99,10 +102,6 @@ public:
|
||||
// Get script execution contexts - always run in the context of the entity that fired it.
|
||||
JSObject* GetScriptExecContext( IEventTarget* target ) { return( ((CEntity*)target)->GetScript() ); }
|
||||
|
||||
// EventListener adaptation?
|
||||
//typedef std::vector<CScriptObject> ExtendedHandlerList;
|
||||
//typedef STL_HASH_MAP<CStrW, HandlerList, CStrW_hash_compare> ExtendedHandlerTable;
|
||||
|
||||
CScriptObject m_EventHandlers[EVENT_LAST];
|
||||
|
||||
CUnit* m_actor;
|
||||
@@ -110,16 +109,23 @@ public:
|
||||
// State transition in the FSM (animations should be reset)
|
||||
bool m_transition;
|
||||
int m_lastState;
|
||||
|
||||
// Position in the current state's cycle
|
||||
static const size_t NOT_IN_CYCLE = -1;
|
||||
size_t m_fsm_cyclepos; // -cycle_length....cycle_length
|
||||
CSkeletonAnim* m_fsm_animation; // the animation we're about to play this cycle,
|
||||
size_t m_fsm_anipos; // the time at which we should start playing it.
|
||||
size_t m_fsm_anipos2; // for when there are two animation-related events we need to take care of.
|
||||
|
||||
std::deque<CEntityOrder> m_orderQueue;
|
||||
|
||||
private:
|
||||
CEntity( CBaseEntity* base, CVector3D position, float orientation );
|
||||
|
||||
/*EGotoSituation*/ uint processGotoHelper( CEntityOrder* current, size_t timestep_milli, HEntity& collide );
|
||||
uint processGotoHelper( CEntityOrder* current, size_t timestep_milli, HEntity& collide );
|
||||
|
||||
bool processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, float range );
|
||||
bool processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, float range, float minRange );
|
||||
bool processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, SEntityAction* action );
|
||||
bool processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, SEntityAction* action );
|
||||
|
||||
bool processAttackMelee( CEntityOrder* current, size_t timestep_milli );
|
||||
bool processAttackMeleeNoPathing( CEntityOrder* current, size_t timestep_milli );
|
||||
@@ -162,6 +168,11 @@ public:
|
||||
void snapToGround();
|
||||
void updateActorTransforms();
|
||||
|
||||
// Getter and setter for the class sets
|
||||
jsval getClassSet();
|
||||
void setClassSet( jsval value );
|
||||
void rebuildClassSet();
|
||||
|
||||
// Things like selection circles and debug info - possibly move to gui if/when it becomes responsible for (and capable of) it.
|
||||
void render();
|
||||
void renderSelectionOutline( float alpha = 1.0f );
|
||||
@@ -207,6 +218,11 @@ public:
|
||||
{
|
||||
return( m_orderQueue.empty() );
|
||||
}
|
||||
bool HasClass( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
assert( argc >= 1 );
|
||||
return( m_classes.IsMember( ToPrimitive<CStrW>( cx, argv[0] ) ) );
|
||||
}
|
||||
static void ScriptingInit();
|
||||
};
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
|
||||
}
|
||||
|
||||
// Handles processing common to (at the moment) gather and melee attack actions
|
||||
bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, float range )
|
||||
bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_millis, int transition, SEntityAction* action )
|
||||
{
|
||||
m_orderQueue.pop_front();
|
||||
|
||||
@@ -278,7 +278,7 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_milli
|
||||
|
||||
current->m_data[0].location = current->m_data[0].entity->m_position;
|
||||
|
||||
if( ( current->m_data[0].location - m_position ).length() < m_meleeRange )
|
||||
if( ( current->m_data[0].location - m_position ).length() < action->m_MaxRange )
|
||||
{
|
||||
(int&)current->m_type = transition;
|
||||
return( true );
|
||||
@@ -297,9 +297,45 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_milli
|
||||
|
||||
return( true );
|
||||
}
|
||||
bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, float range, float minRange = 0.0f )
|
||||
bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, SEntityAction* action )
|
||||
{
|
||||
static size_t cyclepos = 0;
|
||||
if( m_fsm_cyclepos != NOT_IN_CYCLE )
|
||||
{
|
||||
size_t nextpos = m_fsm_cyclepos + timestep_millis * 2;
|
||||
if( ( m_fsm_cyclepos <= m_fsm_anipos ) &&
|
||||
( nextpos > m_fsm_anipos ) )
|
||||
{
|
||||
// Start playing.
|
||||
// Start the animation. Actual damage/gather will be done in a
|
||||
// few hundred ms, at the 'action point' of the animation we're
|
||||
// now setting.
|
||||
|
||||
m_actor->GetModel()->SetAnimation( m_fsm_animation, true );
|
||||
}
|
||||
|
||||
if( ( m_fsm_cyclepos <= action->m_Speed ) && ( nextpos > action->m_Speed ) )
|
||||
{
|
||||
DispatchEvent( contactEvent );
|
||||
// Note that, at the moment, we don't care if the action succeeds or fails -
|
||||
// we could check for failure, then abort the animation.
|
||||
// It depends what we think is worse: stopping an animation halfway through,
|
||||
// or playing the animation without getting a game effect.
|
||||
|
||||
// Could also check again here if the entity still exists, is in range, etc..
|
||||
// and cancel if not, but we'll see how it looks without that, first.
|
||||
}
|
||||
|
||||
if( nextpos >= ( action->m_Speed * 2 ) )
|
||||
{
|
||||
// End of cycle.
|
||||
m_fsm_cyclepos = NOT_IN_CYCLE;
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Otherwise, increment position.
|
||||
m_fsm_cyclepos = nextpos;
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Target's dead (or exhausted)? Then our work here is done.
|
||||
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
|
||||
@@ -308,42 +344,13 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
return( false );
|
||||
}
|
||||
|
||||
if( m_actor )
|
||||
{
|
||||
if( animation && ( m_actor->GetModel()->GetAnimation() == animation ) )
|
||||
{
|
||||
size_t cyclenext = cyclepos + timestep_millis;
|
||||
if( ( cyclepos <= animation->m_ActionPos ) &&
|
||||
( cyclenext > animation->m_ActionPos ) )
|
||||
{
|
||||
// Actually execute the action script in the sim frame
|
||||
// that contains the animation's 'action point' (as
|
||||
// specified by the artist that created it)
|
||||
if( !DispatchEvent( contactEvent ) )
|
||||
{
|
||||
// The script is cancelling the attack/gather action.
|
||||
|
||||
// Cancel the animation (will probably cause a graphical
|
||||
// glitch, but can't be helped)
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
}
|
||||
}
|
||||
cyclepos = cyclenext;
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Just transitioned? No animation? (=> melee just finished) Play walk.
|
||||
if( m_transition || !m_actor->GetModel()->GetAnimation() )
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
}
|
||||
|
||||
CVector2D delta = current->m_data[0].entity->m_position - m_position;
|
||||
|
||||
float adjRange = range + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
|
||||
float adjRange = action->m_MaxRange + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
|
||||
|
||||
if( minRange > 0.0f )
|
||||
if( action->m_MinRange > 0.0f )
|
||||
{
|
||||
float adjMinRange = m_meleeRangeMin + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
|
||||
float adjMinRange = action->m_MinRange + m_bounds->m_radius + current->m_data[0].entity->m_bounds->m_radius;
|
||||
if( delta.within( adjMinRange ) )
|
||||
{
|
||||
// Too close... do nothing.
|
||||
@@ -357,6 +364,14 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
// We're aiming to end up at a location just inside our maximum range
|
||||
// (is this good enough?)
|
||||
|
||||
// Play walk for a bit.
|
||||
if( m_actor && ( m_actor->GetModel()->GetAnimation() != m_actor->GetObject()->m_WalkAnim ) )
|
||||
{
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
// Animation desync
|
||||
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
|
||||
}
|
||||
|
||||
delta = delta.normalize() * ( adjRange - m_bounds->m_radius );
|
||||
|
||||
current->m_data[0].location = (CVector2D)current->m_data[0].entity->m_position - delta;
|
||||
@@ -411,39 +426,52 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close enough, but turn to face them.
|
||||
// Close enough, but turn to face them.
|
||||
m_orientation = atan2( delta.x, delta.y );
|
||||
m_ahead = delta.normalize();
|
||||
}
|
||||
|
||||
// Start the animation. Actual damage/gather will be done in a
|
||||
// few hundred msec, at the 'action point' of the animation we're
|
||||
// now setting.
|
||||
// Pick our animation, calculate the time to play it, and start the timer.
|
||||
m_fsm_animation = animation; // <- Replace with a call that gets one randomly, probably pass in a CSkeletonAnim* (void) fn for this purpose
|
||||
|
||||
// Here's the idea - we want to be at that animation's event point
|
||||
// when the timer reaches action->m_Speed. The timer increments by 2 every millisecond.
|
||||
// animation->m_actionpos is the time offset into that animation that event
|
||||
// should happen. So...
|
||||
m_fsm_anipos = action->m_Speed - ( m_fsm_animation->m_ActionPos * 2 );
|
||||
// But...
|
||||
if( action->m_Speed < ( m_fsm_animation->m_ActionPos * 2 ) )
|
||||
{
|
||||
// We ought to have started it in the past. Oh well.
|
||||
// Here's what we'll do: play it now, and advance it to
|
||||
// the point it should be by now.
|
||||
m_actor->GetModel()->SetAnimation( m_fsm_animation, true );
|
||||
m_actor->GetModel()->Update( m_fsm_animation->m_ActionPos / 1000.0f - action->m_Speed / 2000.0f );
|
||||
}
|
||||
|
||||
m_actor->GetModel()->SetAnimation( animation, true );
|
||||
cyclepos = 0;
|
||||
m_fsm_cyclepos = 0;
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
||||
bool CEntity::processAttackMelee( CEntityOrder* current, size_t timestep_millis )
|
||||
{
|
||||
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING, 0.5 ) );
|
||||
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_ATTACK_MELEE_NOPATHING, &m_melee ) );
|
||||
}
|
||||
bool CEntity::processAttackMeleeNoPathing( CEntityOrder* current, size_t timestep_milli )
|
||||
{
|
||||
CEventAttack evt( current->m_data[0].entity );
|
||||
return( processContactActionNoPathing( current, timestep_milli, m_actor ? m_actor->GetObject()->m_MeleeAnim : NULL, &evt, m_meleeRange, m_meleeRangeMin ) );
|
||||
return( processContactActionNoPathing( current, timestep_milli, m_actor ? m_actor->GetObject()->m_MeleeAnim : NULL, &evt, &m_melee ) );
|
||||
}
|
||||
bool CEntity::processGather( CEntityOrder* current, size_t timestep_millis )
|
||||
{
|
||||
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_GATHER_NOPATHING, 0.5 ) );
|
||||
return( processContactAction( current, timestep_millis, CEntityOrder::ORDER_GATHER_NOPATHING, &m_gather ) );
|
||||
}
|
||||
|
||||
bool CEntity::processGatherNoPathing( CEntityOrder* current, size_t timestep_millis )
|
||||
{
|
||||
CEventGather evt( current->m_data[0].entity );
|
||||
return( processContactActionNoPathing( current, timestep_millis, m_actor ? m_actor->GetObject()->m_GatherAnim : NULL, &evt, 5.0, 0.0 ) );
|
||||
return( processContactActionNoPathing( current, timestep_millis, m_actor ? m_actor->GetObject()->m_GatherAnim : NULL, &evt, &m_gather ) );
|
||||
}
|
||||
|
||||
bool CEntity::processGoto( CEntityOrder* current, size_t timestep_millis )
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Supporting data types for CEntity and related
|
||||
|
||||
#ifndef ENTITY_SUPPORT_INCLUDED
|
||||
#define ENTITY_SUPPORT_INCLUDED
|
||||
|
||||
class CEntityManager;
|
||||
|
||||
class CDamageType : public CJSObject<CDamageType>
|
||||
@@ -68,3 +71,44 @@ public:
|
||||
return( JS_TRUE );
|
||||
}
|
||||
};
|
||||
|
||||
struct SEntityAction
|
||||
{
|
||||
float m_MaxRange;
|
||||
float m_MinRange;
|
||||
size_t m_Speed;
|
||||
SEntityAction() { m_MaxRange = m_MinRange = 0.0f; m_Speed = 1000; }
|
||||
};
|
||||
|
||||
struct SClassSet
|
||||
{
|
||||
SClassSet* m_Parent;
|
||||
|
||||
STL_HASH_SET<CStrW, CStrW_hash_compare> m_Set;
|
||||
std::vector<CStrW> m_Added;
|
||||
std::vector<CStrW> m_Removed;
|
||||
|
||||
inline SClassSet() { m_Parent = NULL; }
|
||||
|
||||
inline bool IsMember( CStrW Test )
|
||||
{ return( m_Set.find( Test ) != m_Set.end() ); }
|
||||
|
||||
inline void SetParent( SClassSet* Parent )
|
||||
{ m_Parent = Parent; Rebuild(); }
|
||||
|
||||
void Rebuild()
|
||||
{
|
||||
if( m_Parent )
|
||||
m_Set = m_Parent->m_Set;
|
||||
else
|
||||
m_Set.clear();
|
||||
|
||||
std::vector<CStrW>::iterator it;
|
||||
for( it = m_Removed.begin(); it != m_Removed.end(); it++ )
|
||||
m_Set.erase( *it );
|
||||
for( it = m_Added.begin(); it != m_Added.end(); it++ )
|
||||
m_Set.insert( *it );
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user