1
0
forked from 0ad/0ad

Added run order (if applicable, the unit will use the run animation and speed for movement, otherwise walk is used). If the unit's run speed is more than 0 and the target is within the run action's range, it will run instead of walk.

Notifications - called from javascript.  You request orders with a
target entity, order type, and whether the previous listeners you have
requested for this entity should be destroyed.  When the target entity
processes an order that was requested, that order is pushed onto the
queue of the requester.  This is useful for things such as follow that
require the actual order to perform the action.

This was SVN commit r3329.
This commit is contained in:
pyrolink 2006-01-05 06:13:31 +00:00
parent 763eb9311d
commit 2d8f45fd94
6 changed files with 128 additions and 39 deletions

View File

@ -19,6 +19,9 @@ 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.move.run.speed", &( m_run.m_Speed ) );
AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) );
AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) );
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 ) );

View File

@ -75,7 +75,8 @@ public:
bool m_permanent;
float m_speed;
SEntityAction m_run;
SEntityAction m_melee;
SEntityAction m_gather;
SEntityAction m_heal;

View File

@ -34,6 +34,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
m_ahead.y = cos( m_orientation );
AddProperty( L"actions.move.speed", &m_speed );
AddProperty( L"actions.move.run.speed", &( m_run.m_Speed ) );
AddProperty( L"actions.move.run.rangemin", &( m_run.m_MinRange ) );
AddProperty( L"actions.move.run.range", &( m_run.m_MaxRange ) );
AddProperty( L"selected", &m_selected, false, (NotifyFn)&CEntity::checkSelection );
AddProperty( L"group", &m_grouped, false, (NotifyFn)&CEntity::checkGroup );
AddProperty( L"traits.extant", &m_extant );
@ -404,6 +407,7 @@ void CEntity::update( size_t timestep )
updateCollisionPatch();
return;
case CEntityOrder::ORDER_GOTO:
case CEntityOrder::ORDER_RUN:
if( processGoto( current, timestep ) )
break;
updateCollisionPatch();
@ -641,28 +645,43 @@ bool CEntity::acceptsOrder( int orderType, CEntity* orderTarget )
return( DispatchEvent( &evt ) );
}
/*void CEntity::RequestNotification( CEntity* target, unsigned long orderType )
jsval CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv )
{
debug_assert( argc >= 3 );
CEntityListener notify;
notify.m_sender = this;
notify.m_type = orderType;
//(Convert from int to enum)
CEntity* target = ToPrimitive<CEntity*>( argv[0] );
*( (uint*) &(notify.m_type) ) = ToPrimitive<int>( argv[1] );
if ( ToPrimitive<bool>( argv[2] ) )
{
std::deque<CEntityListener>::iterator it = target->m_listeners.begin();
for ( ; it != target->m_listeners.end(); it++)
{
if ( it->m_sender == this )
target->m_listeners.erase(it);
}
}
target->m_listeners.push_back( notify );
return JSVAL_VOID;
}
void CEntity::SendNotification( CEntity* target, unsigned long orderType )
jsval CEntity::CheckListeners( JSContext *cx, uintN argc, jsval* argv )
{
CEntityListener notify;
notify.m_sender = this;
notify.m_type = orderType;
target->m_notifications.push_back( notify );
debug_assert( argc >= 1);
int type = ToPrimitive<int>( argv[0] );
for (int i=0; i<m_listeners.size(); i++)
{
if (m_listeners[i].m_type & type)
m_listeners[i].m_sender->pushOrder( this->m_orderQueue.front() );
}
return JSVAL_VOID;
}
void CEntity::DispatchNotification( CEntityListener notify )
{
CEventNotification evt( notify );
DispatchEvent( &evt );
}
*/
void CEntity::repath()
{
CVector2D destination;

View File

@ -72,7 +72,9 @@ public:
float m_speed;
float m_turningRadius;
float m_trampleStart;
SEntityAction m_run;
SEntityAction m_melee;
SEntityAction m_gather;
SEntityAction m_heal;
@ -153,8 +155,7 @@ public:
size_t m_fsm_anipos2; // for when there are two animation-related events we need to take care of.
std::deque<CEntityOrder> m_orderQueue;
//std::deque<CEntityListener> m_notifications;
//std::deque<CEntityListener> m_listeners;
std::deque<CEntityListener> m_listeners;
private:
CEntity( CBaseEntity* base, CVector3D position, float orientation );
@ -253,10 +254,9 @@ public:
void clearOrders();
void pushOrder( CEntityOrder& order );
//void RequestNotification( CEntity* target, unsigned long orderType );
//void SendNotification( CEntity* target, unsigned long orderType );
//void DispatchNotification( CEntityListener notify );
jsval RequestNotification( JSContext* cx, uintN argc, jsval* argv );
jsval CheckListeners( JSContext* cx, uintN argc, jsval* argv );
// Script constructor

View File

@ -38,6 +38,7 @@
#include "EntityHandles.h"
#include "Vector2D.h"
#include "scripting/DOMEvent.h"
struct SOrderData
{
@ -46,6 +47,27 @@ struct SOrderData
HEntity entity;
};
class CEntityListener
{
public:
enum
{
NOTIFY_GOTO = 0x01,
NOTIFY_RUN = 0x02,
NOTIFY_FOLLOW = 0x03, //GOTO | RUN
NOTIFY_ATTACK = 0x04,
NOTIFY_DAMAGE = 0x08,
NOTIFY_ESCORT = 0x0D, //GOTO | ATTACK | DAMAGE
NOTIFY_HEAL = 0x10,
NOTIFY_GATHER = 0x20
} m_type;
CEntity* m_sender;
};
class CEntityOrder
{
public:
@ -53,8 +75,9 @@ public:
{
ORDER_GOTO_NOPATHING,
ORDER_GOTO_SMOOTHED,
ORDER_GOTO_COLLISION,
ORDER_GOTO_COLLISION,
ORDER_GOTO,
ORDER_RUN,
ORDER_PATROL,
ORDER_ATTACK_MELEE,
ORDER_ATTACK_MELEE_NOPATHING,

View File

@ -46,7 +46,7 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis,
// Curve smoothing.
// Here there be trig.
float scale = m_speed * timestep;
float scale = ( m_run.m_Speed > 0 ? m_run.m_Speed : m_speed ) * timestep;
// Note: Easy optimization: flag somewhere that this unit
// is already pointing the right way, and don't do this
@ -278,10 +278,10 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timeste
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
return( false );
current->m_data[0].location = current->m_data[0].entity->m_position;
if( ( current->m_data[0].location - m_position ).length() < action->m_MaxRange )
float Distance = (current->m_data[0].location - m_position).length();
if( Distance < action->m_MaxRange )
{
(int&)current->m_type = transition;
m_orderQueue.push_front(*current); // Seems to be needed since we do a pop above
@ -290,8 +290,19 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timeste
if( m_transition && m_actor )
{
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
if (m_run.m_Speed > 0 )
{
if ( Distance > m_run.m_MinRange && Distance < m_run.m_MaxRange )
{
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
}
}
else
{
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
}
// Animation desync
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
}
@ -353,6 +364,9 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
// Target's dead (or exhausted)? Then our work here is done.
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
{
//TODO: eventually when stances/formations are implemented, if applicable (e.g. not
//heal or if defensive stance), the unit should expand and continue the order.
m_orderQueue.pop_front();
return( false );
}
@ -376,9 +390,27 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
// Too far away at the moment, chase after the target...
// We're aiming to end up at a location just inside our maximum range
// (is this good enough?)
delta = delta.normalize() * ( adjRange - m_bounds->m_radius );
//Determine whether to run or to walk
if ( m_run.m_Speed > 0 )
{
float deltaLength = delta.length();
if ( m_actor && ! m_actor->IsPlayingAnimation( "run" ) )
{
//Are we in range?
if ( deltaLength < m_run.m_MaxRange && deltaLength > m_run.m_MinRange )
{
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
// Animation desync
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
}
}
}
// Play walk for a bit.
if( m_actor && ! m_actor->IsPlayingAnimation( "walk" ) )
else if( m_actor && ! m_actor->IsPlayingAnimation( "walk" ) )
{
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
@ -386,7 +418,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
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;
@ -554,19 +586,30 @@ bool CEntity::processGoto( CEntityOrder* current, size_t UNUSED(timestep_millis)
CVector2D pos( m_position.X, m_position.Z );
CVector2D path_to = current->m_data[0].location;
m_orderQueue.pop_front();
float Distance = ( path_to - pos ).length();
// Let's just check we're going somewhere...
if( ( path_to - pos ).length() < 0.1f )
if( Distance < 0.1f )
return( false );
if( m_transition && m_actor )
{
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
if( walk )
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
// Animation desync
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
if (m_run.m_Speed > 0 )
{
if ( Distance > m_run.m_MinRange && Distance < m_run.m_MaxRange )
{
CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" );
m_actor->GetModel()->SetAnimation( run, false, m_run.m_Speed * run->m_AnimDef->GetDuration() );
}
}
else
{
CSkeletonAnim* walk = m_actor->GetRandomAnimation( "walk" );
if( walk )
m_actor->GetModel()->SetAnimation( walk, false, m_speed * walk->m_AnimDef->GetDuration() );
// Animation desync
m_actor->GetModel()->Update( ( rand() * 1000.0f ) / 1000.0f );
}
}
// The pathfinder will push its result back into this unit's queue.