//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/behaviors_general.cpp $ // $Revision:: 190 $ // $Author:: Steven $ // $Date:: 10/13/03 8:53a $ // // Copyright (C) 2001 by Ritual Entertainment, Inc. // All rights reserved. // // This source may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // #include "_pch_cpp.h" //#include "g_local.h" #include "behavior.h" #include "actor.h" #include "doors.h" #include "object.h" #include "explosion.h" #include "weaputils.h" #include "player.h" #include "behaviors_general.h" extern Container HelperNodes; extern Event EV_Contents; extern Event EV_HelperNodeCommand; //============================================================================== // Define //============================================================================== #define YAW_MAXIMUM 100.0f #define TURN_THRESHOLD_MAJOR 12.0f #define TURN_THRESHOLD_MINOR 3.0f #define CONE_OF_FIRE 14.0f #define MAJOR_YAW_CHANGE 10.0f #define MAJOR_PITCH_CHANGE 8.0f #define WORK_NODE_OCCUPIED_TIME 16.5f #define DUCK_DISTANCE 350.0f #define RUN_DISTANCE 90.0f #define POINT_BLANK 50.0f #define MIN_POSITION_NODE_DIST 50.0f #define MAX_POSITION_NODE_DIST 256.0f //============================================================================== // Helper Node Specific Defines //============================================================================== #define WORK_HELPER_NODE_STATE_TURN_TO_WORK 1 #define WORK_HELPER_NODE_STATE_START_ANIM 2 #define WORK_HELPER_NODE_STATE_PLAY_ANIM 3 #define WORK_HELPER_NODE_STATE_WAITING_FOR_ANIM 4 #define WORK_HELPER_NODE_STATE_FINISH 5 #define PATROL_HELPER_NODE_STATE_GET_NEXT_NODE 1 #define PATROL_HELPER_NODE_STATE_MOVING 2 #define PATROL_HELPER_NODE_STATE_AT_NODE 3 #define PATROL_HELPER_NODE_STATE_WAITING 4 #define PATROL_HELPER_NODE_STATE_WAITING_FOR_ANIM 5 #define PATROL_HELPER_NODE_STATE_FINISHED 6 //=================================================================================== // // Known Working Behaviors -- May need some clean up, but they do work // //=================================================================================== //============================================================================== // WarpToPosition //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, WarpToPosition, NULL ) { { &EV_Behavior_Args, &WarpToPosition::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: WarpToPosition // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void WarpToPosition::SetArgs ( Event *ev) { _position = ev->GetVector( 1 ); } //-------------------------------------------------------------- // Name: Begin() // Class: WarpToPosition // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToPosition::Begin( Actor & ) { _state = WARP_TO_POSITION_CHECK_POSITION; } //-------------------------------------------------------------- // Name: Evaluate() // Class: WarpToPosition // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t WarpToPosition::Evaluate ( Actor &self ) { switch ( _state ) { case WARP_TO_POSITION_CHECK_POSITION: checkPosition( self ); break; case WARP_TO_POSITION_WARP: warpToPosition( self ); break; case WARP_TO_POSITION_FAILED: return BEHAVIOR_FAILED; break; case WARP_TO_POSITION_SUCCESS: return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: WarpToPosition // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToPosition::End ( Actor & ) { } //-------------------------------------------------------------- // Name: checkPosition() // Class: WarpToPosition // // Description: Checks if the actor will fit in the position requested // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToPosition::checkPosition( Actor &self ) { trace_t trace; trace = G_Trace( _position + Vector( "0 0 64" ), self.mins, self.maxs, _position - Vector( "0 0 128" ), &self, self.edict->clipmask, false, "WarpToPosition" ); _position = trace.endpos; if ( trace.allsolid ) checkPositionFailed( self ); else _state = WARP_TO_POSITION_WARP; } //-------------------------------------------------------------- // Name: checkPositionFailed // Class: WarpToPosition // // Description: Failure Handler for CheckPosition -- Sets our animation // to idle and sets our state to FAILED // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToPosition::checkPositionFailed( Actor &self ) { self.SetAnim( "idle" ); _state = WARP_TO_POSITION_FAILED; } //-------------------------------------------------------------- // Name: warpToPosition() // Class: WarpToPosition // // Description: Sets our Origin to the specified location // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToPosition::warpToPosition( Actor &self ) { self.setOrigin( _position ); self.NoLerpThisFrame(); _state = WARP_TO_POSITION_SUCCESS; } //============================================================================== // WarpToEntity //============================================================================== //-------------------------------------------------------------- // // Init Static Vars // //-------------------------------------------------------------- const float WarpToEntity::DIST_TO_ENTITY = 64.0f; //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, WarpToEntity, NULL ) { { &EV_Behavior_Args, &WarpToEntity::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: WarpToEntity // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void WarpToEntity::SetArgs ( Event *ev) { _entity = ev->GetEntity( 1 ); } //-------------------------------------------------------------- // Name: Begin() // Class: WarpToEntity // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToEntity::Begin( Actor & ) { _state = WARP_TO_ENTITY_SELECT_POSITION; _position = POSITION_REAR; } //-------------------------------------------------------------- // Name: Evaluate() // Class: WarpToEntity // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t WarpToEntity::Evaluate ( Actor &self ) { switch ( _state ) { case WARP_TO_ENTITY_SELECT_POSITION: selectPosition( self ); break; case WARP_TO_ENTITY_WARP: warpToPosition( self ); break; case WARP_TO_ENTITY_FAILED: return BEHAVIOR_FAILED; break; case WARP_TO_ENTITY_SUCCESS: return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: WarpToEntity // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToEntity::End ( Actor & ) { } //-------------------------------------------------------------- // Name: selectPosition() // Class: WarpToEntity // // Description: Checks what position we should try and gets a vector // to pass to the WarpToPosition Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToEntity::selectPosition( Actor &self ) { Vector entAngles; Vector f,l,u; entAngles = _entity->angles; entAngles.AngleVectors( &f, &l, &u ); switch ( _position ) { case POSITION_REAR: _destination = ( f * -1 ) * 64.0f; break; case POSITION_LEFT: _destination = l * 64.0f; break; case POSITION_RIGHT: _destination = ( l * -1 ) * 64.0f; break; case POSITION_FRONT: _destination = f * 64.0f; break; } _destination += _entity->origin; setupWarp( self ); _state = WARP_TO_ENTITY_WARP; } //-------------------------------------------------------------- // Name: setupWarp() // Class: WarpToEntity // // Description: Sets up our Warp Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToEntity::setupWarp( Actor &self ) { _warp.SetPosition( _destination ); _warp.Begin( self ); } //-------------------------------------------------------------- // Name: warpToPosition() // Class: WarpToEntity // // Description: Sets our Origin to the specified location // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToEntity::warpToPosition( Actor &self ) { BehaviorReturnCode_t result; result = _warp.Evaluate( self ); if ( result == BEHAVIOR_SUCCESS ) { _state = WARP_TO_ENTITY_SUCCESS; return; } if ( result != BEHAVIOR_EVALUATING ) { warpToPositionFailed( self ); return; } } //-------------------------------------------------------------- // Name: warpToPositionFailed // Class: WarpToEntity // // Description: Failure Handler for warpToPosition -- Increments // us to our next position to check, or fails // us out right if we've exhausted all our possibilities // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void WarpToEntity::warpToPositionFailed( Actor & ) { _position++; //See if we checked all of our options if ( _position >= POSITION_NUMBER_OF_POSITIONS ) { _state = WARP_TO_ENTITY_FAILED; return; } _state = WARP_TO_ENTITY_SELECT_POSITION; } // //============================================================================== // GotoEntity //============================================================================== // //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, GotoEntity, NULL ) { { &EV_Behavior_Args, &GotoEntity::SetArgs }, { NULL, NULL } }; GotoEntity::GotoEntity() { _dist = 96.0f; } //-------------------------------------------------------------- // // Name: SetArgs() // Class: GotoEntity // // Description: // // Parameters: Event *ev -- Event containing the string // // Returns: None // //-------------------------------------------------------------- void GotoEntity::SetArgs( Event *ev ) { // Set some default values here _dist = 96.0f; // We have to have an anim _anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) _dist = ev->GetFloat( 2 ); } //-------------------------------------------------------------- // // Name: Begin() // Class: GotoEntity // // Description: Initializes the behavior // // Parameters: Actor &self -- The actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void GotoEntity::Begin( Actor &self ) { if ( _entity ) { _chase.SetGoal( _entity, _dist, self ); _chase.Begin( self ); } self.SetAnim( _anim ); } //-------------------------------------------------------------- // // Name: Evaluate() // Class: GotoEntity // // Description: Update for this behavior -- called every server frame // // Parameters: Actor &self -- Actor executing this behavior // // Returns: BehaviorReturnCode_t // //-------------------------------------------------------------- BehaviorReturnCode_t GotoEntity::Evaluate( Actor &self ) { unsigned int chaseResult; if ( !_entity ) return BEHAVIOR_FAILED; //self.SetAnim( _anim ); chaseResult = _chase.Evaluate( self ); // Return the appropriate code switch ( chaseResult ) { case Steering::SUCCESS: return BEHAVIOR_SUCCESS; break; case Steering::EVALUATING: return BEHAVIOR_EVALUATING; break; case Steering::FAILED: self.SetAnim( "idle" ); SetFailureReason( "Steering returned FAILED" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED; break; case Steering::FAILED_BLOCKED_BY_ENEMY: self.SetAnim( "idle" ); SetFailureReason( "Steering returned BLOCKED_BY_ENEMY" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY; break; case Steering::FAILED_BLOCKED_BY_CIVILIAN: self.SetAnim( "idle" ); SetFailureReason( "Steering returned BLOCKED_BY_CIVILIAN" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN; break; case Steering::FAILED_BLOCKED_BY_FRIEND: self.SetAnim( "idle" ); SetFailureReason( "Steering returned BLOCKED_BY_FRIEND" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND; break; case Steering::FAILED_BLOCKED_BY_TEAMMATE: self.SetAnim( "idle" ); SetFailureReason( "Steering returned BLOCKED_BY_TEAMMATE" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE; break; case Steering::FAILED_BLOCKED_BY_WORLD: self.SetAnim( "idle" ); SetFailureReason( "Steering returned BLOCKED_BY_WORLD" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD; break; case Steering::FAILED_BLOCKED_BY_DOOR: self.SetAnim( "idle" ); SetFailureReason( "Steering returned BLOCKED_BY_DOOR" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR; break; case Steering::FAILED_CANNOT_GET_TO_PATH: self.AddStateFlag( StateFlagNoPath ); self.SetAnim( "idle" ); SetFailureReason( "Steering returned CANNOT_GET_TO_PATH" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH; break; case Steering::FAILED_NO_PATH: self.AddStateFlag( StateFlagNoPath ); /* if ( !self.GetActorFlag(ACTOR_FLAG_DISPLAYING_FAILURE_FX) ) { Event* event; event = new Event( EV_DisplayEffect ); event->AddString( "electric" ); self.ProcessEvent( event ); self.SetActorFlag( ACTOR_FLAG_DISPLAYING_FAILURE_FX , true ); } */ self.SetAnim( "idle" ); SetFailureReason( "Steering returned NO_PATH" ); self.AddStateFlag( StateFlagSteeringFailed ); return BEHAVIOR_FAILED_STEERING_NO_PATH; break; case Steering::ERROR: self.SetAnim( "idle" ); gi.Error( ERR_DROP, "Steering Error"); break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // // Name: End() // Class: GotoEntity // // Description: Ends this behavior -- cleans things up // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void GotoEntity::End(Actor &self) { _chase.End( self ); } // //============================================================================== // GotoPoint //============================================================================== // //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, GotoPoint, NULL ) { { &EV_Behavior_Args, &GotoPoint::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // // Name: SetArgs() // Class: GotoPoint // // Description: // // Parameters: Event *ev -- Event containing the string // // Returns: None // //-------------------------------------------------------------- void GotoPoint::SetArgs( Event *ev ) { // Set some default values here dist = 96.0f; // We have to have an anim anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) dist = ev->GetFloat( 2 ); } //-------------------------------------------------------------- // // Name: Begin() // Class: GotoPoint // // Description: Initializes the behavior // // Parameters: Actor &self -- The actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void GotoPoint::Begin( Actor &self ) { _chaseFailed = false; _chase.SetGoal( point, dist, self ); unsigned int testResult; testResult = _chase.Evaluate( self ); if ( testResult == Steering::EVALUATING ) self.SetAnim( anim ); else { self.SetAnim ( "idle" ); _chaseFailed = true; } } //-------------------------------------------------------------- // // Name: Evaluate() // Class: GotoPoint // // Description: Update for this behavior -- called every server frame // // Parameters: Actor &self -- Actor executing this behavior // // Returns: BehaviorReturnCode_t // //-------------------------------------------------------------- BehaviorReturnCode_t GotoPoint::Evaluate( Actor &self ) { unsigned int chaseResult; //E3 2002 HACK LOVIN' if ( self.state_flags & StateFlagStuck ) { self.SetAnim( "idle" ); SetFailureReason( "I'm stuck!!!!!!" ); return BEHAVIOR_FAILED; } chaseResult = _chase.Evaluate( self ); // Return the appropriate code switch ( chaseResult ) { case Steering::SUCCESS: return BEHAVIOR_SUCCESS; break; case Steering::EVALUATING: if ( _chaseFailed ) { self.SetAnim( anim ); _chaseFailed = false; } return BEHAVIOR_EVALUATING; break; case Steering::FAILED: self.SetAnim( "idle" ); return BEHAVIOR_FAILED; break; case Steering::FAILED_BLOCKED_BY_ENEMY: self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY; break; case Steering::FAILED_BLOCKED_BY_CIVILIAN: self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN; break; case Steering::FAILED_BLOCKED_BY_FRIEND: self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND; break; case Steering::FAILED_BLOCKED_BY_TEAMMATE: self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE; break; case Steering::FAILED_BLOCKED_BY_WORLD: self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD; break; case Steering::FAILED_BLOCKED_BY_DOOR: self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR; break; case Steering::FAILED_CANNOT_GET_TO_PATH: self.AddStateFlag( StateFlagNoPath ); self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH; break; case Steering::FAILED_NO_PATH: self.AddStateFlag( StateFlagNoPath ); self.SetAnim( "idle" ); return BEHAVIOR_FAILED_STEERING_NO_PATH; break; case Steering::ERROR: self.SetAnim( "idle" ); gi.Error( ERR_DROP, "Steering Error"); break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // // Name: End() // Class: GotoPoint // // Description: Ends this behavior -- cleans things up // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void GotoPoint::End(Actor &self) { _chase.End( self ); } //-------------------------------------------------------------- // Name: SetEntity() // Class: GotoPoint // // Description: Mutator // // Parameters: Entity *ent // // Returns: None //-------------------------------------------------------------- void GotoPoint::SetPoint( const Vector &position ) { point = position; } //-------------------------------------------------------------- // Name: SetAnim() // Class: GotoPoint // // Description: Mutator // // Parameters: const str &animName // // Returns: None //-------------------------------------------------------------- void GotoPoint::SetAnim( const str &animName ) { anim = animName; } //-------------------------------------------------------------- // Name: SetDistance() // Class: GotoPoint // // Description: Mutator // // Parameters: float distance // // Returns: None //-------------------------------------------------------------- void GotoPoint::SetDistance( float distance ) { dist = distance; } // //============================================================================== // MoveDirectlyToPoint //============================================================================== // //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, MoveDirectlyToPoint, NULL ) { { &EV_Behavior_Args, &MoveDirectlyToPoint::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // // Name: SetArgs() // Class: MoveDirectlyToPoint // // Description: // // Parameters: Event *ev -- Event containing the string // // Returns: None // //-------------------------------------------------------------- void MoveDirectlyToPoint::SetArgs( Event *ev ) { // Set some default values here _dist = 96.0f; // We have to have an anim _anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) _dist = ev->GetFloat( 2 ); } //-------------------------------------------------------------- // // Name: Begin() // Class: MoveDirectlyToPoint // // Description: Initializes the behavior // // Parameters: Actor &self -- The actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void MoveDirectlyToPoint::Begin( Actor & ) { _dist = 16.0f; _motion.SetRadius( _dist ); } //-------------------------------------------------------------- // // Name: Evaluate() // Class: MoveDirectlyToPoint // // Description: Update for this behavior -- called every server frame // // Parameters: Actor &self -- Actor executing this behavior // // Returns: BehaviorReturnCode_t // //-------------------------------------------------------------- BehaviorReturnCode_t MoveDirectlyToPoint::Evaluate( Actor &self ) { self.SetAnim( _anim ); unsigned int motionResult = _motion.Evaluate( self ); // Return the appropriate code switch ( motionResult ) { case Steering::SUCCESS: return BEHAVIOR_SUCCESS; break; case Steering::EVALUATING: return BEHAVIOR_EVALUATING; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // // Name: End() // Class: MoveDirectlyToPoint // // Description: Ends this behavior -- cleans things up // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void MoveDirectlyToPoint::End( Actor &self ) { _motion.End( self ); } //-------------------------------------------------------------- // Name: SetAnim() // Class: MoveDirectlyToPoint // // Description: Mutator // // Parameters: const str &animName // // Returns: None //-------------------------------------------------------------- void MoveDirectlyToPoint::SetAnim( const str &animName ) { _anim = animName; } //-------------------------------------------------------------- // Name: SetEntity() // Class: MoveDirectlyToPoint // // Description: Mutator // // Parameters: Entity *ent // // Returns: None //-------------------------------------------------------------- void MoveDirectlyToPoint::SetPoint( const Vector &position ) { _motion.SetDestination( position ); } //-------------------------------------------------------------- // Name: SetDistance() // Class: MoveDirectlyToPoint // // Description: Mutator // // Parameters: float distance // // Returns: None //-------------------------------------------------------------- void MoveDirectlyToPoint::SetDistance( const float distance ) { _dist = distance; } //============================================================================== // GotoSpecified //============================================================================== //-------------------------------------------------------------- // // Init Static Vars // //-------------------------------------------------------------- const float GotoSpecified::DIST_TO_TARGET_POSITION = 16.0f; const float GotoSpecified::DIST_TO_TARGET_ENTITY = 64.0f; //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, GotoSpecified, NULL ) { { &EV_Behavior_Args, &GotoSpecified::SetArgs }, { &EV_Behavior_AnimDone, &GotoSpecified::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: GotoSpecified // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void GotoSpecified::SetArgs ( Event *ev) { TargetList *tlist; PathNode *node; str parm; _state = GOTO_SPEC_FAILED; _turnAtEnd = false ; _maxFailures = 5; // Get our Animation _anim = ev->GetString( 1 ); // Check if we were given a specific position to go to if ( ev->IsVectorAt( 2 ) ) { _targetPosition = ev->GetVector( 2 ); _state = GOTO_SPEC_CHASE_TARGET; _mode = GOTO_SPEC_CHASE_POSITION; } // Check if we were given an entity to go to else if ( ev->IsEntityAt( 2 ) ) { _targetEntity = ev->GetEntity( 2 ); _state = GOTO_SPEC_CHASE_TARGET; _mode = GOTO_SPEC_CHASE_ENTITY; } else { // First see if we were given the target name of a pathnode parm = ev->GetString( 2 ); node = thePathManager.FindNode( parm.c_str() ); if ( node ) { _targetPosition = node->origin; _endAngles = node->angles; _state = GOTO_SPEC_CHASE_TARGET; _mode = GOTO_SPEC_CHASE_POSITION; _turnAtEnd = true ; } // Now see if we were given the target name of another entity else { tlist = world->GetTargetList( parm ); if (tlist->list.NumObjects() > 0 ) { _targetEntity = tlist->list.ObjectAt( 1 ); _state = GOTO_SPEC_CHASE_TARGET; _mode = GOTO_SPEC_CHASE_ENTITY; } else { HelperNode *helperNode; // Try helper nodes helperNode = HelperNode::GetTargetedHelperNode( parm ); if ( helperNode ) { _targetPosition = helperNode->origin; _endAngles = helperNode->angles; _state = GOTO_SPEC_CHASE_TARGET; _mode = GOTO_SPEC_CHASE_POSITION; _turnAtEnd = true; } } } } // See if we have any kind of headwatch target if ( ev->NumArgs() > 2 ) { if ( ev->IsEntityAt( 3 ) ) _headwatchTarget = ev->GetEntity( 3 ); else { parm = ev->GetString( 3 ); // We have a headwatch target, but we need to make sure its not set to "none" // that might occur if we're going to do a forceToTarget, but we don't want to // headwatch anything if ( stricmp( parm.c_str() , "none" ) ) { tlist = world->GetTargetList( parm ); if (tlist->list.NumObjects() > 0 ) { _headwatchTarget = tlist->list.ObjectAt( 1 ); } } } } // See if we're going to try and force ourselves to the target if ( ev->NumArgs() > 3 ) _forceToTarget = ev->GetBoolean( 4 ); else _forceToTarget = false; if ( ev->NumArgs() > 4 ) _maxFailures = ev->GetInteger( 5 ); // Now, let's assert if our _state is still FAILED. That basically means we didn't find // anything to try and goto so we need to throw up a flag assert ( _state != GOTO_SPEC_FAILED ); } //-------------------------------------------------------------- // Name: AnimDone() // Class: GotoSpecified // // Description: Catches the Anim Done event // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void GotoSpecified::AnimDone ( Event * ) { } //-------------------------------------------------------------- // Name: Begin() // Class: GotoSpecified // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::Begin( Actor &self ) { if ( _mode == GOTO_SPEC_CHASE_POSITION ) setupChasePosition( self ); else if ( _mode == GOTO_SPEC_CHASE_ENTITY ) setupChaseEntity( self ); _moveFailures = 0; _holdTime = 0.0f; if ( _headwatchTarget ) self.SetHeadWatchTarget( _headwatchTarget ); _attemptedPathWarp = false; // Setup our TorsoAnim if we need to str torsoAnim; self.ClearTorsoAnim(); if ( self.combatSubsystem->HaveWeapon() ) { if ( self.enemyManager->HasEnemy() ) torsoAnim = self.combatSubsystem->GetAnimForMyWeapon( "CombatGunIdle" ); else torsoAnim = self.combatSubsystem->GetAnimForMyWeapon( "IdleGunIdle" ); } if ( torsoAnim.length() ) { self.ClearTorsoAnim(); self.SetAnim( torsoAnim, NULL , torso ); } } //-------------------------------------------------------------- // Name: Evaluate() // Class: GotoSpecified // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t GotoSpecified::Evaluate ( Actor &self ) { switch ( _state ) { case GOTO_SPEC_CHASE_TARGET: if ( _mode == GOTO_SPEC_CHASE_POSITION ) chasePosition( self ); else chaseEntity( self ); break; case GOTO_SPEC_HOLD: hold( self ); break; case GOTO_SPEC_WARP_TO_PATH: warpToNearestPathNode( self ); break; case GOTO_SPEC_WARP_TO_DESTINATION: warpToDestination( self ); break; case GOTO_SPEC_SUCCESS: if ( _turnAtEnd ) { setAngles( self ); } self.SetAnim( "idle" ); return BEHAVIOR_SUCCESS; break; case GOTO_SPEC_FAILED: self.SetAnim( "idle" ); return BEHAVIOR_FAILED; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: GotoSpecified // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::End ( Actor & ) { } //-------------------------------------------------------------- // Name: setupChaseEntity() // Class: GotoSpecified // // Description: Sets up the ChaseEntity Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::setupChaseEntity( Actor &self ) { _chaseEntity.SetAnim( _anim ); _chaseEntity.SetDistance( DIST_TO_TARGET_ENTITY ); _chaseEntity.SetEntity( self, _targetEntity ); _chaseEntity.Begin( self ); } //-------------------------------------------------------------- // Name: setupChasePosition() // Class: GotoSpecified // // Description: Sets up the ChasePosition Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::setupChasePosition( Actor &self ) { _chasePosition.SetAnim( _anim ); _chasePosition.SetDistance( DIST_TO_TARGET_POSITION ); _chasePosition.SetPoint( _targetPosition ); _chasePosition.Begin( self ); } //-------------------------------------------------------------- // Name: setupWarpToPathNode() // Class: GotoSpecified // // Description: Sets up our WarpToDestination Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::setupWarpToPathNode( Actor &self ) { // Find the path node nearest to us PathNode *goalNode = thePathManager.NearestNode( _targetPosition ); if ( !goalNode ) { _state = GOTO_SPEC_WARP_TO_DESTINATION; setupWarpToDestination( self ); } _warpToPosition.SetPosition( goalNode->origin ); _warpToPosition.Begin( self ); } //-------------------------------------------------------------- // Name: setupWarpToDestination() // Class: GotoSpecified // // Description: Based on our _mode, will set up our WarpToEntity // or WarpToPosition Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::setupWarpToDestination( Actor &self ) { if ( _mode == GOTO_SPEC_CHASE_POSITION ) { _warpToPosition.SetPosition( _targetPosition ); _warpToPosition.Begin( self ); return; } else { _warpToEntity.SetEntity( _targetEntity ); _warpToPosition.Begin( self ); return; } } //-------------------------------------------------------------- // Name: setupHold() // Class: GotoSpecified // // Description: Sets actor to the idle animation and sets up the hold time // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::setupHold( Actor &self ) { self.SetAnim( "idle" ); _holdTime = level.time + G_Random() + .5f; } //-------------------------------------------------------------- // Name: chaseEntity() // Class: GotoSpecified // // Description: Evaluates the ChaseEntity component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::chaseEntity( Actor &self ) { BehaviorReturnCode_t result; result = _chaseEntity.Evaluate( self ); switch ( result ) { case BEHAVIOR_SUCCESS: _state = GOTO_SPEC_SUCCESS; return; break; case BEHAVIOR_FAILED: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED" ); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE: chaseFailed( self ); SetFailureReason( "_chaseEntity returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR"); return; break; case BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH"); return; break; case BEHAVIOR_FAILED_STEERING_NO_PATH: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_FAILED_STEERING_NO_PATH"); return; break; case BEHAVIOR_INVALID: chaseFailed( self ); SetFailureReason("_chaseEntity returned BEHAVIOR_INVALID"); return; break; } // We are still evaluating, which means we can clear out our failure counter _moveFailures = 0; } //-------------------------------------------------------------- // Name: chasePosition() // Class: GotoSpecified // // Description: Evaluates our ChasePosition component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::chasePosition( Actor &self ) { BehaviorReturnCode_t result; result = _chasePosition.Evaluate( self ); switch ( result ) { case BEHAVIOR_SUCCESS: _state = GOTO_SPEC_SUCCESS; return; break; case BEHAVIOR_FAILED: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED" ); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_ENEMY"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_CIVILIAN"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_FRIEND"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE: chaseFailed( self ); SetFailureReason( "_chasePosition returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_TEAMMATE"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_WORLD"); return; break; case BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_BLOCKED_BY_DOOR"); return; break; case BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_CANNOT_GET_TO_PATH"); return; break; case BEHAVIOR_FAILED_STEERING_NO_PATH: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_FAILED_STEERING_NO_PATH"); return; break; case BEHAVIOR_INVALID: chaseFailed( self ); SetFailureReason("_chasePosition returned BEHAVIOR_INVALID"); return; break; } // We are still evaluating, which means we can clear out our failure counter _moveFailures = 0; } //-------------------------------------------------------------- // Name: warpToNearestPathNode() // Class: GotoSpecified // // Description: Evaluates the WarpToPosition Component in an attempt // to warp us to a nearby pathnode // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::warpToNearestPathNode( Actor &self ) { BehaviorReturnCode_t result; result = _warpToPosition.Evaluate( self ); _attemptedPathWarp = true; _moveFailures = 0; if ( result != BEHAVIOR_EVALUATING ) { str tname; tname = self.TargetName(); gi.WDPrintf( "=============================================================\n" ); gi.WDPrintf( "Actor %s is failing to reach destination\n" , tname.c_str() ); gi.WDPrintf( "=============================================================\n" ); gi.WDPrintf( "Reported Reason\n" ); gi.WDPrintf( "%s\n" , GetFailureReason().c_str() ); gi.WDPrintf( "=============================================================\n" ); _state = GOTO_SPEC_CHASE_TARGET; if ( _mode == GOTO_SPEC_CHASE_POSITION ) { setupChasePosition( self ); } else { setupChaseEntity( self ); } } //_state = GOTO_SPEC_CHASE_TARGET; } //-------------------------------------------------------------- // Name: warpToDestination() // Class: GotoSpecified // // Description: Depending on our _mode, will evaluate either // the warpToEntity or warpToPosition component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::warpToDestination( Actor &self ) { BehaviorReturnCode_t result; result = BEHAVIOR_EVALUATING; if ( _mode == GOTO_SPEC_CHASE_POSITION ) result = _warpToPosition.Evaluate( self ); if ( _mode == GOTO_SPEC_CHASE_ENTITY ) result = _warpToEntity.Evaluate( self ); if ( result == BEHAVIOR_SUCCESS ) { _state = GOTO_SPEC_SUCCESS; return; } if ( result != BEHAVIOR_EVALUATING ) { _state = GOTO_SPEC_FAILED; return; } } //-------------------------------------------------------------- // Name: hold() // Class: GotoSpecified // // Description: Holds actor in position until the hold time has expired // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::hold( Actor &self ) { if ( level.time >= _holdTime ) { if ( _mode == GOTO_SPEC_CHASE_POSITION ) { setupChasePosition( self ); _state = GOTO_SPEC_CHASE_TARGET ; } else { setupChaseEntity( self ); _state = GOTO_SPEC_CHASE_TARGET ; } } } //-------------------------------------------------------------- // Name: setAngles() // Class: GotoSpecified // // Description: Sets the angles and animdir of the actor to _endAngles // _endAngles will be the angles of a pathnode -- if // that is the destionation -- otherwise it will be 0 , 0 , 0 // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::setAngles( Actor &self ) { Vector angles; Vector animDir; _endAngles.AngleVectors( &animDir ); if ( _mode == GOTO_SPEC_CHASE_POSITION ) { self.setAngles( _endAngles ); self.movementSubsystem->setAnimDir( animDir ); } } //-------------------------------------------------------------- // Name: chaseFailed() // Class: GotoSpecified // // Description: Failure Handler for the chase type components // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoSpecified::chaseFailed( Actor &self ) { str tname; tname = self.TargetName(); if ( _mode == GOTO_SPEC_CHASE_POSITION ) _chasePosition.End( self ); else _chaseEntity.End( self ); // // Give everybody 5 chances before bombing out // if ( _maxFailures > 0 && _moveFailures > _maxFailures ) { if ( !_forceToTarget ) { gi.WDPrintf( "=============================================================\n" ); gi.WDPrintf( "Actor %s is failing to reach destination\n" , tname.c_str() ); gi.WDPrintf( "=============================================================\n" ); gi.WDPrintf( "Reported Reason\n" ); gi.WDPrintf( "%s\n" , GetFailureReason().c_str() ); gi.WDPrintf( "=============================================================\n" ); _state = GOTO_SPEC_FAILED; return; } setupWarpToPathNode( self ); _state = GOTO_SPEC_WARP_TO_PATH; return; } _moveFailures++; setupHold( self ); _state = GOTO_SPEC_HOLD; return; } //============================================================================== // MoveFromConeOfFire //============================================================================== //-------------------------------------------------------------- // // Init Static Vars // //-------------------------------------------------------------- const float MoveFromConeOfFire::CONE_OF_FIRE_RADIUS = 500.0f; //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, MoveFromConeOfFire, NULL ) { { &EV_Behavior_Args, &MoveFromConeOfFire::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: MoveFromConeOfFire // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::SetArgs ( Event *ev) { if ( ev->NumArgs() > 0 ) _anim = ev->GetString( 1 ); } //-------------------------------------------------------------- // Name: Begin() // Class: MoveFromConeOfFire // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::Begin( Actor &self ) { Vector dir; movegoal = NULL; dir = self.movementSubsystem->getAnimDir(); dir = dir.toAngles(); self.setAngles( dir ); _nextsearch = 0.0f; _torsoAnim = ""; _state = MOVE_FCOF_SEARCHING_FOR_SPOT; _stuckOnPlayer = false; _nextToObstacle = false; _chase.SetAnim( _anim ); _chase.SetDistance( 16.0f ); _oldTurnSpeed = self.movementSubsystem->getTurnSpeed(); self.movementSubsystem->setTurnSpeed( 360.0f ); } //-------------------------------------------------------------- // Name: Evaluate() // Class: MoveFromConeOfFire // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t MoveFromConeOfFire::Evaluate ( Actor &self ) { if ( _stuckOnPlayer ) return BEHAVIOR_FAILED; /* if ( g_showactortrace ) { G_DebugLine( self.origin , _left, 1.0f, 0.0f, 1.0f, 1.0f ); G_DebugLine( self.origin , _right, 1.0f, 0.0f, 1.0f, 1.0f ); G_DebugLine( self.origin , _destination , 1.0f ,1.0f , 1.0f, 1.0f ); } */ switch ( _state ) { case MOVE_FCOF_SEARCHING_FOR_SPOT: _setDirectionVectors(self); break; case MOVE_FCOF_STATE_FOUND_SPOT: _foundDestination( self ); break; case MOVE_FCOF_STATE_NO_SPOT: _noDestination( self ); break; case MOVE_FCOF_STATE_SEARCHING_FOR_NODE: _searchForNode( self ); break; case MOVE_FCOF_STATE_FOUND_NODE : _foundDestination( self ); break; case MOVE_FCOF_SUCCESS: return BEHAVIOR_SUCCESS; break; case MOVE_FCOF_FAILED: return BEHAVIOR_FAILED; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: MoveFromConeOfFire // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::End ( Actor &self ) { _chase.End( self ); self.movementSubsystem->setTurnSpeed( _oldTurnSpeed ); self.movementSubsystem->setMovingBackwards( false ); self.movementSubsystem->setAdjustAnimDir( true ); } //-------------------------------------------------------------- // Name: _setDirectionVectors() // Class: MoveFromConeOfFire // // Description: Determines where we are going to go // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::_setDirectionVectors(Actor &self) { float dot; Vector selfToPlayer; Vector playerAngles; Player *player; player = GetPlayer( 0 ); assert( player ); // Get our Direction Vectors set up selfToPlayer = self.origin - player->origin; playerAngles = player->GetVAngles(); playerAngles[PITCH] = 0.0f; playerAngles[ROLL] = 0.0f; playerAngles.AngleVectors( NULL , &_left , NULL ); assert( fSmallEnough( _left.z, fEpsilon()) ); const Vector startPos = self.origin; //startPos.z += 32; // Check if we're left or right of the player dot = DotProduct( selfToPlayer, _left ); _right = _left * -1; _right *= 75.0f; _right += startPos; _left *= 75.0f; _left += startPos; _right.z = startPos.z; _left.z = startPos.z; if ( dot <= 0 ) { // Go Right First if ( !_checkDesiredMovement( self , startPos , _right ) ) { gi.WDPrintf( "=============================\n" ); gi.WDPrintf( "Right Failed, trying Left\n" ); gi.WDPrintf( "Anim = %s\n" , _anim.c_str() ); gi.WDPrintf( "=============================\n" ); if ( _nextToObstacle ) { _state = MOVE_FCOF_FAILED; return; } //Well, no luck there, let's try left //if ( !_checkDesiredMovement( self , startPos , _left ) ) // { // _state = MOVE_FCOF_FAILED; // return; // } _state = MOVE_FCOF_FAILED; return; } } else { // Go Left First if ( !_checkDesiredMovement( self , startPos , _left ) ) { gi.WDPrintf( "=============================\n" ); gi.WDPrintf( "Left Failed, trying Right\n" ); gi.WDPrintf( "Anim = %s\n" , _anim.c_str() ); gi.WDPrintf( "=============================\n" ); if ( _nextToObstacle ) { _state = MOVE_FCOF_FAILED; return; } //Well, no luck there, let's try left //if ( !_checkDesiredMovement( self , startPos , _right ) ) // { // _state = MOVE_FCOF_FAILED; // return; // } _state = MOVE_FCOF_FAILED; return; } } _chase.Begin( self ); } //-------------------------------------------------------------- // Name: _checkDesiredMovement() // Class: MoveFromConeOfFire // // Description: Checks if the desired movement is possibe // // Parameters: Actor &self // const Vector &startPos, // const Vector &endPos // // Returns: true or false //-------------------------------------------------------------- bool MoveFromConeOfFire::_checkDesiredMovement( Actor &self , const Vector &startPos , const Vector &endPos ) { trace_t trace; trace = G_Trace( startPos, self.mins, self.maxs, endPos, &self, self.edict->clipmask, false, "MoveFromConeOfFire: Right Direction Test" ); //G_DebugLine( startPos, endPos, 1.0f, 0.0f, 1.0f, 1.0f ); // See if we're stuck on the player if ( trace.entityNum == 0 ) _stuckOnPlayer = true; // See if we got far enough if ( trace.fraction < .35 ) return false; if ( trace.fraction < .20 ) { _nextToObstacle = true; return false; } // Update our Search Time _nextsearch = level.time + 0.25f; _destination = trace.endpos; //New stuff to fix bumping the wall Vector selfToDestination; selfToDestination = trace.endpos - startPos; selfToDestination *= .80f; _destination = selfToDestination + startPos; Vector selfToDestinationAngles = _destination - self.origin; Vector animAngles = self.movementSubsystem->getAnimDir(); float yawDiff; selfToDestinationAngles = selfToDestinationAngles.toAngles(); animAngles = animAngles.toAngles(); yawDiff = AngleNormalize180(selfToDestinationAngles[YAW] - animAngles[YAW] ); if ( yawDiff >= -45.0 && yawDiff <= 45.0 ) _anim = "walk"; if ( yawDiff >= -135.0 && yawDiff <= -45.0 ) { _anim = "strafe_left_clear"; //_anim = "strafe_left"; //_anim = "strafe_right"; self.movementSubsystem->setAdjustAnimDir( false ); } if ( yawDiff >= 45.0 && yawDiff <= 135.0f ) { _anim = "strafe_right_clear"; //_anim = "strafe_right"; //_anim = "strafe_left"; self.movementSubsystem->setAdjustAnimDir( false ); } if ( yawDiff >= 135.0 && yawDiff <= 180.0f ) { _anim = "backpedal"; self.movementSubsystem->setMovingBackwards( true ); } if ( yawDiff <= -135.0 && yawDiff >= -180.0 ) { _anim = "backpedal"; self.movementSubsystem->setMovingBackwards( true ); } //Setup Torso Anim if appropriate if ( !self.torsoBehavior ) { _torsoAnim = self.combatSubsystem->GetAnimForMyWeapon( "Idle" ); if ( _torsoAnim.length() ) { self.SetAnim( _torsoAnim, NULL , torso ); } } // Setup our Component _chase.SetDistance( 32.0f ); _chase.SetPoint( _destination ); _chase.SetAnim( _anim ); //_chase.Begin( self ); _state = MOVE_FCOF_STATE_FOUND_SPOT; return true; } //-------------------------------------------------------------- // Name: _foundDestination() // Class: MoveFromConeOfFire // // Description: Handles the evaluation of our GotoPoint component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::_foundDestination( Actor &self ) { BehaviorReturnCode_t result; //self.SetAnim( _anim ); result = _chase.Evaluate( self ); if ( result == BEHAVIOR_EVALUATING ) return; // Check for any failure if ( result != BEHAVIOR_SUCCESS ) { _state = MOVE_FCOF_FAILED; return; } // Reached spot _chase.End( self ); self.SetAnim( "idle" ); _state = MOVE_FCOF_SUCCESS; } //-------------------------------------------------------------- // Name: _noDestination() // Class: MoveFromConeOfFire // // Description: Handles failure to find a destination // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::_noDestination( Actor & ) { _state = MOVE_FCOF_STATE_SEARCHING_FOR_NODE; _nextsearch = 0.0f; } //-------------------------------------------------------------- // Name: _searchForNode() // Class: MoveFromConeOfFire // // Description: Handles searching for a viable node // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::_searchForNode( Actor &self ) { if ( level.time > _nextsearch ) movegoal = _FindNode( self ); if ( !movegoal ) { self.SetAnim( "idle" ); return; } _state = MOVE_FCOF_STATE_FOUND_NODE; // Found node, going to it _nextsearch = level.time + 1.0f; } //-------------------------------------------------------------- // Name: _FindNode() // Class: MoveFromConeOfFire // // Description: Finds the a viable node, if one is available // // Parameters: Actor &self // // Returns: PathNode* //-------------------------------------------------------------- PathNode *MoveFromConeOfFire::_FindNode( Actor &self ) { int i; float dot; //float bestdist; float bestdot; float testdot; float newDot; Vector delta; Vector distanceToNode; Vector playerToSelf; Vector nodeToSelf; Vector l; PathNode *bestnode; PathNode *node; Player *player; //bestdist = 0.0f; bestdot = 0.0f; testdot = 0.0f; bestnode = NULL; player = GetPlayer( 0 ); playerToSelf = self.origin - player->origin; for ( i = 0 ; i < thePathManager.NumNodes() ; i++ ) { node = thePathManager.GetNode( i ); if ( !node ) return NULL; distanceToNode = node->origin - self.origin; if ( distanceToNode.length() > CONE_OF_FIRE_RADIUS ) continue; nodeToSelf = self.origin - node->origin; dot = DotProduct( playerToSelf, nodeToSelf ) ; if ( dot < 0 ) { playerToSelf = playerToSelf.toAngles(); playerToSelf.AngleVectors(NULL, &l, NULL ); newDot = DotProduct ( l , nodeToSelf ); testdot = abs( (int)newDot ); if ( testdot > bestdot ) { bestnode = node; bestdot = testdot; } } } if ( bestnode ) { bestnode->occupiedTime = level.time + 1.5f; bestnode->entnum = self.entnum; _chase.SetPoint( bestnode->origin ); _chase.Begin( self ); return bestnode; } return NULL; } //============================================================================== // Strafe //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, Strafe, NULL ) { { &EV_Behavior_Args, &Strafe::SetArgs }, { &EV_Behavior_AnimDone, &Strafe::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: Strafe // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Strafe::SetArgs ( Event *ev) { SetMode( ev->GetInteger( 1 ) ); } //-------------------------------------------------------------- // Name: AnimDone() // Class: Strafe // // Description: Handles an animation completion // // Parameters: Event *ev -- Event holding the completion notification // // Returns: None //-------------------------------------------------------------- void Strafe::AnimDone( Event * ) { _strafeComplete = true; } //-------------------------------------------------------------- // Name: Begin() // Class: Strafe // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Strafe::Begin( Actor &self ) { _canStrafe = true; _strafeComplete = false; _init( self ); } //-------------------------------------------------------------- // Name: _init() // Class: Strafe // // Description: Initializes memeber variables // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void Strafe::_init( Actor &self ) { float chance; chance = G_Random(); // First see if our mode is explicit if ( mode != STRAFE_RANDOM ) { _setAnim( self ); return; } //We'll go random then if ( chance < .5 ) { mode = STRAFE_LEFT; _setAnim( self ); if ( !_canStrafe ) { mode = STRAFE_RIGHT; _setAnim( self ); } } else { mode = STRAFE_RIGHT; _setAnim( self ); if ( !_canStrafe ) { mode = STRAFE_LEFT; _setAnim( self ); } } } //-------------------------------------------------------------- // Name: _setAnim() // Class: Strafe // // Description: Initializes memeber variables // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void Strafe::_setAnim( Actor &self ) { str currentPostureState = self.postureController->getCurrentPostureName(); switch ( mode ) { case STRAFE_LEFT: if ( !self.checkLeftDirectionClear( 64.0f ) ) { _canStrafe = false; return; } if ( currentPostureState.length() ) { if ( currentPostureState == "STAND" ) _anim = "strafe_left"; else if ( currentPostureState == "DUCK" ) _anim = "roll_left"; } else { _anim = "strafe_left"; } break; case STRAFE_RIGHT: if ( !self.checkRightDirectionClear( 64.0f ) ) { _canStrafe = false; return; } if ( currentPostureState.length() ) { if ( currentPostureState == "STAND" ) _anim = "strafe_right"; else if ( currentPostureState == "DUCK" ) _anim = "roll_right"; } else { _anim = "strafe_right"; } break; } if ( _canStrafe ) { self.SetAnim( _anim , EV_Actor_NotifyBehavior , legs ); } } //-------------------------------------------------------------- // Name: Evaluate() // Class: Strafe // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t Strafe::Evaluate ( Actor & ) { //self.SetAnim( _anim , EV_Actor_NotifyBehavior , legs ); if ( !_canStrafe ) return BEHAVIOR_FAILED; if ( _strafeComplete ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: Strafe // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Strafe::End ( Actor & ) { } void Strafe::SetMode( unsigned int strafeMode ) { if ( strafeMode >= STRAFE_NUMBER_OF_MODES ) gi.Error( ERR_DROP, "Strafe -- Unknown Strafe Mode"); mode = strafeMode; } //============================================================================== // CircleStrafeEntity //============================================================================== CLASS_DECLARATION( Behavior, CircleStrafeEntity, NULL ) { { &EV_Behavior_Args, &CircleStrafeEntity::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: CircleStrafeEntity // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void CircleStrafeEntity::SetArgs ( Event *ev) { _checkParameters(ev); _type = ev->GetString ( 1 ); _legAnim = ev->GetString ( 2 ); _radius = ev->GetFloat ( 3 ); _clockwise = ev->GetBoolean( 4 ); if ( ev->NumArgs() > 4 ) _testDistance = ev->GetFloat ( 5 ); else _testDistance = 80.0f; } //-------------------------------------------------------------- // Name: Begin() // Class: CircleStrafeEntity // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void CircleStrafeEntity::Begin( Actor &self ) { // Set our Strafe Target _strafeTarget = _getStrafeTarget( self , _type ); // Set our Anim self.SetAnim( _legAnim, EV_Actor_NotifyBehavior ); _failed = false; // Initialize _lastPosition = self.origin; _moveAttempts = 0; _startWanderTime = 0; _recheckTime = -1; _init( self ); } //-------------------------------------------------------------- // Name: Evaluate() // Class: CircleStrafeEntity // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t CircleStrafeEntity::Evaluate ( Actor &self ) { Vector Destination; Vector SelfToTarget; if ( !_strafeTarget ) return BEHAVIOR_FAILED; if ( _failed ) return BEHAVIOR_FAILED; SelfToTarget = _strafeTarget->origin - self.origin; SelfToTarget = SelfToTarget.toAngles(); SelfToTarget[PITCH] = 0; SelfToTarget[ROLL] = 0; _init( self ); _holdAngles.AngleVectors( &Destination ); if ( _checkIfStuck(self) ) return BEHAVIOR_FAILED; else { self.movementSubsystem->setMoveDir( Destination ); self.setAngles(SelfToTarget); } return BEHAVIOR_EVALUATING; } void CircleStrafeEntity::_init( Actor &self ) { Vector SelfToTarget; //trace_t traceccw, tracecw; trace_t tracecw; float StrafeAngle; float FallbackAngle; float EmergencyAngle; if ( !_strafeTarget ) { _failed = true; return; } //_recheckTime = level.time + G_Random() + .75; SelfToTarget = _strafeTarget->origin - self.origin; _holdAngles = SelfToTarget.toAngles(); _holdAngles.z = 0; _holdAngles.EulerNormalize(); if ( self.WithinDistance( _strafeTarget , _radius ) ) StrafeAngle = 90; else StrafeAngle = 45 + G_Random ( 10 ); FallbackAngle = 80 + G_Random ( 10 ); EmergencyAngle = 120 + G_Random ( 10 ); if ( !_clockwise ) { StrafeAngle *= -1; FallbackAngle *= -1; EmergencyAngle *= -1; } tracecw = self.Trace(StrafeAngle, _testDistance, "CircleStrafe Avoid Trace"); if ( tracecw.fraction < 1.0 ) { tracecw = self.Trace(FallbackAngle, _testDistance, "CircleStrafe Avoid Trace"); if ( tracecw.fraction < 1.0 ) { tracecw = self.Trace(EmergencyAngle, _testDistance, "CircleStrafe Avoid Trace"); if ( tracecw.fraction < 1.0 ) { _failed = true; return; } else { _holdAngles[YAW] += FallbackAngle; return; } } else { _holdAngles[YAW] += FallbackAngle; return; } } else { _holdAngles[YAW] += StrafeAngle; } } //-------------------------------------------------------------- // Name: End() // Class: CircleStrafeEntity // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void CircleStrafeEntity::End ( Actor &self ) { _wander.End( self ); } //-------------------------------------------------------------- // Name: _getStrafeTarget() // Class: CircleStrafeEntity // // Description: Sets the Strafe Target // // Parameters: Actor &self // const str &target // // Returns: Entity *ent //-------------------------------------------------------------- Entity* CircleStrafeEntity::_getStrafeTarget( Actor &self, const str &target ) { Entity *ent = NULL; if ( target == "player" ) ent = GetPlayer( 0 ); else if ( target == "enemy" ) ent = self.enemyManager->GetCurrentEnemy(); return ent; } //-------------------------------------------------------------- // Name: _checkParameters() // Class: CircleStrafeEntity // // Description: Checks the Parameters for proper number and type // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void CircleStrafeEntity::_checkParameters( Event *ev ) { if ( ev->NumArgs() < 4 ) gi.Error( ERR_DROP, "CircleStrafeEntity::_checkParameters -- Wrong Parameter Count"); } //-------------------------------------------------------------- // Name: _checkIfStuck() // Class: CircleStrafeEntity // // Description: Checks if the actor is stuck in position // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- qboolean CircleStrafeEntity::_checkIfStuck(Actor &self) { // See if we're stuck Vector checkDistance = _lastPosition - self.origin; if ( checkDistance.length() < 1.5f ) _moveAttempts++; else { _moveAttempts = 0; _lastPosition = self.origin; } if ( _moveAttempts >= 3 ) return true; else return false; } //============================================================================== // FollowInFormation //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, FollowInFormation, NULL ) { { &EV_Behavior_Args, &FollowInFormation::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: FollowInFormation // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void FollowInFormation::SetArgs ( Event *ev) { _anim = ev->GetString ( 1 ); if ( ev->NumArgs() > 1 ) _emergencyDistance = ev->GetFloat( 2 ); else _emergencyDistance = 0; if ( ev->NumArgs() > 2 ) _catchupSpeed = ev->GetFloat( 3 ); else _catchupSpeed = 10.0f; } //-------------------------------------------------------------- // Name: Begin() // Class: FollowInFormation // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::Begin( Actor &self ) { // Set appropriate flags self.SetActorFlag( ACTOR_FLAG_FOLLOWING_IN_FORMATION , true ); _selectedFollowTarget = false; _followDist = self.followTarget.maxRangeIdle; _followDistMin = _followDist * 0.75f; if ( _followDistMin < 96.0f ) _followDistMin = 96.0f; // If we don't have a follow target, then we are going to assume // we're following the player SetDefaultFollowTarget( self ); _state = FOLLOW_TARGET_STATE_SELECT_STATE; // Setup failure handlers _nextFollowAttemptTime = 0.0f; _nextTargetCheckTime = 0.0f; _endHold = 0.0f; _followFailureTime = 0.0f; _setFollowFailureTime = false; _attemptedWarpToPath = false; _codeDriven = false; _oldForwardSpeed = self.movementSubsystem->getForwardSpeed(); _oldTurnSpeed = self.movementSubsystem->getTurnSpeed(); } //-------------------------------------------------------------- // Name: Evaluate() // Class: FollowInFormation // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t FollowInFormation::Evaluate ( Actor &self ) { switch ( _state ) { case FOLLOW_TARGET_STATE_SELECT_STATE: selectState( self ); break; case FOLLOW_TARGET_STATE_FOLLOW_TARGET: follow( self ); break; case FOLLOW_TARGET_STATE_FOLLOW_HOLD: hold ( self ); break; case FOLLOW_TARGET_STATE_FOLLOW_WARP_TO_NEAREST_PATHNODE: warpToPathNode( self ); break; case FOLLOW_TARGET_STATE_FOLLOW_WARP_TO_FOLLOW_TARGET: warpToTarget( self ); break; case FOLLOW_TARGET_STATE_FIND_FOLLOW_TARGET: findFollowTarget( self ); break; case FOLLOW_TARGET_STATE_FOLLOW_FAILED: return BEHAVIOR_FAILED; break; case FOLLOW_TARGET_STATE_FOLLOW_NO_TARGET: return BEHAVIOR_FAILED; case FOLLOW_TARGET_STATE_FOLLOW_SUCCESS: return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: FollowInFormation // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::End ( Actor &self ) { self.SetActorFlag( ACTOR_FLAG_FOLLOWING_IN_FORMATION , false ); self.movementSubsystem->setForwardSpeed( _oldForwardSpeed ); self.movementSubsystem->setTurnSpeed( _oldTurnSpeed ); } //-------------------------------------------------------------- // Name: SetDefaultFollowTarget() // Class: FollowInFormation // // Description: Checks if we have a specified follow target, if not // we default it to the player // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::SetDefaultFollowTarget( Actor &self ) { if ( !self.followTarget.specifiedFollowTarget ) { Player *player; player = GetPlayer( 0 ); if ( !player ) { _state = FOLLOW_TARGET_STATE_FOLLOW_NO_TARGET; return; } self.followTarget.specifiedFollowTarget = player; } } //-------------------------------------------------------------- // Name: findFollowTarget() // Class: FollowInFormation // // Description: Iterates through everyone in the actor's group // and, based on distance, determines who it's // current FollowTarget is. // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::findFollowTarget( Actor &self ) { Vector selfToFollowTarget; Vector actToFollowTarget; Vector selfToAct; float actDistToFollowTarget; float distToFollowTarget; float distToAct; float bestDist; Actor *act; if ( level.time < _nextTargetCheckTime || !self.followTarget.specifiedFollowTarget ) return; // We default to our specifed target self.followTarget.currentFollowTarget = self.followTarget.specifiedFollowTarget; selfToFollowTarget = self.followTarget.currentFollowTarget->origin - self.origin; //Get our distance; selfToFollowTarget = self.followTarget.specifiedFollowTarget->origin - self.origin; distToFollowTarget = selfToFollowTarget.length(); bestDist = 9999999.9f; // Here we iterate through the active actors looking for groupmembers who have the same // follow target AND are currently following in formation. We find the group member who // is closer to the specified target AND closest to this actor and we follow it. This // will allow a nice single-file formation for( int i = 1; i <= ActiveList.NumObjects(); i++ ) { act = ActiveList.ObjectAt( i ); if ( act && act->entnum != self.entnum && act->GetGroupID() == self.GetGroupID() ) { if ( !act->GetActorFlag(ACTOR_FLAG_FOLLOWING_IN_FORMATION) || act->followTarget.specifiedFollowTarget != self.followTarget.specifiedFollowTarget ) continue; actToFollowTarget = self.followTarget.currentFollowTarget->origin - act->origin; actDistToFollowTarget = actToFollowTarget.length(); if ( actDistToFollowTarget < distToFollowTarget && actDistToFollowTarget < bestDist ) { selfToAct = act->origin - self.origin; distToAct = selfToAct.length(); if ( distToAct < bestDist ) { // We need to maintain who is closest to us, here. self.followTarget.currentFollowTarget = act; bestDist = distToAct; } } } } _selectedFollowTarget = true; // Set up our component behavior _followEntity.SetAnim( _anim ); _followEntity.SetEntity( self, self.followTarget.currentFollowTarget ); if ( self.followTarget.currentFollowTarget != self.followTarget.specifiedFollowTarget ) _followEntity.SetDistance( _followDistMin ); else _followEntity.SetDistance( _followDist ); _followEntity.Begin( self ); _state = FOLLOW_TARGET_STATE_SELECT_STATE; _nextTargetCheckTime = level.time + G_Random(); } //-------------------------------------------------------------- // Name: selectState() // Class: FollowInFormation // // Description: Sets the internal state of this behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::selectState( Actor &self ) { if ( !_selectedFollowTarget ) { _state = FOLLOW_TARGET_STATE_FIND_FOLLOW_TARGET; return; } if ( !self.WithinDistanceXY( self.followTarget.currentFollowTarget , _followDist ) ) { setupFollow( self ); _state = FOLLOW_TARGET_STATE_FOLLOW_TARGET; return; } _state = FOLLOW_TARGET_STATE_FOLLOW_HOLD; setupHold( self ); } //-------------------------------------------------------------- // Name: follow() // Class: FollowInFormation // // Description: Evaluates our GotoEntity Component Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::follow( Actor &self ) { findFollowTarget( self ); BehaviorReturnCode_t gotoEntityResult; //checkSpeed( self ); if ( !self.followTarget.currentFollowTarget ) return; if ( self.WithinDistanceXY(self.followTarget.currentFollowTarget , _followDist + 96.0f ) ) { _followEntity.SetAnim( "walk" ); } gotoEntityResult = _followEntity.Evaluate( self ); if ( gotoEntityResult == BEHAVIOR_SUCCESS ) { _followEntity.End( self ); _nextFollowAttemptTime = 0.0f; _state = FOLLOW_TARGET_STATE_SELECT_STATE; return; } if ( gotoEntityResult != BEHAVIOR_EVALUATING ) { // Comment this line out if you need to disable // fail safes for debugging purposes //followFailed( self ); _state = FOLLOW_TARGET_STATE_FOLLOW_FAILED; return; } } //-------------------------------------------------------------- // Name: followFailed() // Class: FollowInFormation // // Description: Failure Handler for _follow // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::followFailed( Actor &self ) { if ( !_setFollowFailureTime ) { _followFailureTime = level.time + 1.5; _setFollowFailureTime = true; return; } if ( level.time >= _followFailureTime ) { _followFailureTime = 0.0f; _setFollowFailureTime = false; if ( !_attemptedWarpToPath ) { _state = FOLLOW_TARGET_STATE_FOLLOW_WARP_TO_NEAREST_PATHNODE; setupWarpToPathNode( self ); return; } else { _state = FOLLOW_TARGET_STATE_FOLLOW_WARP_TO_FOLLOW_TARGET; setupWarpToTarget( self ); return; } } else _state = FOLLOW_TARGET_STATE_SELECT_STATE; _nextFollowAttemptTime = level.time + G_Random( 1.0f ); } //-------------------------------------------------------------- // Name: setupWarpToPathNode() // Class: FollowInFormation // // Description: Sets up our WarpToPosition Component to warp us // to the nearest pathnode // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::setupWarpToPathNode( Actor &self ) { // Find the path node nearest to us PathNode *goalNode = thePathManager.NearestNode( self.origin , NULL , true , false ); if ( !goalNode ) { _state = FOLLOW_TARGET_STATE_FOLLOW_WARP_TO_FOLLOW_TARGET; setupWarpToTarget( self ); return; } _warpToPosition.SetPosition( goalNode->origin ); _warpToPosition.Begin( self ); } //-------------------------------------------------------------- // Name: warpToPathNode() // Class: FollowInFormation // // Description: Evaluates our WarpToPosition Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::warpToPathNode( Actor &self ) { BehaviorReturnCode_t result; _attemptedWarpToPath = true; result = _warpToPosition.Evaluate( self ); if ( result != BEHAVIOR_EVALUATING ) { setupFollow( self ); _state = FOLLOW_TARGET_STATE_FOLLOW_TARGET; } } //-------------------------------------------------------------- // Name: setupWarpToTarget() // Class: FollowInFormation // // Description: Sets up our WarpToEntity Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::setupWarpToTarget( Actor &self ) { _warpToEntity.SetEntity( self.followTarget.currentFollowTarget ); _warpToEntity.Begin( self ); } //-------------------------------------------------------------- // Name: warpToTarget() // Class: FollowInFormation // // Description: Evaluates our WarpToEntity Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::warpToTarget( Actor &self ) { BehaviorReturnCode_t result; result = _warpToEntity.Evaluate( self ); if ( result != BEHAVIOR_EVALUATING ) { setupFollow( self ); _state = FOLLOW_TARGET_STATE_FOLLOW_TARGET; } } //-------------------------------------------------------------- // Name: setupFollow() // Class: FollowInFormation // // Description: Calls Begin() on our GotoEntity Component Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::setupFollow( Actor &self ) { _followEntity.Begin( self ); } //-------------------------------------------------------------- // Name: setupHold() // Class: FollowInFormation // // Description: Sets up back to the "idle" animation so that we // don't go running into walls // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::setupHold( Actor &self ) { _endHold = level.time + .75; self.SetAnim( "idle" ); } //-------------------------------------------------------------- // Name: hold() // Class: FollowInFormation // // Description: Keeps us stationary until our current follow // target moves out of range // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowInFormation::hold( Actor &self ) { if ( !self.followTarget.currentFollowTarget ) { _state = FOLLOW_TARGET_STATE_FOLLOW_NO_TARGET; return; } findFollowTarget( self ); if ( level.time > _endHold ) { _state = FOLLOW_TARGET_STATE_SELECT_STATE; return; } } void FollowInFormation::checkSpeed( Actor &self ) { if ( _emergencyDistance < 1 ) return; if ( !self.WithinDistanceXY( self.followTarget.currentFollowTarget , _emergencyDistance ) ) { _followEntity.SetAnim( "run_codedriven" ); self.movementSubsystem->setForwardSpeed( _catchupSpeed ); self.movementSubsystem->setTurnSpeed( 360.0f ); _codeDriven = true; } else if ( _codeDriven ) { self.movementSubsystem->setForwardSpeed( 0.0 ); //E3 2002 Hack -- Needs to be_oldForwardSpeed, but it's bugged right now self.movementSubsystem->setTurnSpeed( 25.0 ); //E3 2002 Hack -- Needs to be _oldTurnspeed, but it's bugged right now _codeDriven = false; } } //============================================================================== // GroupFollow //============================================================================== const float GroupFollow::_minRangeMultiplier = 0.9f; const float GroupFollow::_maxRangeMultiplier = 1.1f; //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, GroupFollow, NULL ) { { &EV_Behavior_Args, &GroupFollow::SetArgs }, { &EV_Behavior_AnimDone, &GroupFollow::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- float GetAnimationRate( Entity &entity, const int animation ) { const float time = gi.Anim_Time( entity.edict->s.modelindex, animation ); assert( time != 0.0f ); if ( time == 0.0f ) { gi.DPrintf( "Invalid animation for %d\n", entity.entnum ); return 0.0f; } Vector myNewAnimationTotalDelta; gi.Anim_Delta( entity.edict->s.modelindex, animation, myNewAnimationTotalDelta ); return myNewAnimationTotalDelta.length() / time; } //-------------------------------------------------------------- float GetAnimationRate( Entity &entity, const str &animationName ) { const int animation = gi.Anim_Random( entity.edict->s.modelindex, animationName ); return GetAnimationRate( entity, animation ); } //-------------------------------------------------------------- // Name: SetArgs() // Class: GroupFollow // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void GroupFollow::SetArgs ( Event *ev) { _stopDistance = ev->GetFloat( 1 ); _paceDistance = ev->GetFloat( 2 ); _idleAnimation = "idle"; _paceAnimation = "walk"; _closeAnimation = "run"; if (ev->NumArgs() > 2 ) { _idleAnimation = ev->GetString( 3 ); if (ev->NumArgs() > 3 ) { _paceAnimation = ev->GetString( 4 ); if (ev->NumArgs() > 4 ) { _closeAnimation = ev->GetString( 5 ); } } } } //-------------------------------------------------------------- // Name: AnimDone() // Class: GroupFollow // // Description: Resets the animation rate each time the previous // animation cycle finishes // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void GroupFollow::AnimDone( Event * ) { _animationRateNeedsUpdate = true; } //-------------------------------------------------------------- // Name: SetArgs() // Class: GroupFollow // // Description: Sets the arguments of the behavior // // Parameters: const str &anim, EntityPtr specifiedTarget, // const float stopDistance, const float paceDistance // // Returns: None //-------------------------------------------------------------- void GroupFollow::SetArgs ( const float stopDistance, const float paceDistance ) { _stopDistance = stopDistance; _paceDistance = paceDistance; } //-------------------------------------------------------------- // Name: Begin() // Class: GroupFollow // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GroupFollow::Begin( Actor &self ) { _nextFindFollowTime = 0.0f; _nextPathLenCheckTime = 0.0f; _nextPathLenCheckTime2 = 0.0f; if ( !self.followTarget.specifiedFollowTarget ) { self.followTarget.specifiedFollowTarget = GetPlayer( 0 ); assert( self.followTarget.specifiedFollowTarget ); } self.followTarget.currentFollowTarget = self.followTarget.specifiedFollowTarget; // Set appropriate flags self.SetActorFlag( ACTOR_FLAG_FOLLOWING_IN_FORMATION , true ); _animationRateNeedsUpdate = false; _follow.Begin( self ); GotoHoldState( self ); //Setup Torso Anim if appropriate _torsoAnimation = self.combatSubsystem->GetAnimForMyWeapon( "Idle" ); if ( _torsoAnimation.length() ) { self.SetAnim( _torsoAnimation, NULL , torso ); } // Setup failure handlers _endHold = 0.0f; _oldForwardSpeed = self.movementSubsystem->getForwardSpeed(); } //-------------------------------------------------------------- // Name: Evaluate() // Class: GroupFollow // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t GroupFollow::Evaluate ( Actor &self ) { BehaviorReturnCode_t returnCode = BEHAVIOR_INVALID; FindFollowTarget( self ); switch ( _state ) { case CLOSE_WITH_TARGET: returnCode = CloseWithTarget( self ); break; case PACE_TARGET: returnCode = PaceTarget( self ); break; case HOLD: returnCode = Hold( self ); break; case WANDER: returnCode = Wander(self); break; default: assert( false ); // the default case is not valid break; } //_follow.SetEntity( self, self.followTarget.currentFollowTarget ); return returnCode; } //-------------------------------------------------------------- // Name: End() // Class: GroupFollow // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GroupFollow::End ( Actor &self ) { self.SetActorFlag( ACTOR_FLAG_FOLLOWING_IN_FORMATION , false ); self.movementSubsystem->setForwardSpeed( _oldForwardSpeed ); } //-------------------------------------------------------------- // Name: findFollowTarget() // Class: GroupFollow // // Description: Iterates through everyone in the actor's group // and, based on distance, determines who it's // current FollowTarget is. // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- BehaviorReturnCode_t GroupFollow::FindFollowTarget( Actor &self ) { // FindFollowTarget is expensive, we're putting a timer on it to keep it throttled if ( level.time <= _nextFindFollowTime ) return BEHAVIOR_EVALUATING; _nextFindFollowTime = level.time + (G_Random() + 0.25 ); // Here we iterate through the active actors looking for groupmembers who have the same // follow target AND are currently following in formation. We find the group member who // is closer to the specified target AND closest to this actor and we follow it. This // will allow a nice single-file formation FindMovementPath find; Path *path; float distanceFromCurrentActorToTarget; float distanceFromGroupMemberToTarget; // Set up our pathing heuristics find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; // First Check how far away we are from our specified followtarget, if we're pretty far away, chances are // something is all kinds of screwed up, so we're just going to follow our specified target for a while path = find.FindPath( self.followTarget.specifiedFollowTarget->origin, self.origin ); if ( path ) { distanceFromCurrentActorToTarget = path->Length(); delete path; path = NULL; } else { distanceFromCurrentActorToTarget = Vector::Distance( self.followTarget.specifiedFollowTarget->origin, self.origin ); } Entity* currentEnemy = NULL; float maxDistance; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( currentEnemy ) { maxDistance = self.followTarget.maxRangeCombat; } else { maxDistance = self.followTarget.maxRangeIdle; } //If our specified target is more than 3 times farther away than our maximum follow range //(based on our combat situation ) then we forget about trying to find other targets and run //for our "master" -- This should help prevent wacky circular screwups.where: //A is Following B who is Following C who is Following A if ( distanceFromCurrentActorToTarget > ( maxDistance * 3 ) ) { _follow.SetEntity( self, self.followTarget.specifiedFollowTarget ); return BEHAVIOR_EVALUATING; } // Create a list of group members sorted by distance to target Container< Actor * > groupList; for( int i = 1; i <= ActiveList.NumObjects(); i++ ) { Actor ¤tActor = *ActiveList.ObjectAt( i ); if ( currentActor.GetGroupID() == self.GetGroupID() ) { if ( ( currentActor.GetActorFlag(ACTOR_FLAG_FOLLOWING_IN_FORMATION ) ) && ( currentActor.followTarget.specifiedFollowTarget == self.followTarget.specifiedFollowTarget ) ) { path = NULL; if ( level.time >= _nextPathLenCheckTime ) { if ( sv_traceinfo->integer ) gi.WDPrintf( "Pathing To FollowTarget1" ); path = find.FindPath( self.followTarget.specifiedFollowTarget->origin, currentActor.origin ); _nextPathLenCheckTime = level.time + (G_Random() + 1.0); } if ( path ) { distanceFromCurrentActorToTarget = path->Length(); delete path; path = NULL; } else //Was DistanceXY distanceFromCurrentActorToTarget = Vector::Distance( self.followTarget.specifiedFollowTarget->origin, currentActor.origin ); int j; for ( j = 1; j <= groupList.NumObjects(); j++ ) { path = NULL; const Actor ¤tGroupMember = *groupList.ObjectAt( j ); if ( level.time >= _nextPathLenCheckTime2 ) { if ( sv_traceinfo->integer ) gi.WDPrintf( "Pathing To FollowTarget2" ); path = find.FindPath( self.followTarget.specifiedFollowTarget->origin, currentGroupMember.origin ); _nextPathLenCheckTime2 = level.time + (G_Random() + 1.0); } if ( path ) { distanceFromGroupMemberToTarget = path->Length(); delete path; path = NULL; } else //Was DistanceXY distanceFromGroupMemberToTarget = Vector::Distance( self.followTarget.specifiedFollowTarget->origin, currentGroupMember.origin ); if ( distanceFromCurrentActorToTarget < distanceFromGroupMemberToTarget ) { break; } } if ( j > groupList.NumObjects() ) { groupList.AddObject( ¤tActor ); } else { groupList.InsertObjectAt( j, ¤tActor ); } } } } // Make the next closest group member our current target, // if we are closest then set our current target to our // specified target int j; for ( j = 1; j <= groupList.NumObjects(); j++ ) { if ( groupList.ObjectAt( j )->entnum == self.entnum ) { break; } } Entity *currentFollowEntity; currentFollowEntity = _follow.GetEntity(); if ( !currentFollowEntity ) { _follow.SetEntity( self, self.followTarget.currentFollowTarget ); } if ( j == 1 ) { if ( self.followTarget.currentFollowTarget != self.followTarget.specifiedFollowTarget ) { self.followTarget.currentFollowTarget = self.followTarget.specifiedFollowTarget; currentFollowEntity = _follow.GetEntity(); if ( !currentFollowEntity ) { _follow.SetEntity( self, self.followTarget.currentFollowTarget ); } else if ( currentFollowEntity != self.followTarget.currentFollowTarget ) { _follow.SetEntity( self, self.followTarget.currentFollowTarget ); } } } else { if ( self.followTarget.currentFollowTarget != groupList.ObjectAt( j - 1 ) ) { self.followTarget.currentFollowTarget = groupList.ObjectAt( j - 1 ); currentFollowEntity = _follow.GetEntity(); if ( !currentFollowEntity ) { _follow.SetEntity( self, self.followTarget.currentFollowTarget ); } else if ( currentFollowEntity != self.followTarget.currentFollowTarget ) { _follow.SetEntity( self, self.followTarget.currentFollowTarget ); } } } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: hold() // Class: GroupFollow // // Description: Keeps us stationary until our current follow // target moves out of range // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- BehaviorReturnCode_t GroupFollow::Hold( Actor &self ) { assert( _stopDistance * _maxRangeMultiplier < _paceDistance * _minRangeMultiplier ); //float distanceToTarget = Vector::DistanceXY( self.origin, self.followTarget.currentFollowTarget->origin ); float distanceToTarget = Vector::Distance( self.origin, self.followTarget.currentFollowTarget->origin ); if ( level.time > _endHold ) { if ( distanceToTarget > _stopDistance * _maxRangeMultiplier) { GotoPaceTargetState( self ); } else { return BEHAVIOR_SUCCESS; } } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: PaceTarget() // Class: GroupFollow // // Description: Handles the state where the behavior wants to // maintain distance with a moving target or slowly // close with a stationary target // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- BehaviorReturnCode_t GroupFollow::PaceTarget( Actor &self ) { if ( _animationRateNeedsUpdate ) { const float animationRate = ComputeAnimationRate( self, _paceAnimation, ComputePaceAnimationRateMultiplier( self ) ); if ( !fSmallEnough( animationRate, fEpsilon() ) ) { if ( !fCloseEnough( animationRate, self.edict->s.animationRate, 0.5f) ) { self.SetAnim( _paceAnimation, EV_Actor_NotifyBehavior, legs, animationRate ); _follow.Begin( self ); } } else { GotoHoldState( self ); return BEHAVIOR_EVALUATING; } _animationRateNeedsUpdate = false; } BehaviorReturnCode_t gotoEntityResult = _follow.Evaluate( self ); if ( gotoEntityResult != BEHAVIOR_EVALUATING ) { _follow.End( self ); if ( gotoEntityResult == BEHAVIOR_FAILED ) GotoWanderState(self); else GotoHoldState( self ); } else { assert( _stopDistance * _maxRangeMultiplier < _paceDistance * _minRangeMultiplier ); //float distanceToTarget = Vector::DistanceXY( self.origin, self.followTarget.currentFollowTarget->origin ); float distanceToTarget = Vector::Distance( self.origin, self.followTarget.currentFollowTarget->origin ); if ( distanceToTarget > _paceDistance * _maxRangeMultiplier ) { GotoCloseWithTargetState( self ); } else if ( distanceToTarget < _stopDistance * _minRangeMultiplier ) { GotoHoldState( self ); } } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: CloseWithTarget() // Class: GroupFollow // // Description: Handles the state where the behavior wants to // rapidly close with its target // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- BehaviorReturnCode_t GroupFollow::CloseWithTarget( Actor &self ) { if ( _animationRateNeedsUpdate ) { const float animationRate = ComputeAnimationRate( self, _closeAnimation, 1.75f ); if ( !fCloseEnough( animationRate, self.edict->s.animationRate, 0.5f) ) { self.SetAnim( _closeAnimation, EV_Actor_NotifyBehavior, legs, animationRate ); _follow.Begin( self ); _animationRateNeedsUpdate = false; } } BehaviorReturnCode_t gotoEntityResult = _follow.Evaluate( self ); /* if ( gotoEntityResult != BEHAVIOR_SUCCESS && gotoEntityResult != BEHAVIOR_EVALUATING ) { _follow.End( self ); //GotoHoldState( self ); GotoWanderState(self); } if ( gotoEntityResult == BEHAVIOR_SUCCESS ) { _follow.End( self ); GotoHoldState( self ); } */ if ( gotoEntityResult != BEHAVIOR_EVALUATING ) { _follow.End( self ); GotoHoldState( self ); } assert( _stopDistance * _maxRangeMultiplier < _paceDistance * _minRangeMultiplier ); float distanceToTarget = Vector::DistanceXY( self.origin, self.followTarget.currentFollowTarget->origin ); if ( distanceToTarget < _paceDistance * _minRangeMultiplier && self.sensoryPerception->CanSeeEntity( &self,self.followTarget.currentFollowTarget , false , false ) ) { GotoPaceTargetState( self ); } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: GotoHoldState() // Class: GroupFollow // // Description: Puts the behavior in the "Hold" state // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GroupFollow::GotoHoldState( Actor &self ) { _follow.End( self ); _state = HOLD; _endHold = level.time + 0.75f; self.SetAnim( _idleAnimation, EV_Actor_NotifyBehavior, legs, 1.0f ); } float GroupFollow::ComputePaceAnimationRateMultiplier( Actor &self ) { const float distanceToTarget = Vector::DistanceXY( self.origin, self.followTarget.currentFollowTarget->origin ); float animationRateMultiplier = 1.0f; if ( distanceToTarget < _stopDistance ) { animationRateMultiplier = 0.0f; } else if ( distanceToTarget < _paceDistance ) { animationRateMultiplier = 0.0f + ( ( distanceToTarget - _stopDistance) / ( _paceDistance - _stopDistance) ) * 1.0f; } return animationRateMultiplier; } //-------------------------------------------------------------- // Name: GotoPaceTargetState() // Class: GroupFollow // // Description: Puts the behavior in the "PaceTarget" state // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GroupFollow::GotoPaceTargetState( Actor &self ) { float animationRateMultiplier = ComputePaceAnimationRateMultiplier( self ); _follow.End( self ); _state = PACE_TARGET; const float animationRate = ComputeAnimationRate( self, _paceAnimation, animationRateMultiplier ); if ( !fSmallEnough( animationRate, fEpsilon() ) ) { self.SetAnim( _paceAnimation, EV_Actor_NotifyBehavior, legs, animationRate ); _follow.Begin( self ); } else { GotoHoldState( self ); } } float GroupFollow::ComputeAnimationRate( Actor &self, const str &animationName, const float scale ) { const float myNewAnimationSpeed = GetAnimationRate( self, animationName); if ( !fSmallEnough( myNewAnimationSpeed, fEpsilon() ) ) { const float targetAnimationSpeed = GetAnimationRate( *self.followTarget.currentFollowTarget, self.followTarget.currentFollowTarget->CurrentAnim( legs ) ); if ( !fSmallEnough( targetAnimationSpeed, fEpsilon() ) ) { const float animationRate = scale * targetAnimationSpeed / myNewAnimationSpeed; if ( animationRate > 1.0f ) { return animationRate; } } } return 1.0f; } //-------------------------------------------------------------- // Name: GotoCloseWithTargetState() // Class: GroupFollow // // Description: Puts the behavior in the "CloseWithTarget" state // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GroupFollow::GotoCloseWithTargetState( Actor &self ) { _follow.End( self ); _state = CLOSE_WITH_TARGET; const float animationRate = ComputeAnimationRate( self, _closeAnimation, 1.1f ); self.SetAnim( _closeAnimation, EV_Actor_NotifyBehavior, legs, animationRate ); _follow.Begin( self ); } void GroupFollow::GotoWanderState( Actor &self ) { _wander.SetAnim( "walk" ); _wander.SetMinDistance( 32 ); _wander.SetDistance( 48 ); _wander.Begin(self); _state = WANDER; } BehaviorReturnCode_t GroupFollow::Wander( Actor &self ) { BehaviorReturnCode_t result; result = _wander.Evaluate( self ); if ( result == BEHAVIOR_FAILED ) { GotoHoldState(self); } if ( result == BEHAVIOR_SUCCESS ) { GotoCloseWithTargetState(self); } return BEHAVIOR_EVALUATING; } //============================================================================== // MoveToDistanceFromEnemy //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, MoveToDistanceFromEnemy, NULL ) { { &EV_Behavior_Args, &MoveToDistanceFromEnemy::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // // Name: SetArgs() // Class: MoveToDistanceFromEnemy // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None // //-------------------------------------------------------------- void MoveToDistanceFromEnemy::SetArgs ( Event *ev) { _checkParameters(ev); _anim = ev->GetString( 1 ); _distance = ev->GetFloat( 2 ); } //-------------------------------------------------------------- // // Name: Begin() // Class: MoveToDistanceFromEnemy // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None // //-------------------------------------------------------------- void MoveToDistanceFromEnemy::Begin( Actor &self ) { movegoal = NULL; _state = MOVE_TO_DISTANCE_STATE_SEARCHING_FOR_NODE; //self.SetAnim( _anim ); Vector dir; dir = self.movementSubsystem->getAnimDir(); dir = dir.toAngles(); self.setAngles( dir ); self.testing = true; self.SetActorFlag(ACTOR_FLAG_RETREATING , true ); _away = self.enemyManager->GetAwayFromEnemies(); _away = _away * _distance; _away = _away + self.origin; } //-------------------------------------------------------------- // // Name: Evaluate() // Class: MoveToDistanceFromEnemy // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False // //-------------------------------------------------------------- BehaviorReturnCode_t MoveToDistanceFromEnemy::Evaluate ( Actor &self ) { Entity *currentEnemy = NULL; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_FAILED; Vector startPos; Vector endPos; startPos = self.origin; endPos = _away; startPos.z += 25; endPos.z += 25; //G_DebugLine( startPos, endPos , 0.0f, 1.0f, 0.0f, 1.0 ); if ( !movegoal ) _state = MOVE_TO_DISTANCE_STATE_SEARCHING_FOR_NODE; switch( _state ) { case MOVE_TO_DISTANCE_STATE_SEARCHING_FOR_NODE : // Checking for nodes /* _away = self.enemyManager->GetAwayFromEnemies(); _away = _away * _distance; _away = _away + self.origin; */ _chase.Begin( self ); movegoal = _FindNode( self ); if ( !movegoal ) return BEHAVIOR_SUCCESS; // Found node, going to it self.SetAnim( _anim ); _state = MOVE_TO_DISTANCE_STATE_FOUND_NODE; _nextsearch = level.time + 1.25f; // lint -fallthrough case MOVE_TO_DISTANCE_STATE_FOUND_NODE : Vector distToGoal; distToGoal = movegoal->origin - _away; float dist; dist = distToGoal.length(); if ( dist >= 400 ) { return BEHAVIOR_FAILED; } if ( _chase.Evaluate( self ) == Steering::EVALUATING ) { if ( _nextsearch < level.time ) _state = MOVE_TO_DISTANCE_STATE_SEARCHING_FOR_NODE; return BEHAVIOR_EVALUATING; } // Reached node _chase.End( self ); self.SetAnim( "idle" ); return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // // Name: End() // Class: MoveToDistanceFromEnemy // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None // //-------------------------------------------------------------- void MoveToDistanceFromEnemy::End ( Actor &self ) { self.testing = false; self.SetActorFlag(ACTOR_FLAG_RETREATING , false ); _chase.End( self ); } //-------------------------------------------------------------- // // Name: _FindNode() // Class: MoveToDistanceFromEnemy // // Description: Gets an appropriate pathnode, flagged as AI_ACTION // // Parameters: Actor &self // // Returns: PathNode* // //-------------------------------------------------------------- PathNode *MoveToDistanceFromEnemy::_FindNode( Actor &self ) { int i; PathNode *bestnode; PathNode *node; FindCoverPath find; Vector delta; Vector pos; pos = _away; bestnode = NULL; node = NULL; float bestdist; float dist; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return NULL; bestdist = 9999999999.9f; for( i = 0; i <= thePathManager.NumNodes(); i++ ) { node = thePathManager.GetNode( i ); if ( node && (node->occupiedTime <= level.time) ) { // Get ourselves a good one delta = node->origin - pos; dist = delta.length(); if ( dist < bestdist ) { bestnode = node; bestdist = dist; } } } if ( bestnode ) { bestnode->occupiedTime = level.time + 1.5f; bestnode->entnum = self.entnum; float radius=16.0f; _chase.SetGoal( bestnode->origin, radius, self ); return bestnode; } return NULL; } //-------------------------------------------------------------- // // Name: _checkParameters() // Class: MoveToDistanceFromEnemy // // Description: Checks if the passed in parameters are what we are expecting // // Parameters: Event *ev // // Returns: None // //-------------------------------------------------------------- void MoveToDistanceFromEnemy::_checkParameters( Event *ev ) { if ( ev->NumArgs() != 2 ) gi.Error( ERR_DROP, "FindWork::FindWork -- Wrong Parameter Count"); if ( ev->IsNumericAt( 1 ) ) gi.Error( ERR_DROP, "FindWork::_checkParameters -- Parameter Mismatch"); } //-------------------------------------------------------------- // Name: SetAnim() // Class: MoveToDistanceFromEnemy // // Description: Mutator // // Parameters: const str &anim // // Returns: None //-------------------------------------------------------------- void MoveToDistanceFromEnemy::SetAnim( const str &anim ) { _anim = anim; } //-------------------------------------------------------------- // Name: SetDistance() // Class: MoveToDistanceFromEnemy // // Description: Mutator // // Parameters: float distance // // Returns: None //-------------------------------------------------------------- void MoveToDistanceFromEnemy::SetDistance( float distance ) { _distance = distance; } //============================================================================== // Template //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, BackAwayFromEnemy, NULL ) { { &EV_Behavior_Args, &BackAwayFromEnemy::SetArgs }, { &EV_Behavior_AnimDone, &BackAwayFromEnemy::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: BackAwayFromEnemy // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::SetArgs ( Event *ev) { _anim = ev->GetString( 1 ); _dist = ev->GetFloat( 2 ); _minDist = ev->GetFloat( 3 ); } void BackAwayFromEnemy::AnimDone( Event * ) { } //-------------------------------------------------------------- // Name: Begin() // Class: BackAwayFromEnemy // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::Begin( Actor & ) { _state = BAFE_SELECT_STATE; } //-------------------------------------------------------------- // Name: Evaluate() // Class: BackAwayFromEnemy // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t BackAwayFromEnemy::Evaluate ( Actor &self ) { switch ( _state ) { case BAFE_SELECT_STATE: selectState( self ); break; case BAFE_BACK_AWAY: moveRandom( self ); break; case BAFE_BACK_AWAY_FAILED: return BEHAVIOR_FAILED; break; case BAFE_BACK_AWAY_SUCCESS: return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: BackAwayFromEnemy // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::End ( Actor &self ) { self.movementSubsystem->setMovingBackwards( false ); } //-------------------------------------------------------------- // Name: selectState() // Class: BackAwayFromEnemy // // Description: Selects the state for our behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::selectState( Actor &self ) { setupMoveRandom( self ); _state = BAFE_BACK_AWAY; } //-------------------------------------------------------------- // Name: setupMoveRandom() // Class: BackAwayFromEnemy() // // Description: Sets up our _moveRandom component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::setupMoveRandom( Actor &self ) { self.movementSubsystem->setMovingBackwards( true ); _moveRandom.SetMode( MoveRandomDirection::RANDOM_MOVE_IN_BACK ); _moveRandom.SetAnim( _anim ); _moveRandom.SetDistance( _dist ); _moveRandom.SetMinDistance( _minDist ); _moveRandom.Begin( self ); } //-------------------------------------------------------------- // Name: moveRandom() // Class: BackAwayFromEnemy() // // Description: Evaluates our _moveRandom component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::moveRandom( Actor &self ) { BehaviorReturnCode_t result; result = _moveRandom.Evaluate( self ); if ( result == BEHAVIOR_SUCCESS ) { _state = BAFE_BACK_AWAY_SUCCESS; self.movementSubsystem->setMovingBackwards( false ); return; } if ( result != BEHAVIOR_EVALUATING ) moveRandomFailed( self ); } //-------------------------------------------------------------- // Name: moveRandomFailed( Actor &self ) // Class: BackAwayFromEnemy // // Description: Failure Handler for move Random // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void BackAwayFromEnemy::moveRandomFailed( Actor &self ) { _state = BAFE_BACK_AWAY_FAILED; self.movementSubsystem->setMovingBackwards( false ); } //============================================================================== // AlertIdle //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, AlertIdle, NULL ) { { &EV_Behavior_Args, &AlertIdle::SetArgs }, { &EV_Behavior_AnimDone, &AlertIdle::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: AlertIdle // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void AlertIdle::SetArgs ( Event *ev) { _followAnim = ev->GetString( 1 ); _torsoAnim = ev->GetString( 2 ); _baseIdleTime = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) _emergencyDist = ev->GetFloat( 4 ); else _emergencyDist = 600.0f; if ( ev->NumArgs() > 4 ) _followDist = ev->GetFloat( 5 ); else _followDist = 176.0f; if ( ev->NumArgs() > 5 ) _wanderDist = ev->GetFloat( 6 ); else _wanderDist = 328.0f; if ( !_torsoAnim.length() ) _useTorsoAnim = false; else _useTorsoAnim = true; } //-------------------------------------------------------------- // Name: AnimDone() // Class: AlertIdle // // Description: Handles an animation completion // // Parameters: Event *ev -- Event holding the completion notification // // Returns: None //-------------------------------------------------------------- void AlertIdle::AnimDone( Event * ) { } //-------------------------------------------------------------- // Name: Begin() // Class: AlertIdle // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::Begin( Actor &self ) { _init( self ); } //-------------------------------------------------------------- // Name: _init() // Class: AlertIdle // // Description: Initializes memeber variables // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void AlertIdle::_init( Actor &self ) { // We need to make sure the actor has some form of follow target // so we'll ask our follow component to set that up. _setupFollow( self ); _nextFollowAttempt = 0.0f; _nextWanderTime = -1.0f; _self = &self; _unableToFollow = false; self.postureController->setPostureState( "STAND" , "STAND" ); _state = ALERT_IDLE_SELECT_STATE; } //-------------------------------------------------------------- // Name: Evaluate() // Class: AlertIdle // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t AlertIdle::Evaluate ( Actor &self ) { _setTorsoAnim( self ); switch ( _state ) { case ALERT_IDLE_SELECT_STATE: _selectState( self ); break; case ALERT_IDLE_IN_THE_WAY: _doGetOutOfTheWay( self ); break; case ALERT_IDLE_FOLLOW: _doFollow( self ); break; case ALERT_IDLE_WANDER: _doWander( self ); break; case ALERT_IDLE_HOLD: _hold ( self ); break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: AlertIdle // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::End ( Actor &self ) { self.SetActorFlag( ACTOR_FLAG_FOLLOWING_IN_FORMATION , false ); } //-------------------------------------------------------------- // Name: _selectState() // Class: AlertIdle // // Description: Selects the state for the behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_selectState( Actor &self ) { //Entity *followTarget; //followTarget = NULL; if ( level.time > _nextFollowAttempt ) { _setupFollow( self ); _state = ALERT_IDLE_FOLLOW; return; } _setupHold( self ); _state = ALERT_IDLE_HOLD; } //-------------------------------------------------------------- // Name: _setupGetOutOfTheWay() // Class: AlertIdle // // Description: Sets up our MoveFromConeOfFire Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_setupGetOutOfTheWay( Actor &self ) { self.SetAnim( "idle" ); if ( self.checkplayerranged() ) _getOutOfTheWay.SetAnim( "run" ); else _getOutOfTheWay.SetAnim( "walk" ); _getOutOfTheWay.Begin( self ); } //-------------------------------------------------------------- // Name: _doGetOutOfTheWay() // Class: AlertIdle // // Description: Evaluates our MoveFromConeOfFire Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_doGetOutOfTheWay( Actor &self ) { BehaviorReturnCode_t result; result = _getOutOfTheWay.Evaluate( self ); if ( result == BEHAVIOR_SUCCESS ) { self.ClearStateFlags(); _getOutOfTheWay.End( self ); _state = ALERT_IDLE_SELECT_STATE; return; } // If we got here, we caught a failure condition of some // kind if ( result != BEHAVIOR_EVALUATING ) { self.ClearStateFlags(); _getOutOfTheWay.End( self ); self.SetAnim( "idle" ); _setupGetOutOfTheWay( self ); _state = ALERT_IDLE_SELECT_STATE; } } //-------------------------------------------------------------- // Name: _setupHold() // Class: AlertIdle // // Description: Set ourselves up to stand idle // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_setupHold( Actor &self ) { self.SetAnim( "idle" ); _setNextWanderTime( self ); } //-------------------------------------------------------------- // Name: _hold() // Class: AlertIdle // // Description: Makes us hold in idle, unless we really need to // move // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_hold( Actor &self ) { if ( self.checktouchedbyplayer() ) { _state = ALERT_IDLE_IN_THE_WAY; _setupGetOutOfTheWay( self ); return; } if ( !self.WithinDistance( self.followTarget.specifiedFollowTarget , _followDist ) ) { _state = ALERT_IDLE_SELECT_STATE; return; } if ( level.time > _nextWanderTime ) { _setupWander( self ); _state = ALERT_IDLE_WANDER; return; } } //-------------------------------------------------------------- // Name: _setupFollow() // Class: AlertIdle // // Description: Sets up our Follow In Formation Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_setupFollow( Actor &self ) { _setTorsoAnim( self ); _nextWanderTime = -1.0f; //_follow.SetArgs( 192.0f, 278.0f ); _follow.SetAnim( _followAnim ); _follow.SetEmergencyDistance( _emergencyDist ); _follow.SetCatchupSpeed( 25.0f ); _follow.Begin( self ); } //-------------------------------------------------------------- // Name: _doFollow() // Class: AlertIdle // // Description: Evaluates our Follow In Formation Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_doFollow( Actor &self ) { BehaviorReturnCode_t result; if ( _checkInTheWay( self ) ) return; if ( _follow.GetState() == FollowInFormation::FOLLOW_TARGET_STATE_FOLLOW_HOLD ) { if ( _tryWander( self ) ) return; } result = _follow.Evaluate( self ); if ( result == BEHAVIOR_SUCCESS ) { self.SetAnim ( "idle" ); _follow.End( self ); _state = ALERT_IDLE_SELECT_STATE; return; } if ( result != BEHAVIOR_EVALUATING ) { //gi.WDPrintf( "%d: !!!!FAILURE!!!!!!\n" , self.entnum ); _nextFollowAttempt = level.time + 0.25f; _unableToFollow = true; _setupWander( self ); _state = ALERT_IDLE_WANDER; //_state = ALERT_IDLE_SELECT_STATE; //_setupHold( self ); //_state = ALERT_IDLE_HOLD; } else _unableToFollow = false; } //-------------------------------------------------------------- // Name: _tryWander() // Class: AlertIdle // // Description: Attempts to put us in the Wander State // // Parameters: Actor &self // // Returns: true or false //-------------------------------------------------------------- bool AlertIdle::_tryWander( Actor &self ) { if ( _nextWanderTime < 0 ) _setNextWanderTime( self ); if ( !self.WithinDistance( self.followTarget.specifiedFollowTarget , _wanderDist ) ) return false; if ( ( _nextWanderTime > 0 ) && ( level.time > _nextWanderTime ) ) { _setupWander( self ); _state = ALERT_IDLE_WANDER; return true; } return false; } //-------------------------------------------------------------- // Name: _setupWander() // Class: AlertIdle // // Description: Sets up our MoveRandomDirection Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_setupWander( Actor &self ) { _follow.End( self ); _wander.SetAnim( "walk" ); _wander.SetDistance( 128.0f ); _wander.SetMinDistance( 96.0f ); _wander.SetMode( MoveRandomDirection::RANDOM_MOVE_ANYWHERE ); _wander.Begin( self ); } //-------------------------------------------------------------- // Name: _doWander() // Class: AlertIdle // // Description: Evaluates our MoveRandomDirection Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_doWander( Actor &self ) { BehaviorReturnCode_t result; if ( _checkInTheWay( self ) ) return; result = _wander.Evaluate( self ); if ( !self.WithinDistance( self.followTarget.specifiedFollowTarget , _followDist ) && !_unableToFollow) { _state = ALERT_IDLE_SELECT_STATE; return; } if ( result != BEHAVIOR_EVALUATING ) { self.SetAnim ( "idle" ); _wander.End( self ); _setNextWanderTime( self ); _state = ALERT_IDLE_HOLD; return; } } //-------------------------------------------------------------- // Name: _setNextWanderTime() // Class: AlertIdle // // Description: Sets up our Wander Time // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_setNextWanderTime( Actor & ) { _nextWanderTime = level.time + G_Random( 3.0 ) + _baseIdleTime; } //-------------------------------------------------------------- // Name: _checkInTheWay() // Class: AlertIdle // // Description: Checks our "in the way" status and changes our // state if appropriate // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- bool AlertIdle::_checkInTheWay( Actor &self ) { if ( self.checktouchedbyplayer() ) { _state = ALERT_IDLE_IN_THE_WAY; _setupGetOutOfTheWay( self ); return true; } return false; } //-------------------------------------------------------------- // Name: _setTorsoAnim() // Class: AlertIdle // // Description: Sets our Torso Animation // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void AlertIdle::_setTorsoAnim( Actor &self ) { if ( _useTorsoAnim ) self.SetAnim( _torsoAnim , NULL , torso ); } // //============================================================================== // DoAttack //============================================================================== // //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- // //============================================================================== // DoBeamAttack //============================================================================== // // Init Static Vars const float DoBeamAttack::BEAMATTACK_SPREADFACTOR = 2.0f; //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, DoBeamAttack, NULL ) { { &EV_Behavior_Args, &DoBeamAttack::SetArgs }, { &EV_Behavior_AnimDone, &DoBeamAttack::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // // Name: SetArgs() // Class: DoBeamAttack // // Description: // // Parameters: Event *ev -- Event containing the string // // Returns: None // //-------------------------------------------------------------- void DoBeamAttack::SetArgs( Event *ev ) { tagName = ev->GetString ( 1 ); beamShader = ev->GetString ( 2 ); impactModel = ev->GetString ( 3 ); flashModel = ev->GetString ( 4 ); anim = ev->GetString ( 5 ); damage = ev->GetFloat ( 6 ); time = ev->GetFloat ( 7 ); turnspeed = ev->GetFloat ( 8 ); trackEnemy = ev->GetBoolean ( 9 ); if ( ev->NumArgs() > 9 ) beamCount = ev->GetInteger( 10 ); else beamCount = 1; if ( ev->NumArgs() > 10 ) useRotation = ev->GetBoolean( 11 ); else useRotation = false; } //-------------------------------------------------------------- // Name: AnimDone() // Class: DoBeamAttack // // Description: AnimDone Event Handler // // Parameters: Event *ev -- The AnimDone event // // Returns: None //-------------------------------------------------------------- void DoBeamAttack::AnimDone( Event * ) { if ( _state == BEAMATTACK_START_ANIM ) _state = BEAMATTACK_START_ATTACK; } //-------------------------------------------------------------- // // Name: Begin() // Class: DoBeamAttack // // Description: Initializes the behavior // // Parameters: Actor &self -- The actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void DoBeamAttack::Begin( Actor & ) { _initialRotationComplete = false; _state = BEAMATTACK_SETUP; } //-------------------------------------------------------------- // // Name: Evaluate() // Class: DoBeamAttack // // Description: Update for this behavior -- called every server frame // // Parameters: Actor &self -- Actor executing this behavior // // Returns: BehaviorReturnCode_t // //-------------------------------------------------------------- BehaviorReturnCode_t DoBeamAttack::Evaluate( Actor &self ) { // If we've finished rotating towards the enemy, and // we're supposed to track the enemy, then we need // to continue rotating to keep on target if ( _initialRotationComplete && trackEnemy ) _rotate( self ); switch ( _state ) { case BEAMATTACK_SETUP: _setupRotate( self ); break; case BEAMATTACK_ROTATE: _rotate( self ); break; case BEAMATTACK_START_ANIM: _playAttackAnim( self ); break; case BEAMATTACK_START_ATTACK: _createBeam( self ); break; case BEAMATTACK_ATTACKING: _updateBeam( self ); break; case BEAMATTACK_COMPLETE: return BEHAVIOR_SUCCESS; break; case BEAMATTACK_FAILED: return BEHAVIOR_FAILED; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // // Name: End() // Class: DoBeamAttack // // Description: Ends this behavior -- cleans things up // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None // //-------------------------------------------------------------- void DoBeamAttack::End(Actor &) { } //-------------------------------------------------------------- // Name: _setupRotate() // Class: DoBeamAttack // // Description: Sets up the Rotate Component Behavior // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void DoBeamAttack::_setupRotate( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( currentEnemy ) { _rotateBehavior.SetEntity( currentEnemy ); _rotateBehavior.SetTurnSpeed( turnspeed ); _rotateBehavior.Begin( self ); } if ( useRotation ) _state = BEAMATTACK_ROTATE; else _state = BEAMATTACK_START_ANIM; } //-------------------------------------------------------------- // Name: _rotate() // Class: DoBeamAttack // // Description: Evaluates the Rotate Component Behavior // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void DoBeamAttack::_rotate( Actor &self ) { BehaviorReturnCode_t result; result = _rotateBehavior.Evaluate( self ); if ( result == BEHAVIOR_SUCCESS ) _state = BEAMATTACK_START_ANIM; } //-------------------------------------------------------------- // Name: _playAttackAnim() // Class: DoBeamAttack // // Description: Plays the specified attack animation // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void DoBeamAttack::_playAttackAnim( Actor &self ) { self.SetAnim( anim , EV_Actor_NotifyBehavior); } void DoBeamAttack::_createBeam( Actor &self ) { Vector tagOrig; Vector dir; Vector angles; Vector spread; FuncBeam *beam; trace_t trace; Entity *currentEnemy; // Snag our current enemy currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy || !_canAttack( self ) || tagName.length() == 0 ) { _attackFailed( self ); return; } self.setOrigin(); // Get our tag position self.GetTag( tagName.c_str(), &tagOrig ); // Get the angles to our enemy //dir = currentEnemy->centroid - self.centroid; dir = currentEnemy->centroid - tagOrig; angles = dir.toAngles(); for ( int i = 1 ; i <= beamCount ; i++ ) { // Calculate our spread spread.x = G_CRandom(BEAMATTACK_SPREADFACTOR); spread.y = G_CRandom(BEAMATTACK_SPREADFACTOR); spread.z = G_CRandom(BEAMATTACK_SPREADFACTOR); angles = angles + spread; // Get our beam's end position; angles.AngleVectors( &_beamEndPos, NULL, NULL ); _beamEndPos *= dir.length(); _beamEndPos += tagOrig; // See if we hit our enemy trace = _beamAttackTrace(self , tagOrig ); if ( ( trace.fraction < 1.0f ) ) { dir = _beamEndPos - tagOrig; dir.normalize(); trace.ent->entity->Damage( &self, &self, damage, vec_zero, dir, vec_zero, 0, 0, MOD_ELECTRIC ); } beam = CreateBeam( NULL, beamShader.c_str(), tagOrig, _beamEndPos, 1, 1.5f, 0.25f ); _beamList.AddObject ( beam ); } // Add the beam _endTime = level.time + time; _state = BEAMATTACK_ATTACKING; } trace_t DoBeamAttack::_beamAttackTrace( Actor &self , const Vector &startPos ) { //G_DebugLine( startPos , _beamEndPos, 1.0f, 0.0f, 1.0f, 1.0f ); return G_Trace( startPos, Vector (-15.0f, -15.0f, -15.0f), Vector (15.0f, 15.0f, 15.0f), _beamEndPos, &self, MASK_SHOT, false, "doBeamAttack" ); } void DoBeamAttack::_updateBeam( Actor &self ) { Vector tagOrig; trace_t trace; EntityPtr beam; if ( level.time >= _endTime ) _state = BEAMATTACK_COMPLETE; self.GetTag( tagName.c_str(), &tagOrig ); for ( int i = 1 ; i <= beamCount ; i++ ) { beam = _beamList.ObjectAt( i ); if ( beam ) { beam->setOrigin( tagOrig ); self.Entity::SpawnEffect( flashModel , tagOrig , vec_zero , 0.25f ); trace = _beamAttackTrace( self , tagOrig ); if ( trace.fraction < 1.0f ) { _beamEndPos = trace.endpos; FuncBeam* _theBeam; _theBeam = (FuncBeam *)(Entity *)beam; _theBeam->SetEndPoint( _beamEndPos ); self.Entity::SpawnEffect(impactModel , _beamEndPos , vec_zero , 0.35f ); } } } } void DoBeamAttack::_attackFailed( Actor & ) { _state = BEAMATTACK_FAILED; } //-------------------------------------------------------------- // Name: _canAttack() // Class: DoBeamAttack // // Description: Checks if the actor can attack // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- bool DoBeamAttack::_canAttack( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy) return false; if ( self.combatSubsystem->CanAttackTarget( currentEnemy ) ) return true; else return false; } //============================================================================== // FireWeapon //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, FireWeapon, NULL ) { { &EV_Behavior_Args, &FireWeapon::SetArgs }, { NULL, NULL } }; FireWeapon::FireWeapon() { _target = NULL; _havePosition = false; } //-------------------------------------------------------------- // Name: SetArgs() // Class: FireWeapon // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void FireWeapon::SetArgs ( Event *ev) { _anim = ev->GetString ( 1 ); } //-------------------------------------------------------------- // Name: SetAnim() // Class: FireWeapon() // // Description: Sets the _anim // // Parameters: const str &anim -- The animation to set // // Returns: None //-------------------------------------------------------------- void FireWeapon::SetAnim(const str &anim ) { _anim = anim; } //-------------------------------------------------------------- // Name: Begin() // Class: FireWeapon // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FireWeapon::Begin( Actor &self ) { if ( _havePosition ) self.combatSubsystem->AimWeaponTag( _targetPosition ); else if ( _target ) self.combatSubsystem->AimWeaponTag(_target); self.SetAnim( _anim , EV_Actor_NotifyTorsoBehavior , torso ); } //-------------------------------------------------------------- // Name: Evaluate() // Class: FireWeapon // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t FireWeapon::Evaluate ( Actor &self ) { // no weapon? bad mojo, bail if( !self.combatSubsystem->HaveWeapon() ) return BEHAVIOR_FAILED; if ( !_target ) _target = self.enemyManager->GetCurrentEnemy(); // still no target? bail if( !_target ) return BEHAVIOR_FAILED; if ( _havePosition ) self.combatSubsystem->AimWeaponTag( _targetPosition ); else if ( _target ) self.combatSubsystem->AimWeaponTag(_target); self.combatSubsystem->AimWeaponTag(_target); //self.SetAnim( _anim , EV_Torso_Anim_Done , torso ); return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: FireWeapon // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FireWeapon::End ( Actor &self ) { _stopFire( self ); } //-------------------------------------------------------------- // Name: _stopFire() // Class: FireWeapon // // Description: Creates a stop fire event, and tells the actor to process it // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FireWeapon::_stopFire( Actor &self ) { Event *StopFireEvent; StopFireEvent = new Event( EV_Sentient_StopFire ); StopFireEvent->AddString ( "dualhand" ); self.ProcessEvent( StopFireEvent ); } void FireWeapon::SetTargetPosition( const Vector &targetPos ) { _targetPosition = targetPos; _havePosition = true; } //-------------------------------------------------------------------------- // // MetaBehaviors // //-------------------------------------------------------------------------- //============================================================================== // SimpleMelee //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, SimpleMelee, NULL ) { { &EV_Behavior_Args, &SimpleMelee::SetArgs }, { &EV_Behavior_AnimDone, &SimpleMelee::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: SimpleMelee // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void SimpleMelee::SetArgs ( Event *ev) { rushAnim = ev->GetString( 1 ); attackAnim = ev->GetString( 2 ); meleeDist = ev->GetFloat( 3 ); turnSpeed = ev->GetFloat( 4 ); } //-------------------------------------------------------------- // Name: AnimDone() // Class: SimpleMelee // // Description: Handles an animation completion // // Parameters: Event *ev -- Event holding the completion notification // // Returns: None //-------------------------------------------------------------- void SimpleMelee::AnimDone( Event *ev ) { if ( _state == SIMPLE_MELEE_ATTACK ) _attack.AnimDone( ev ); } //-------------------------------------------------------------- // Name: Begin() // Class: SimpleMelee // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void SimpleMelee::Begin( Actor &self ) { _init( self ); } //-------------------------------------------------------------- // Name: _init() // Class: SimpleMelee // // Description: Initializes memeber variables // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void SimpleMelee::_init( Actor &self ) { _self = &self; _holdTime = 0.0f; _strafeAttempts = 0; _nextStrafeTime = 0.0f; _nextEnemyCheckTime = 0.0f; _holdCount = 0; _state = SIMPLE_MELEE_SELECT_STATE; } //-------------------------------------------------------------- // Name: Evaluate() // Class: SimpleMelee // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t SimpleMelee::Evaluate ( Actor &self ) { if ( level.time > _nextEnemyCheckTime ) { self.enemyManager->FindHighestHateEnemy(); _nextEnemyCheckTime = level.time + G_Random() + 2.0f; } switch ( _state ) { case SIMPLE_MELEE_SELECT_STATE: _selectState( self ); break; case SIMPLE_MELEE_RUSH_ENEMY: _rush( self ); break; case SIMPLE_MELEE_CIRCLE_STRAFE: _strafe( self ); break; case SIMPLE_MELEE_ATTACK: _meleeAttack( self ); break; case SIMPLE_MELEE_HOLD: _hold ( self ); break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: SimpleMelee // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void SimpleMelee::End ( Actor & ) { } void SimpleMelee::SetRushAnim( const str &anim ) { rushAnim = anim; } void SimpleMelee::SetAttackAnim( const str &anim ) { attackAnim = anim; } void SimpleMelee::SetMeleeDist( float dist ) { meleeDist = dist; } void SimpleMelee::SetTurnSpeed( float turnspeed ) { turnSpeed = turnspeed; } void SimpleMelee::_setupRush( Actor &self ) { _nextStrafeTime = level.time + G_Random(.75); _rushEnemy.setAnim( rushAnim ); _rushEnemy.setDist( meleeDist ); _rushEnemy.Begin( self ); } void SimpleMelee::_rush( Actor &self ) { BehaviorReturnCode_t result; Entity *currentEnemy; result = _rushEnemy.Evaluate( self ); if ( level.time >= _nextStrafeTime ) { _setupStrafe( self ); _state = SIMPLE_MELEE_CIRCLE_STRAFE; return; } currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) { _state = SIMPLE_MELEE_SELECT_STATE; return; } if ( currentEnemy && self.WithinDistance( currentEnemy , meleeDist ) ) { _state = SIMPLE_MELEE_SELECT_STATE; return; } if ( result == BEHAVIOR_SUCCESS ) { _state = SIMPLE_MELEE_SELECT_STATE; _holdCount = 0; return; } if ( result != BEHAVIOR_EVALUATING ) { float chance; chance = G_Random(); if ( chance < .25 ) { _setupHold( self ); _state = SIMPLE_MELEE_HOLD; return; } } // If we get here, we're evaluating, which means we can clear // out our hold count; _holdCount = 0; } void SimpleMelee::_setupStrafe( Actor &self ) { _circleStrafe.SetAnim( rushAnim ); _circleStrafe.SetRadius ( meleeDist ); _circleStrafe.SetType( "enemy" ); _circleStrafe.SetTestDistance( 128.0f ); float chance; chance = G_Random(); if ( chance < .5 ) _strafeClockwise = true; else _strafeClockwise = false; _circleStrafe.SetClockwise( _strafeClockwise ); _circleStrafe.Begin( self ); } void SimpleMelee::_strafe( Actor &self ) { BehaviorReturnCode_t result; result = _circleStrafe.Evaluate( self ); if ( result == BEHAVIOR_FAILED || self.combatSubsystem->CanAttackTarget( self.enemyManager->GetCurrentEnemy() )) { _state = SIMPLE_MELEE_SELECT_STATE; return; } } void SimpleMelee::_setupMeleeAttack( Actor &self ) { _attack.SetAnim( attackAnim ); _attack.SetTurnSpeed ( turnSpeed ); _attack.SetForceAttack( true ); _attack.Begin( self ); } void SimpleMelee::_meleeAttack( Actor &self ) { BehaviorReturnCode_t result; result = _attack.Evaluate( self ); if ( result == BEHAVIOR_FAILED ) { _setupHold( self ); _state = SIMPLE_MELEE_HOLD; } if ( result == BEHAVIOR_SUCCESS ) _state = SIMPLE_MELEE_SELECT_STATE; if ( !self.combatSubsystem->CanAttackTarget( self.enemyManager->GetCurrentEnemy() ) ) { float chance; chance = G_Random(); if ( chance <= .25 ) { self.enemyManager->FindNextEnemy(); _nextEnemyCheckTime = level.time + G_Random() + 3.0f; } } } void SimpleMelee::_setupHold( Actor & ) { _holdCount++; _holdTime = level.time + G_Random(0.25f) + 0.50f; } void SimpleMelee::_hold( Actor &self ) { self.SetAnim( "idle" ); if ( level.time >= _holdTime ) _state = SIMPLE_MELEE_SELECT_STATE; } //-------------------------------------------------------------- // Name: _selectState() // Class: SimpleMelee // // Description: Selects the state for the behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void SimpleMelee::_selectState( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) self.enemyManager->FindHighestHateEnemy(); if ( !self.WithinDistance( currentEnemy , meleeDist ) ) { _state = SIMPLE_MELEE_RUSH_ENEMY; _setupRush( self ); return; } _setupMeleeAttack( self ); _state = SIMPLE_MELEE_ATTACK; return; /* if ( !self.combatSubsystem->CanShootTarget( currentEnemy ) && level.time > _nextStrafeTime ) { _setupStrafe( self ); _state = SIMPLE_MELEE_CIRCLE_STRAFE; return; } */ /* _setupHold( self ); _state = SIMPLE_MELEE_HOLD; return; */ } //============================================================================== // Patrol //============================================================================== //============================================================================== // FollowPathBlindly //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, FollowPathBlindly, NULL ) { { &EV_Behavior_Args, &FollowPathBlindly::SetArgs }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: FollowPathBlindly // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void FollowPathBlindly::SetArgs ( Event *ev ) { if ( ev->NumArgs() > 0 ) { _animName = ev->GetString( 1 ); } if ( ev->NumArgs() > 1 ) { _offset = ev->GetFloat( 2 ); } else { _offset = 0.0f; } if ( ev->NumArgs() > 2 ) { _startNodeName = ev->GetString( 3 ); } } //-------------------------------------------------------------- // Name: Begin() // Class: FollowPathBlindly // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowPathBlindly::Begin( Actor &self ) { HelperNodePtr startNode=NULL; if ( _startNodeName.length() > 0 ) { startNode = HelperNode::GetTargetedHelperNode( _startNodeName ); } else { startNode = FindNearestNode( self ); } if ( startNode != NULL ) { SetNode( self, startNode ); } _gotoHelperNode.Begin( self ); _gotoHelperNode.SetDistance( 32.0f ); _gotoHelperNode.SetAnim( _animName ); } //-------------------------------------------------------------- // Name: Evaluate() // Class: FollowPathBlindly // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t FollowPathBlindly::Evaluate ( Actor &self ) { if ( _node == NULL ) { _node = FindNearestNode( self ); if ( _node == NULL ) { return BEHAVIOR_FAILED; } } assert( _node != NULL ); BehaviorReturnCode_t moveResult = _gotoHelperNode.Evaluate( self ); if ( moveResult == BEHAVIOR_SUCCESS ) { _node->RunExitThread(); // See if we hit the end of our path HelperNodePtr lastNode = _node; if ( !AdvanceNode( self ) ) { self.SetAnim( "idle" ); return BEHAVIOR_SUCCESS; } _gotoHelperNode.SetPoint( ComputeTargetPoint( lastNode, _node, _nextNode ) ); } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: FollowPathBlindly // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void FollowPathBlindly::End ( Actor & ) { } //-------------------------------------------------------------- // Name: FindNextNode() // Class: FollowPathBlindly // // Description: Attempts to find a nearby path node // // Parameters: Actor &self // // Returns: bool - whether node was found //-------------------------------------------------------------- HelperNodePtr FollowPathBlindly::FindNextNode( Actor & ) { if ( _node != NULL) { str nextNodeTargetName = _node->target; if ( nextNodeTargetName.length() ) { return HelperNode::GetTargetedHelperNode( nextNodeTargetName ); } } return NULL; } //-------------------------------------------------------------- // Name: FindNearestNode() // Class: FollowPathBlindly // // Description: Attempts to find a nearby path node // // Parameters: Actor &self // // Returns: bool - whether node was found //-------------------------------------------------------------- HelperNodePtr FollowPathBlindly::FindNearestNode( Actor &self ) { return HelperNode::FindClosestHelperNodeWithoutPathing( self, 512.0f ); } //-------------------------------------------------------------- // Name: SetNode() // Class: FollowPathBlindly // // Description: Sets node and nextnode // // Parameters: Actor &self // // Returns: none //-------------------------------------------------------------- void FollowPathBlindly::SetNode( Actor &self, HelperNodePtr node ) { _node = node; _nextNode = FindNextNode( self ); _gotoHelperNode.SetPoint( ComputeTargetPoint( NULL, _node, _nextNode ) ); } //-------------------------------------------------------------- // Name: AdvanceNode() // Class: FollowPathBlindly // // Description: Advances _node down the waypoint path // // Parameters: Actor &self // // Returns: bool - whether node was found //-------------------------------------------------------------- const bool FollowPathBlindly::AdvanceNode( Actor &self ) { _node = _nextNode; if ( _node != NULL) { _nextNode = FindNextNode( self ); return true; } return false; } //-------------------------------------------------------------- // Name: ComputeTargetPoint() // Class: FollowPathBlindly // // Description: Adds offset into location that is move to // // Parameters: Actor &self // // Returns: bool - whether node was found //-------------------------------------------------------------- const Vector FollowPathBlindly::ComputeTargetPoint( const HelperNodePtr lastNode, HelperNodePtr const currentNode, const HelperNodePtr nextNode ) const { if ( currentNode == NULL ) { return Vector::Identity(); } if ( fSmallEnough(_offset, fEpsilon() ) ) { return ( currentNode->origin ); } Vector averagePerpendicular; Vector parallelDirection1; if ( lastNode ) { parallelDirection1 = currentNode->origin - lastNode->origin; parallelDirection1.normalize(); averagePerpendicular.CrossProduct( parallelDirection1, Vector( 0, 0, 1 ) ); } Vector parallelDirection2; if ( nextNode ) { parallelDirection2 = nextNode->origin - currentNode->origin; parallelDirection2.normalize(); Vector secondPerpendicular; secondPerpendicular.CrossProduct( parallelDirection2, Vector( 0, 0, 1 ) ); if ( lastNode ) { averagePerpendicular += secondPerpendicular; averagePerpendicular /= 2.0f; } else { averagePerpendicular = secondPerpendicular; } } if ( Vector::SmallEnough( averagePerpendicular ) ) { if ( lastNode ) { averagePerpendicular = parallelDirection1; } else { averagePerpendicular = -parallelDirection2; } } return currentNode->origin + ( averagePerpendicular * _offset ); } //============================================================================== // Hibernate //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, Hibernate, NULL ) { { &EV_Behavior_Args, &Hibernate::SetArgs }, { &EV_Behavior_AnimDone, &Hibernate::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: Hibernate // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Hibernate::SetArgs ( Event *) { } //-------------------------------------------------------------- // Name: AnimDone() // Class: Hibernate // // Description: Handles an animation completion // // Parameters: Event *ev -- Event holding the completion notification // // Returns: None //-------------------------------------------------------------- void Hibernate::AnimDone( Event * ) { switch ( _state ) { case HIBERNATE_START_HIBERNATE: _state = HIBERNATE_HIBERNATE; break; case HIBERNATE_END_HIBERNATE: _state = HIBERNATE_SUCCESSFUL; break; case HIBERNATE_WAIT: _state = HIBERNATE_SUCCESSFUL; break; } } //-------------------------------------------------------------- // Name: Begin() // Class: Hibernate // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::Begin( Actor &self ) { _init( self ); } //-------------------------------------------------------------- // Name: _init() // Class: Hibernate // // Description: Initializes memeber variables // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void Hibernate::_init( Actor &self ) { _nextMoveAttempt = 0.0f; _moveFailures = 0; _state = HIBERNATE_FIND_CLOSEST_NODE; if ( !_setupFindClosestHibernateNode( self ) ) _setupFindClosestHibernateNodeFailed( self ); } //-------------------------------------------------------------- // Name: Evaluate() // Class: Hibernate // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t Hibernate::Evaluate ( Actor &self ) { switch ( _state ) { case HIBERNATE_FIND_CLOSEST_NODE: _findClosestHibernateNode( self ); break; case HIBERNATE_MOVING_TO_NODE: _moveToHibernateNode( self ); break; case HIBERNATE_AT_NODE: _atHibernateNode( self ); break; case HIBERNATE_START_HIBERNATE: //Intentionally Empty Here break; case HIBERNATE_HIBERNATE: _hibernate( self ); break; case HIBERNATE_END_HIBERNATE: _endHibernate( self ); break; case HIBERNATE_HOLD: _hold( self ); break; case HIBERNATE_WAIT: //_wait( self ); break; case HIBERNATE_SUCCESSFUL: self.SetActorFlag(ACTOR_FLAG_IN_ALCOVE , false ); self.ignoreHelperNode.node = _node; return BEHAVIOR_SUCCESS; break; case HIBERNATE_FAILED: _endHibernate( self ); return BEHAVIOR_FAILED; break; } return BEHAVIOR_EVALUATING; } void Hibernate::_wait( Actor &self ) { self.SetAnim( "idle", EV_Actor_NotifyBehavior ); } //-------------------------------------------------------------- // Name: End() // Class: Hibernate // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::End ( Actor & ) { } //-------------------------------------------------------------- // Name: _setupFindClosestHibernateNode() // Class: Hibernate // // Description: Sets up the behavior for the Find Node State // // Parameters: Actor &self // // Returns: true or false //-------------------------------------------------------------- bool Hibernate::_setupFindClosestHibernateNode( Actor &self ) { _node = self.currentHelperNode.node; return true; } //-------------------------------------------------------------- // Name: _setupFindClosestHibernateNodeFailed() // Class: Hibernate // // Description: Failure Handler for _setupFindClosestWorkNode // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_setupFindClosestHibernateNodeFailed( Actor & ) { _state = HIBERNATE_FAILED; } //-------------------------------------------------------------- // Name: _findClosestHibernateNode() // Class: Hibernate // // Description: Finds the closest work node // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_findClosestHibernateNode( Actor &self ) { if ( !_node ) _node = HelperNode::FindClosestHelperNode( self , "hibernate" , 512.0f ); if ( !_node ) { _findClosestHibernateNodeFailed(self); return; } _state = HIBERNATE_MOVING_TO_NODE; if (!_setupMovingToHibernateNode( self ) ) _setupMovingToHibernateNodeFailed( self ); } //-------------------------------------------------------------- // Name: _findClosestHibernateNodeFailed() // Class: Hibernate // // Description: Failure Handler for _findClosestWorkNode() // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_findClosestHibernateNodeFailed( Actor & ) { _state = HIBERNATE_FAILED; } //-------------------------------------------------------------- // Name: _setupMovingToHibernateNode() // Class: Hibernate // // Description: Sets up Behavior to Move To Work Node // // Parameters: Actor &self // // Returns: true or false //-------------------------------------------------------------- bool Hibernate::_setupMovingToHibernateNode( Actor &self ) { _gotoHelperNode.SetDistance( 16.0f ); _gotoHelperNode.SetAnim( "walk" ); _gotoHelperNode.SetPoint( _node->origin ); _gotoHelperNode.Begin( self ); return true; } //-------------------------------------------------------------- // Name: _setupMovingToHibernateNodeFailed() // Class: Hibernate // // Description: Failure Handler for _setupMovingToWorkNode // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_setupMovingToHibernateNodeFailed( Actor & ) { _state = HIBERNATE_FAILED; } //-------------------------------------------------------------- // Name: _moveToHibernateNode() // Class: Hibernate // // Description: Moves to a work node // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_moveToHibernateNode( Actor &self ) { BehaviorReturnCode_t moveResult; moveResult = _gotoHelperNode.Evaluate( self ); if ( moveResult == BEHAVIOR_SUCCESS ) { _nextMoveAttempt = 0.0f; _moveFailures = 0; _state = HIBERNATE_AT_NODE; if ( !_setupAtHibernateNode( self ) ) _setupAtHibernateNodeFailed( self ); } else if ( moveResult == BEHAVIOR_FAILED ) { _moveToHibernateNodeFailed( self ); } } //-------------------------------------------------------------- // Name: _moveToHibernateNodeFailed() // Class: Hibernate // // Description: Failure Handler for _moveToWorkNode() // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_moveToHibernateNodeFailed( Actor &self ) { _moveFailures++; if ( _moveFailures > 10 ) { _state = HIBERNATE_FAILED; } else { _state = HIBERNATE_HOLD; if ( !_setupHold( self ) ) _setupHoldFailed( self ); } } //-------------------------------------------------------------- // Name: _setupAtHibernateNode() // Class: Hibernate // // Description: Sets up behavior for AtWorkNode // // Parameters: Actor &self // // Returns: true or false //-------------------------------------------------------------- bool Hibernate::_setupAtHibernateNode( Actor &self ) { Vector nodeAngles; nodeAngles = _node->angles; self.movementSubsystem->setAnimDir( nodeAngles ); self.setAngles( nodeAngles ); return true; } //-------------------------------------------------------------- // Name: _setupAtHibernateNodeFailed() // Class: Hibernate // // Description: Failure Handler for _setupAtWork() // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_setupAtHibernateNodeFailed( Actor & ) { _state = HIBERNATE_FAILED; } //-------------------------------------------------------------- // Name: _atHibernateNode() // Class: Hibernate // // Description: Determines how to work // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_atHibernateNode( Actor &self ) { _node->RunEntryThread(); _state = HIBERNATE_START_HIBERNATE; //Snap ourselves into place self.setOrigin( _node->origin ); _startHibernate( self ); } //-------------------------------------------------------------- // Name: _setupHold() // Class: Hibernate // // Description: Sets up behavior to hold // // Parameters: Actor &self // // Returns: true or false //-------------------------------------------------------------- bool Hibernate::_setupHold( Actor &self ) { self.SetAnim( "idle" ); _nextMoveAttempt = level.time + 0.5f + G_Random(); return true; } //-------------------------------------------------------------- // Name: _setupHoldFailed() // Class: Hibernate // // Description: Failure Handler for _setupHold() // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_setupHoldFailed( Actor & ) { _state = HIBERNATE_FAILED; } //-------------------------------------------------------------- // Name: _hold() // Class: Hibernate // // Description: Holds the Actor in place // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void Hibernate::_hold( Actor &self ) { if ( level.time > _nextMoveAttempt ) { _state = HIBERNATE_MOVING_TO_NODE; if ( !_setupMovingToHibernateNode( self ) ) _setupMovingToHibernateNodeFailed( self ); } } void Hibernate::_startHibernate( Actor &self ) { self.SetAnim( "alcove_deactivate", EV_Actor_NotifyBehavior ); } void Hibernate::_hibernate( Actor &self ) { float tendencyToHibernate = self.personality->GetTendency( "hibernate" ); if ( G_Random() > tendencyToHibernate ) { _state = HIBERNATE_END_HIBERNATE; } self.SetAnim( "alcove_idle", EV_Actor_NotifyBehavior ); } void Hibernate::_endHibernate( Actor &self ) { self.SetAnim( "alcove_activate", EV_Actor_NotifyBehavior ); _state = HIBERNATE_WAIT; } //============================================================================== // GotoLiftPosition //============================================================================== //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, GotoLiftPosition, NULL ) { { &EV_Behavior_Args, &GotoLiftPosition::SetArgs }, { &EV_Behavior_AnimDone, &GotoLiftPosition::AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: GotoLiftPosition // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::SetArgs ( Event * ) { } //-------------------------------------------------------------- // Name: AnimDone() // Class: GotoLiftPosition // // Description: Handles an animation completion // // Parameters: Event *ev -- Event holding the completion notification // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::AnimDone( Event * ) { } //-------------------------------------------------------------- // Name: Begin() // Class: GotoLiftPosition // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::Begin( Actor &self ) { _init( self ); } //-------------------------------------------------------------- // Name: _init() // Class: GotoLiftPosition // // Description: Initializes memeber variables // // Parameters: Actor &self -- Actor executing this behavior // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_init( Actor &self ) { _nextMoveAttempt = 0.0f; _moveFailures = 0; _node = NULL; FindNodes( self ); _state = GLP_FIND_NODE; } //-------------------------------------------------------------- // Name: Evaluate() // Class: GotoLiftPosition // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: BehaviorReturnCode_t Return Code //-------------------------------------------------------------- BehaviorReturnCode_t GotoLiftPosition::Evaluate ( Actor &self ) { switch ( _state ) { case GLP_FIND_NODE: _findLiftNode( self ); break; case GLP_MOVING_TO_NODE: _moveToLiftNode( self ); break; case GLP_MOVE_FAILED: _moveToLiftNodeFailed( self ); break; case GLP_AT_NODE: _atLiftNode( self ); break; case GLP_SUCCESSFUL: return BEHAVIOR_SUCCESS; break; case GLP_FAILED: return BEHAVIOR_FAILED; break; } return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: GotoLiftPosition // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::End ( Actor & ) { if ( _node ) _node->UnreserveNode(); _availableNodes.ClearObjectList(); _attemptedNodes.ClearObjectList(); } //-------------------------------------------------------------- // Name: _findLiftNode() // Class: GotoLiftPosition // // Description: Finds a good lift node position, based on priority // and the currentCallVolume on the player // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_findLiftNode( Actor &self ) { Player *player; player = GetPlayer( 0 ); if ( !player ) { _state = GLP_FAILED; return; } //Search Through available nodes, and make sure that we haven't put it in our attempted list HelperNode* theNode; HelperNode* checkNode; HelperNode* liftNode; bool alreadyTriedNode; int i, j; liftNode = NULL; for( i = 1 ; i <= _availableNodes.NumObjects(); i++ ) { theNode = _availableNodes.ObjectAt(i); //First check if its in our attempted list alreadyTriedNode = false; for ( j = 1 ; j <= _attemptedNodes.NumObjects() ; j++ ) { checkNode = _attemptedNodes.ObjectAt(j); if ( checkNode == theNode ) { alreadyTriedNode = true; break; } } if ( alreadyTriedNode ) continue; liftNode = theNode; } if ( !liftNode ) { _state = GLP_FAILED; return; } _node = liftNode; _node->ReserveNode(); _setupMovingToLiftNode( self ); _state = GLP_MOVING_TO_NODE; } //-------------------------------------------------------------- // Name: _setupMovingToLiftNode() // Class: GotoLiftPosition // // Description: Sets up our GotoPoint Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_setupMovingToLiftNode( Actor &self ) { _gotoHelperNode.SetAnim( "run" ); _gotoHelperNode.SetDistance( 16.0f ); _gotoHelperNode.SetPoint( _node->origin ); _gotoHelperNode.Begin( self ); } //-------------------------------------------------------------- // Name: _moveToLiftNode() // Class: GotoLiftPosition // // Description: Evaluates our MoveToPoint Component // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_moveToLiftNode( Actor &self ) { BehaviorReturnCode_t result; result = _gotoHelperNode.Evaluate( self ); if ( result == BEHAVIOR_FAILED ) { _setupMoveToLiftFailed( self ); _state = GLP_MOVE_FAILED; return; } if ( result == BEHAVIOR_SUCCESS ) { _setupAtLiftNode( self ); _state = GLP_AT_NODE; return; } if ( result == BEHAVIOR_FAILED_STEERING_NO_PATH ) { if ( !self.GetActorFlag(ACTOR_FLAG_IN_CALL_VOLUME) ) { _setupMoveToLiftFailed(self); _state = GLP_MOVE_FAILED; } } } //-------------------------------------------------------------- // Name: _setupMoveToLiftFailed() // Class: GotoLiftPosition // // Description: Failure Handler // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_setupMoveToLiftFailed( Actor &self ) { self.SetAnim( "idle" ); _moveFailures++; _nextMoveAttempt = level.time + G_Random() + 0.5f; } //-------------------------------------------------------------- // Name: _moveToLiftNodeFailed() // Class: GotoLiftPosition // // Description: Depending on the failure count, will either try warping // the actor to a convient pathnode, or warping it // all the way to the position // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_moveToLiftNodeFailed( Actor &self ) { if ( level.time > _nextMoveAttempt && !self.GetActorFlag(ACTOR_FLAG_IN_CALL_VOLUME) ) { /*if ( _moveFailures > 5 ) _warpToLiftNode( self ); else if ( _moveFailures > 3 ) _warpToPathNode( self ); */ _moveFailures++; if ( _moveFailures > 5 ) { _attemptedNodes.AddObject(_node); _findLiftNode(self); _setupMovingToLiftNode( self ); _state = GLP_MOVING_TO_NODE; } } } //-------------------------------------------------------------- // Name: _setupAtLiftNode() // Class: GotoLiftPosition // // Description: Sets us in the idle animation // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_setupAtLiftNode( Actor &self ) { self.SetAnim( "idle" ); _node->RunEntryThread(); } //-------------------------------------------------------------- // Name: _atLiftNode() // Class: GotoLiftPosition // // Description: Holds us in position until the player // is no longer in the call volume // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_atLiftNode( Actor &self ) { //Check Player Range for Sanity as sometimes the trigger call volume fails to //catch the player leaving -- It sucks, but it happens, this is bit of jazz here //is for bullet proofing. if ( self.checkSpecifiedFollowTargetOutOfRange() ) self.SetActorFlag( ACTOR_FLAG_PLAYER_IN_CALL_VOLUME, false ); if ( !self.GetActorFlag( ACTOR_FLAG_PLAYER_IN_CALL_VOLUME ) ) _state = GLP_SUCCESSFUL; } //-------------------------------------------------------------- // Name: _warpToLift // Class: GotoLiftPosition // // Description: Sets our origin to the lift node's origin. // We will likely need to modify this in the future // to do a trace and verify that the position is // clear // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_warpToLiftNode( Actor &self ) { self.setOrigin( _node->origin ); _setupAtLiftNode( self ); _state = GLP_AT_NODE; } //-------------------------------------------------------------- // Name: _warpToPathNode() // Class: GotoLiftPosition // // Description: Sets our origin to the pathnode nearest to us. // Hopefully this will be enough to all pathfinding // to find a good route // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void GotoLiftPosition::_warpToPathNode( Actor &self ) { // Find the path node nearest to us PathNode *goalNode = thePathManager.NearestNode( self.origin ); if ( !goalNode ) _warpToLiftNode( self ); } void GotoLiftPosition::FindNodes(Actor &self ) { Player* player = GetPlayer(0); str volumeName = player->GetCurrentCallVolume(); HelperNode* node; int nodeID; for ( int i = 1 ; i <= HelperNodes.NumObjects() ; i++ ) { node = NULL; node = HelperNodes.ObjectAt( i ); nodeID = node->GetID(); if ( nodeID != -1 && nodeID != self.currentHelperNode.nodeID ) continue; if ( node->isReserved() ) continue; if ( node->isOfType(NODETYPE_CUSTOM)) { str type; type = node->GetCustomType(); if ( !stricmp(type.c_str() , "lift" ) && !stricmp(node->target, volumeName.c_str() ) ) { _availableNodes.AddObject(node); } } } } /* //============================================================================== // Template //============================================================================== //-------------------------------------------------------------- // // Init Static Vars // //-------------------------------------------------------------- const float MoveFromConeOfFire::THE_CONSTANT = 500.0f; //-------------------------------------------------------------- // // Class Declaration and Event Registration // //-------------------------------------------------------------- CLASS_DECLARATION( Behavior, Template, NULL ) { { &EV_Behavior_Args, SetArgs }, { &EV_Behavior_AnimDone, AnimDone }, { NULL, NULL } }; //-------------------------------------------------------------- // Name: SetArgs() // Class: Template // // Description: Sets the arguments of the behavior // // Parameters: Event *ev // // Returns: None //-------------------------------------------------------------- void Template::SetArgs ( Event *ev) { } //-------------------------------------------------------------- // Name: Begin() // Class: Template // // Description: Begins the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::Begin( Actor &self ) { } //-------------------------------------------------------------- // Name: Evaluate() // Class: Template // // Description: Returns True Or False, and is run every frame // that the behavior // // Parameters: Actor &self // // Returns: True or False //-------------------------------------------------------------- BehaviorReturnCode_t Template::Evaluate ( Actor &self ) { return BEHAVIOR_EVALUATING; } //-------------------------------------------------------------- // Name: End() // Class: Template // // Description: Ends the Behavior // // Parameters: Actor &self // // Returns: None //-------------------------------------------------------------- void MoveFromConeOfFire::End ( Actor &self ) { } */