diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index 03981817e2..b86c63cc29 100755 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -14,7 +14,7 @@ STL_HASH_SET CBaseEntity::scriptsLoaded; CBaseEntity::CBaseEntity() { m_base = NULL; - + AddProperty( L"tag", &m_Tag, false ); AddProperty( L"parent", &m_base, false ); AddProperty( L"actions.move.speed", &m_speed ); @@ -22,6 +22,8 @@ CBaseEntity::CBaseEntity() 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.move.run.regen_rate", &m_runRegenRate ); + AddProperty( L"actions.move.run.decay_rate", &m_runDecayRate ); 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 ) ); @@ -35,8 +37,13 @@ CBaseEntity::CBaseEntity() AddProperty( L"traits.extant", &m_extant ); AddProperty( L"traits.corpse", &m_corpse ); AddProperty( L"traits.health.curr", &m_healthCurr ); - AddProperty( L"traits.health.max", &m_healthMax ); - AddProperty( L"traits.health.bar_height", &m_healthBarHeight ); + AddProperty( L"traits.health.max", &m_healthMax ); + AddProperty( L"traits.health.bar_height", &m_healthBarHeight ); + AddProperty( L"traits.health.bar_size", &m_healthBarSize ); + AddProperty( L"traits.stamina.curr", &m_staminaCurr ); + AddProperty( L"traits.stamina.max", &m_staminaMax ); + AddProperty( L"traits.stamina.bar_height", &m_staminaBarHeight ); + AddProperty( L"traits.stamina.bar_size", &m_staminaBarSize ); AddProperty( L"traits.minimap.type", &m_minimapType ); AddProperty( L"traits.minimap.red", &m_minimapR ); AddProperty( L"traits.minimap.green", &m_minimapG ); diff --git a/source/simulation/BaseEntity.h b/source/simulation/BaseEntity.h index 7879c6abd0..7eeb7651fd 100755 --- a/source/simulation/BaseEntity.h +++ b/source/simulation/BaseEntity.h @@ -55,11 +55,18 @@ public: CBoundingCircle* m_bound_circle; CBoundingBox* m_bound_box; CBoundingObject::EBoundingType m_bound_type; - + + //SP properties + float m_staminaCurr; + float m_staminaMax; + float m_staminaBarHeight; + int m_staminaBarSize; + // HP properties float m_healthCurr; float m_healthMax; float m_healthBarHeight; + int m_healthBarSize; // Minimap properties CStrW m_minimapType; @@ -75,7 +82,9 @@ public: bool m_permanent; float m_speed; - + float m_runRegenRate; + float m_runDecayRate; + SEntityAction m_run; SEntityAction m_melee; SEntityAction m_gather; diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index d20de51531..6283cd437b 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -33,11 +33,16 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) m_orientation = orientation; m_ahead.x = sin( m_orientation ); m_ahead.y = cos( m_orientation ); + + /* Anything added to this list MUST be added to BaseEntity.cpp (and variables used should + also be added to BaseEntity.h */ 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"actions.move.run.regen_rate", &m_runRegenRate ); + AddProperty( L"actions.move.run.decay_rate", &m_runDecayRate ); AddProperty( L"selected", &m_selected, false, (NotifyFn)&CEntity::checkSelection ); AddProperty( L"group", &m_grouped, false, (NotifyFn)&CEntity::checkGroup ); AddProperty( L"traits.extant", &m_extant ); @@ -58,6 +63,11 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) AddProperty( L"traits.health.curr", &m_healthCurr ); AddProperty( L"traits.health.max", &m_healthMax ); AddProperty( L"traits.health.bar_height", &m_healthBarHeight ); + AddProperty( L"traits.health.bar_size", &m_healthBarSize ); + AddProperty( L"traits.stamina.curr", &m_staminaCurr ); + AddProperty( L"traits.stamina.max", &m_staminaMax ); + AddProperty( L"traits.stamina.bar_height", &m_staminaBarHeight ); + AddProperty( L"traits.stamina.bar_size", &m_staminaBarSize ); AddProperty( L"traits.minimap.type", &m_minimapType ); AddProperty( L"traits.minimap.red", &m_minimapR ); AddProperty( L"traits.minimap.green", &m_minimapG ); @@ -102,6 +112,12 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) m_isRunning = false; + m_shouldRun = false; + + m_triggerRun = false; + + m_frameCheck = 0; + m_grouped = -1; m_player = g_Game->GetPlayer( 0 ); @@ -304,8 +320,21 @@ void CEntity::update( size_t timestep ) { m_position_previous = m_position; m_orientation_previous = m_orientation; + - // Note: aura processing is done before state processing because the state + if ( m_triggerRun ) + m_frameCheck++; + + CalculateRun( timestep ); + + if ( m_frameCheck != 0 ) + { + m_shouldRun = true; + m_triggerRun = false; + m_frameCheck = 0; + } + + // Note: aura processing is done before state processing because the state // processing code is filled with all kinds of returns PROFILE_START( "aura processing" ); @@ -410,6 +439,7 @@ void CEntity::update( size_t timestep ) updateCollisionPatch(); return; case CEntityOrder::ORDER_GOTO: + case CEntityOrder::ORDER_RUN: if( processGoto( current, timestep ) ) break; updateCollisionPatch(); @@ -647,90 +677,13 @@ bool CEntity::acceptsOrder( int orderType, CEntity* orderTarget ) return( DispatchEvent( &evt ) ); } -jsval CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv ) -{ - debug_assert( argc > 3 ); - CEntityListener notify; - - notify.m_sender = this; - //(Convert from int to enum) - CEntity *target = ToNative( argv[0] ); - *( (uint*) &(notify.m_type) ) = ToPrimitive( argv[1] ); - - bool destroy = ToPrimitive( argv[2] ); - - std::deque::iterator it = target->m_listeners.begin(); - for ( ; it != target->m_listeners.end(); it++) - { - if ( destroy && it->m_sender == this ) - target->m_listeners.erase(it); - } - - target->m_listeners.push_back( notify ); - return JSVAL_VOID; -} void CEntity::DispatchNotification( CEntityOrder order, uint type ) { CEventNotification evt( order, type ); DispatchEvent( &evt ); } -jsval CEntity::CheckListeners( JSContext *cx, uintN argc, jsval* argv ) -{ - if( argc < 1 ) - { - JS_ReportError( cx, "Too few parameters" ); - return( false ); - } - int type = ToPrimitive( argv[0] ); //notify code - CEntityOrder order = this->m_orderQueue.front(); - CEntity* target; - for (int i=0; iDispatchNotification( order, type ); - break; - - case CEntityListener::NOTIFY_HEAL: - case CEntityListener::NOTIFY_ATTACK: - case CEntityListener::NOTIFY_GATHER: - if( argc < 3 ) - { - JS_ReportError( cx, "Too few parameters" ); - continue; - } - target = ToNative( argv[1] ); - order.m_data[1].data = ToPrimitive( argv[2] ); //which action - order.m_data[0].entity = target->me; - - m_listeners[i].m_sender->DispatchNotification( order, type ); - break; - case CEntityListener::NOTIFY_DAMAGE: - if( argc < 2 ) - { - JS_ReportError( cx, "Too few parameters" ); - continue; - } - target = ToNative( argv[1] ); - order.m_data[0].entity = target->me; - - m_listeners[i].m_sender->DispatchNotification( order, type ); - - default: - JS_ReportError( cx, "Invalid order type" ); - continue; - } - } - return JSVAL_VOID; -} - - - void CEntity::repath() { CVector2D destination; @@ -855,11 +808,8 @@ void CEntity::render() glShadeModel( GL_FLAT ); glBegin( GL_LINE_STRIP ); - - glVertex3f( m_position.X, m_position.Y + 0.25f, m_position.Z ); - x = m_position.X; y = m_position.Z; @@ -1047,9 +997,8 @@ void CEntity::renderHealthBar() g_Camera.GetScreenCoordinates(above, sx, sy); float fraction = clamp(m_healthCurr / m_healthMax, 0.0f, 1.0f); - const float SIZE = 20; - float x1 = sx - SIZE/2; - float x2 = sx + SIZE/2; + float x1 = sx - m_healthBarSize/2; + float x2 = sx + m_healthBarSize/2; float y = g_yres - sy; glBegin(GL_LINES); @@ -1058,19 +1007,61 @@ void CEntity::renderHealthBar() glColor3f( 0, 1, 0 ); glVertex3f( x1, y, 0 ); glColor3f( 0, 1, 0 ); - glVertex3f( x1 + SIZE*fraction, y, 0 ); + glVertex3f( x1 + m_healthBarSize*fraction, y, 0 ); // red part of bar glColor3f( 1, 0, 0 ); - glVertex3f( x1 + SIZE*fraction, y, 0 ); + glVertex3f( x1 + m_healthBarSize*fraction, y, 0 ); glColor3f( 1, 0, 0 ); glVertex3f( x2, y, 0 ); glEnd(); } +void CEntity::renderStaminaBar() +{ + if( !m_bounds ) + return; + if( m_staminaBarHeight < 0 ) + return; // negative bar height means don't display health bar + CCamera *g_Camera=g_Game->GetView()->GetCamera(); + float sx, sy; + CVector3D above; + above.X = m_position.X; + above.Z = m_position.Z; + above.Y = getAnchorLevel(m_position.X, m_position.Z) + m_staminaBarHeight; + g_Camera->GetScreenCoordinates(above, sx, sy); + float fraction = clamp(m_staminaCurr / m_staminaMax, 0.0f, 1.0f); + + float x1 = sx - m_staminaBarSize/2; + float x2 = sx + m_staminaBarSize/2; + float y = g_yres - sy; + + glBegin(GL_LINES); + + // blue part of bar + glColor3f( 0, 0, 1 ); + glVertex3f( x1, y, 0 ); + glColor3f( 0, 0, 1 ); + glVertex3f( x1 + m_staminaBarSize*fraction, y, 0 ); + + // red part of bar + glColor3f( 1, 0, 0 ); + glVertex3f( x1 + m_staminaBarSize*fraction, y, 0 ); + glColor3f( 1, 0, 0 ); + glVertex3f( x2, y, 0 ); + + glEnd(); +} +void CEntity::CalculateRun(float timestep) +{ + if ( m_isRunning ) + m_staminaCurr = max( 0.0f, m_staminaCurr - timestep / 1000.0f / m_runDecayRate * m_staminaMax ); + else if ( m_orderQueue.empty() ) + m_staminaCurr = min( m_staminaMax, m_staminaCurr + timestep / 1000.0f / m_runRegenRate * m_staminaMax ); +} /* Scripting interface @@ -1092,6 +1083,11 @@ void CEntity::ScriptingInit() AddMethod( "addAura", 3 ); AddMethod( "removeAura", 1 ); AddMethod( "setActionParams", 5 ); + AddMethod( "checkListeners", 1 ); + AddMethod( "requestNotification", 3 ); + AddMethod( "triggerRun", 1 ); + AddMethod( "setRun", 1 ); + AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase ); AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet ); @@ -1549,3 +1545,90 @@ jsval CEntity::SetActionParams( JSContext* UNUSED(cx), uintN argc, jsval* argv ) return JSVAL_VOID; } + +jsval CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv ) +{ + if( argc < 3 ) + { + JS_ReportError( cx, "Too few parameters" ); + return( false ); + } + CEntityListener notify; + + notify.m_sender = this; + //(Convert from int to enum) + CEntity *target = ToNative( argv[0] ); + *( (uint*) &(notify.m_type) ) = ToPrimitive( argv[1] ); + + bool destroy = ToPrimitive( argv[2] ); + + std::deque::iterator it = target->m_listeners.begin(); + for ( ; it != target->m_listeners.end(); it++) + { + if ( destroy && it->m_sender == this ) + target->m_listeners.erase(it); + } + + target->m_listeners.push_back( notify ); + return JSVAL_VOID; +} +jsval CEntity::CheckListeners( JSContext *cx, uintN argc, jsval* argv ) +{ + if( argc < 1 ) + { + JS_ReportError( cx, "Too few parameters" ); + return( false ); + } + int type = ToPrimitive( argv[0] ); //notify code + CEntityOrder order = this->m_orderQueue.front(); + CEntity* target; + + for (int i=0; iDispatchNotification( order, type ); + break; + + case CEntityListener::NOTIFY_HEAL: + case CEntityListener::NOTIFY_ATTACK: + case CEntityListener::NOTIFY_GATHER: + case CEntityListener::NOTIFY_DAMAGE: + if( argc < 2 ) + { + JS_ReportError( cx, "Too few parameters" ); + continue; + } + target = ToNative( argv[1] ); + order.m_data[0].entity = target->me; + + m_listeners[i].m_sender->DispatchNotification( order, type ); + break; + + default: + JS_ReportError( cx, "Invalid order type" ); + continue; + } + } + return JSVAL_VOID; +} + +jsval CEntity::TriggerRun( JSContext* cx, uintN argc, jsval* argv ) +{ + m_triggerRun = true; + return JSVAL_VOID; +} +jsval CEntity::SetRun( JSContext* cx, uintN argc, jsval* argv ) +{ + if( argc < 1 ) + { + JS_ReportError( cx, "Too few parameters" ); + return( false ); + } + m_isRunning = ToPrimitive ( argv[0] ); + return JSVAL_VOID; +} + \ No newline at end of file diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 0fb16d1da0..16ae711282 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -72,8 +72,9 @@ public: float m_speed; float m_turningRadius; - float m_trampleStart; - + float m_runRegenRate; + float m_runDecayRate; + SEntityAction m_run; SEntityAction m_melee; SEntityAction m_gather; @@ -91,12 +92,23 @@ public: // If this unit is still active in the gameworld - i.e. not a corpse. bool m_extant; - bool m_isRunning; + bool m_isRunning; //is it actually running + bool m_shouldRun; //if run was issued, it will remain true until it is stopped + bool m_triggerRun; //used in SetRun, corrects 1 frame stamina imbalance + int m_frameCheck; //counts the frame + + + //SP properties + float m_staminaCurr; + float m_staminaMax; + float m_staminaBarHeight; + int m_staminaBarSize; // HP properties float m_healthCurr; float m_healthMax; float m_healthBarHeight; + int m_healthBarSize; // Minimap properties CStrW m_minimapType; @@ -237,9 +249,13 @@ public: void render(); void renderSelectionOutline( float alpha = 1.0f ); void renderHealthBar(); + void renderStaminaBar(); // After a collision, recalc the path to the next fixed waypoint. void repath(); + + //Calculate stamina points + void CalculateRun(float timestep); // Reset properties after the entity-template we use changes. void loadBase(); @@ -257,10 +273,8 @@ public: void clearOrders(); void pushOrder( CEntityOrder& order ); - jsval RequestNotification( JSContext* cx, uintN argc, jsval* argv ); void DispatchNotification( CEntityOrder order,uint type ); - jsval CheckListeners( JSContext* cx, uintN argc, jsval* argv ); - + // Script constructor static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ); @@ -277,6 +291,12 @@ public: jsval RemoveAura( JSContext* cx, uintN argc, jsval* argv ); jsval SetActionParams( JSContext* cx, uintN argc, jsval* argv ); + jsval TriggerRun( JSContext* cx, uintN argc, jsval* argv ); + jsval SetRun( JSContext* cx, uintN argc, jsval* argv ); + jsval IsRunning( JSContext* cx, uintN argc, jsval* argv ); + + jsval RequestNotification( JSContext* cx, uintN argc, jsval* argv ); + jsval CheckListeners( JSContext* cx, uintN argc, jsval* argv ); bool Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ); inline bool OrderSingle( JSContext* cx, uintN argc, jsval* argv ) diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index 9076b3668e..1f138d0043 100755 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -53,8 +53,8 @@ public: enum { NOTIFY_GOTO = 0x01, - //NOTIFY_ = 0x02, reservered for possible second goto like command - //NOTIFY_FOLLOW = 0x03, //GOTO | RUN + NOTIFY_RUN = 0x02, + NOTIFY_FOLLOW = 0x03, //GOTO | RUN NOTIFY_ATTACK = 0x04, NOTIFY_DAMAGE = 0x08, diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index d298b1b588..54d41423f6 100755 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -47,7 +47,10 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, // Here there be trig. float scale; - if ( m_run.m_Speed > 0 && len < m_run.m_MaxRange && ( len > m_run.m_MinRange || m_isRunning ) ) + + //Should we run or walk + if (m_shouldRun && m_staminaCurr > 0 && len < m_run.m_MaxRange && + ( len > m_run.m_MinRange || m_isRunning ) ) { scale = m_run.m_Speed * timestep; if ( m_actor ) @@ -57,7 +60,7 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); if ( 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 ); m_isRunning = true; @@ -66,28 +69,29 @@ uint CEntity::processGotoHelper( CEntityOrder* current, size_t timestep_millis, } else { - scale = m_speed * timestep; + scale = m_speed * timestep; if ( m_actor ) { + //Should we update animation? if ( !m_actor->IsPlayingAnimation( "walk" ) ) { 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 ); m_isRunning = false; - } - } + } + } } // Note: Easy optimization: flag somewhere that this unit - // is already pointing the right way, and don't do this - // trig every time. + // is already pointing the way, and don't do this + // trig every time.right m_targetorientation = atan2( delta.x, delta.y ); @@ -237,6 +241,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli { m_orderQueue.pop_front(); m_isRunning = false; + m_shouldRun = false; } return( false ); case COLLISION_OVERLAPPING_OBJECTS: @@ -245,6 +250,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli // We're here... m_orderQueue.pop_front(); m_isRunning = false; + m_shouldRun = false; return( false ); case COLLISION_NEAR_DESTINATION: @@ -309,6 +315,7 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli // Just stop here, repath if necessary. m_orderQueue.pop_front(); m_isRunning = false; + m_shouldRun = false; return( false ); default: @@ -329,31 +336,41 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timeste if( Distance < action->m_MaxRange ) { (int&)current->m_type = transition; + m_isRunning = false; m_orderQueue.push_front(*current); // Seems to be needed since we do a pop above return( true ); } - if( m_transition && m_actor ) + if ( m_actor ) { - if ( m_run.m_Speed > 0 && Distance < m_run.m_MaxRange && ( Distance > m_run.m_MinRange || m_isRunning ) ) + //Should we run or walk + if (m_shouldRun && m_staminaCurr > 0 && Distance < m_run.m_MaxRange && + ( Distance > m_run.m_MinRange || m_isRunning ) ) { - CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); - if ( 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 ); - m_isRunning = true; + if ( !m_actor->IsPlayingAnimation( "run" ) ) + { + CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); + if ( 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 ); + m_isRunning = true; + } } 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 ); - m_isRunning = false; + //Should we update animation? + if ( !m_actor->IsPlayingAnimation( "walk" ) ) + { + 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 ); + m_isRunning = false; + } } } @@ -419,6 +436,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times m_orderQueue.pop_front(); m_isRunning = false; + m_shouldRun = false; return( false ); } @@ -433,6 +451,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times { // Too close... do nothing. m_isRunning = false; + m_shouldRun = false; return( false ); } } @@ -445,28 +464,37 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times delta = delta.normalize() * ( adjRange - m_bounds->m_radius ); float deltaLength = delta.length(); - if( m_transition && m_actor ) + if ( m_actor ) { - if ( m_run.m_Speed > 0 && deltaLength < m_run.m_MaxRange && ( deltaLength > m_run.m_MinRange || m_isRunning ) ) + //Should we run or walk + if (m_shouldRun && m_staminaCurr > 0 && deltaLength < m_run.m_MaxRange && + ( deltaLength > m_run.m_MinRange || m_isRunning ) ) { - CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); - if ( 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 ); - m_isRunning = true; + if ( !m_actor->IsPlayingAnimation( "run" ) ) + { + CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); + if ( 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 ); + m_isRunning = true; + } } 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 ); - m_isRunning = false; - } + //Should we update animation? + if ( !m_actor->IsPlayingAnimation( "walk" ) ) + { + 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 ); + m_isRunning = false; + } + } } current->m_data[0].location = (CVector2D)current->m_data[0].entity->m_position - delta; @@ -639,29 +667,42 @@ bool CEntity::processGoto( CEntityOrder* current, size_t UNUSED(timestep_millis) // Let's just check we're going somewhere... if( Distance < 0.1f ) - return( false ); - - if( m_transition && m_actor ) { - if ( m_run.m_Speed > 0 && Distance < m_run.m_MaxRange && ( Distance > m_run.m_MinRange || m_isRunning ) ) + m_isRunning = false; + m_shouldRun = false; + return( false ); + } + + if ( m_actor ) + { + //Should we run or walk + if (m_shouldRun && m_staminaCurr > 0 && Distance < m_run.m_MaxRange && + ( Distance > m_run.m_MinRange || m_isRunning ) ) { - CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); - if ( 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 ); - m_isRunning = true; + if ( !m_actor->IsPlayingAnimation( "run" ) ) + { + CSkeletonAnim* run = m_actor->GetRandomAnimation( "run" ); + if ( 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 ); + m_isRunning = true; + } } 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 ); - m_isRunning = false; + //Should we update animation? + if ( !m_actor->IsPlayingAnimation( "walk" ) ) + { + 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 ); + m_isRunning = false; + } } } diff --git a/source/simulation/EventHandlers.cpp b/source/simulation/EventHandlers.cpp index 3ef28a654e..2eefb6fbdd 100755 --- a/source/simulation/EventHandlers.cpp +++ b/source/simulation/EventHandlers.cpp @@ -42,10 +42,19 @@ CEventTargetChanged::CEventTargetChanged( CEntity* target ) : CScriptEvent( L"ta m_defaultOrder = -1; m_defaultAction = 0; m_defaultCursor = L"arrow-default"; + + m_secondaryOrder = -1; + m_secondaryAction = 0; + m_secondaryCursor = L"arrow-default"; + + AddLocalProperty( L"target", &m_target, true ); AddLocalProperty( L"defaultOrder", &m_defaultOrder ); AddLocalProperty( L"defaultAction", &m_defaultAction ); AddLocalProperty( L"defaultCursor", &m_defaultCursor ); + AddLocalProperty( L"secondaryOrder", &m_secondaryOrder ); + AddLocalProperty( L"secondaryCursor", &m_secondaryCursor ); + AddLocalProperty( L"secondaryAction", &m_secondaryAction ); } CEventPrepareOrder::CEventPrepareOrder( CEntity* target, int orderType ) : CScriptEvent( L"prepareOrder", EVENT_PREPARE_ORDER, true ) diff --git a/source/simulation/EventHandlers.h b/source/simulation/EventHandlers.h index 1013b3af94..0875b2771f 100755 --- a/source/simulation/EventHandlers.h +++ b/source/simulation/EventHandlers.h @@ -67,6 +67,9 @@ public: int m_defaultOrder; int m_defaultAction; CStrW m_defaultCursor; + CStrW m_secondaryCursor; + int m_secondaryOrder; + int m_secondaryAction; CEventTargetChanged( CEntity* target ); }; diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp index 56e0e6631a..8ea78c3dfe 100755 --- a/source/simulation/Simulation.cpp +++ b/source/simulation/Simulation.cpp @@ -263,6 +263,9 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU case NMT_Goto: ENTITY_POSITION(CGoto, ORDER_GOTO); break; + case NMT_Run: + ENTITY_POSITION(CRun, ORDER_RUN); + break; case NMT_Patrol: ENTITY_POSITION(CPatrol, ORDER_PATROL); break;