1
0
forked from mirrors/0ad

Jason's animation events. Also reworked entity-types system.

This was SVN commit r2209.
This commit is contained in:
MarkT
2005-05-01 19:09:13 +00:00
parent 32e3ff0921
commit db168702df
79 changed files with 485 additions and 250 deletions
+8 -4
View File
@@ -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();
+1 -1
View File
@@ -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);
+6 -4
View File
@@ -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
+5 -3
View File
@@ -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;
};
+7 -1
View File
@@ -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__
+41 -2
View File
@@ -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 );
+3
View File
@@ -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;
+75 -3
View File
@@ -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" );
}
+9 -3
View File
@@ -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
+86 -47
View File
@@ -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 );
}
+25 -9
View File
@@ -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();
};
+74 -46
View File
@@ -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 )
+44
View File
@@ -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