diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index f1d52c6af8..03981817e2 100755 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -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 ) ); diff --git a/source/simulation/BaseEntity.h b/source/simulation/BaseEntity.h index 1d2c32089e..7879c6abd0 100755 --- a/source/simulation/BaseEntity.h +++ b/source/simulation/BaseEntity.h @@ -75,7 +75,8 @@ public: bool m_permanent; float m_speed; - + + SEntityAction m_run; SEntityAction m_melee; SEntityAction m_gather; SEntityAction m_heal; diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 93bca3b112..dbef40ae01 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -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( argv[0] ); + *( (uint*) &(notify.m_type) ) = ToPrimitive( argv[1] ); + + if ( ToPrimitive( argv[2] ) ) + { + std::deque::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( argv[0] ); + for (int i=0; ipushOrder( this->m_orderQueue.front() ); + } + return JSVAL_VOID; } -void CEntity::DispatchNotification( CEntityListener notify ) -{ - CEventNotification evt( notify ); - DispatchEvent( &evt ); -} -*/ + void CEntity::repath() { CVector2D destination; diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index c3fa4622dd..35729cdf06 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -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 m_orderQueue; - //std::deque m_notifications; - //std::deque m_listeners; + std::deque 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 diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index af073022b4..32b2822660 100755 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -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, diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index a236445586..d6c2eebf7a 100755 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -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.