//----------------------------------------------------------------------------- // // $Logfile:: /Code/DLLs/game/behavior.cpp $ // $Revision:: 135 $ // $Author:: Steven $ // $Date:: 10/13/03 9:11a $ // // Copyright (C) 1998 by Ritual Entertainment, Inc. // All rights reserved. // // This source may not be distributed and/or modified without // expressly written permission by Ritual Entertainment, Inc. // // DESCRIPTION: // Behaviors used by the AI. // #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" Event EV_Behavior_Args ( "args", EV_CODEONLY, "SSSSSS", "token1 token2 token3 token4 token5 token6", "Gives the current behavior arguments." ); Event EV_Behavior_AnimDone ( "animdone", EV_CODEONLY, NULL, NULL, "Tells the behavior that the current animation is done, " "it is not meant to be called from script." ); Event EV_PostureChanged_Completed ( "posturechangecompleted", EV_CODEONLY, NULL, NULL, "Tells the behavior that the requested posture is active" ); Vector ChooseRandomDirection( Actor &self, const Vector &previousdir, float *time, int disallowcontentsmask, qboolean usepitch ); /**************************************************************************** Behavior Class Definition ****************************************************************************/ CLASS_DECLARATION( Listener, Behavior, NULL ) { { NULL, NULL } }; Behavior::Behavior() : _controller( 0 ) // NULL { _self = NULL; } void Behavior::ShowInfo ( Actor &self ) { if ( movegoal ) { gi.Printf( "movegoal: ( %f, %f, %f ) - '%s'\n", movegoal->origin.x, movegoal->origin.y, movegoal->origin.z, movegoal->targetname.c_str() ); } else { gi.Printf( "movegoal: NULL\n" ); } } void Behavior::Begin ( Actor &self ) { } BehaviorReturnCode_t Behavior::Evaluate ( Actor &self ) { return BEHAVIOR_SUCCESS; } void Behavior::End ( Actor &self ) { } /**************************************************************************** ***************************************************************************** General behaviors ***************************************************************************** ****************************************************************************/ /**************************************************************************** Idle Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Idle, NULL ) { { NULL, NULL } }; void Idle::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nnexttwitch : %f\n", nexttwitch ); } void Idle::Begin ( Actor &self ) { nexttwitch = level.time + 10.0f + G_Random( 30.0f ); self.SetAnim( "idle", EV_Actor_NotifyBehavior ); } BehaviorReturnCode_t Idle::Evaluate ( Actor &self ) { if ( self.enemyManager->GetCurrentEnemy() ) { return BEHAVIOR_EVALUATING; } if ( nexttwitch < level.time ) { self.chattime += 10.0f; self.AddStateFlag( STATE_FLAG_TWITCH ); return BEHAVIOR_EVALUATING; } else { //self.Chatter( "snd_idle", 1 ); } return BEHAVIOR_EVALUATING; } void Idle::End ( Actor &self ) { } /**************************************************************************** Pain Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Pain, NULL ) { { &EV_Behavior_AnimDone, &Pain::AnimDone }, { NULL, NULL } }; void Pain::SetPainAnim ( Actor &self, int new_pain_type, int new_anim_number ) { str anim_name; // Determine which pain type to play if ( new_pain_type == PAIN_SMALL ) anim_name = "pain_small"; else anim_name = "pain"; // Try to find the correct anim to play anim_name += new_anim_number; if ( !self.animate->HasAnim( anim_name.c_str() ) ) { if ( new_pain_type == PAIN_SMALL ) anim_name = "pain_small1"; else anim_name = "pain1"; pain_anim_number = 1; } anim_done = false; // Play the animation if we can if ( !self.animate->HasAnim( anim_name.c_str() ) ) anim_done = true; else self.SetAnim( anim_name.c_str(), EV_Actor_NotifyBehavior ); } int Pain::GetNumberOfPainAnims ( Actor &self, int new_pain_type ) { str anim_name; str real_anim_name; int i; // Determine base animation name if ( new_pain_type == PAIN_SMALL ) anim_name = "pain_small"; else anim_name = "pain"; // Find how many pain animations we have for( i = 1 ; i < 10 ; i++ ) { real_anim_name = anim_name + i; if ( !self.animate->HasAnim( real_anim_name.c_str() ) ) return( i - 1 ); } return 9; } void Pain::Begin ( Actor &self ) { str anim_name; int num_pain_anims; num_pain_anims = GetNumberOfPainAnims( self, self.pain_type ); pain_anim_number = (int)G_Random( num_pain_anims ) + 1; // Figure out which type of pain to do if ( self.pain_type == PAIN_SMALL ) SetPainAnim( self, PAIN_SMALL, pain_anim_number ); else SetPainAnim( self, PAIN_BIG, pain_anim_number ); current_pain_type = self.pain_type; number_of_pains = 1; // Make sure we don't have pain any more self.state_flags &= ~STATE_FLAG_SMALL_PAIN; self.state_flags &= ~STATE_FLAG_IN_PAIN; max_pains = (int)G_Random( 4 ) + 4; } void Pain::AnimDone ( Event *ev ) { anim_done = true; } BehaviorReturnCode_t Pain::Evaluate ( Actor &self ) { str anim_name; if ( self.state_flags & STATE_FLAG_SMALL_PAIN ) { // See if we should play another pain animation if ( ( self.means_of_death != MOD_FIRE ) && ( self.means_of_death != MOD_ON_FIRE ) && ( self.means_of_death != MOD_FIRE_BLOCKABLE ) ) { if ( ( self.pain_type == PAIN_SMALL ) && ( current_pain_type == PAIN_SMALL ) && ( number_of_pains < max_pains ) ) { pain_anim_number++; number_of_pains++; SetPainAnim( self, PAIN_SMALL, pain_anim_number ); } else if ( self.pain_type == PAIN_BIG ) { pain_anim_number++; current_pain_type = PAIN_BIG; SetPainAnim( self, PAIN_BIG, pain_anim_number ); } } // Reset pain stuff self.state_flags &= ~STATE_FLAG_SMALL_PAIN; self.state_flags &= ~STATE_FLAG_IN_PAIN; } // If the pain animation has finished, then we are done if ( anim_done ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void Pain::End ( Actor &self ) { self.bullet_hits = 0; } /**************************************************************************** Watch Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Watch, NULL ) { { &EV_Behavior_Args, &Watch::SetArgs }, { NULL, NULL } }; Watch::Watch() { turn_speed = 360.0f; old_turn_speed = 0.0f; } void Watch::SetArgs ( Event *ev ) { turn_speed = ev->GetFloat( 1 ); } void Watch::Begin ( Actor &self ) { ent_to_watch = self.enemyManager->GetCurrentEnemy(); old_turn_speed = self.movementSubsystem->getTurnSpeed(); self.movementSubsystem->setTurnSpeed( turn_speed ); Vector test; Vector testAng; test = self.movementSubsystem->getMoveDir(); testAng = test.toAngles(); } BehaviorReturnCode_t Watch::Evaluate ( Actor &self ) { Vector dir; Vector testAng; dir = self.movementSubsystem->getMoveDir(); testAng = dir.toAngles(); if ( !ent_to_watch ) return BEHAVIOR_SUCCESS; if ( self.IsEntityAlive( ent_to_watch ) ) { dir = self.movementSubsystem->getMoveDir(); testAng = dir.toAngles(); self.movementSubsystem->Accelerate( self.movementSubsystem->SteerTowardsPoint(ent_to_watch->centroid, vec_zero, dir, 1.0f) ); } return BEHAVIOR_EVALUATING; } void Watch::End ( Actor &self ) { self.movementSubsystem->setTurnSpeed( old_turn_speed ); } /**************************************************************************** Turn Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Turn, NULL ) { { &EV_Behavior_Args, &Turn::SetArgs }, { NULL, NULL } }; Turn::Turn() { turn_speed = 5; } void Turn::SetArgs ( Event *ev ) { turn_speed = ev->GetFloat( 1 ); } void Turn::Begin ( Actor &self ) { } BehaviorReturnCode_t Turn::Evaluate ( Actor &self ) { self.angles[YAW] += turn_speed; self.setAngles( self.angles ); return BEHAVIOR_EVALUATING; } void Turn::End ( Actor &self ) { } /**************************************************************************** Aim Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Aim, NULL ) { { NULL, NULL } }; void Aim::SetTarget ( Entity *ent ) { target = ent; } void Aim::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nseek:\n" ); if ( target ) { gi.Printf( "\ntarget : #%d '%s'\n", target->entnum, target->targetname.c_str() ); } else { gi.Printf( "\ntarget : NULL\n" ); } } void Aim::Begin ( Actor &self ) { } BehaviorReturnCode_t Aim::Evaluate ( Actor &self ) { Vector dir; Vector ang; Vector pos; if ( !target ) { return BEHAVIOR_SUCCESS; } // // get my gun pos // // Fixme ? //pos = self.GunPosition(); //ang = self.MyGunAngles( pos, false ); pos = self.centroid; ang = self.angles; ang.AngleVectors( &dir, NULL, NULL ); Vector newSteeringDirection=self.movementSubsystem->SteerTowardsPoint(target->centroid, target->velocity, dir, 1400 + ( skill->value * 600 )); self.movementSubsystem->Accelerate( newSteeringDirection ); //self.Accelerate( seek.steeringforce ); if ( newSteeringDirection.y < 0.25f ) { // dead on return BEHAVIOR_SUCCESS; } return BEHAVIOR_EVALUATING; } void Aim::End ( Actor &self ) { } /**************************************************************************** TurnTo Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TurnTo, NULL ) { { &EV_Behavior_AnimDone, &TurnTo::AnimDone }, { NULL, NULL } }; TurnTo::TurnTo() { dir = Vector( 1.0f, 0.0f, 0.0f ); mode = 0; ent = NULL; yaw = 0; anim_done = true; useTurnAnim = true; _useAnims = true; } void TurnTo::SetDirection ( float new_yaw ) { Vector ang; ang = Vector( 0.0f, new_yaw, 0.0f ); yaw = anglemod( new_yaw ); ang.AngleVectors( &dir, NULL, NULL ); dir *= 100.0f; mode = 1; } void TurnTo::useAnims( bool useAnims ) { _useAnims = useAnims; } void TurnTo::SetTarget ( Entity *target_ent ) { ent = target_ent; mode = 2; } void TurnTo::AnimDone ( Event *ev ) { anim_done = true; } void TurnTo::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nseek:\n" ); if ( ent ) { gi.Printf( "\nent: #%d '%s'\n", ent->entnum, ent->targetname.c_str() ); } else { gi.Printf( "\nent: NULL\n" ); } gi.Printf( "dir: ( %f, %f, %f )\n", dir.x, dir.y, dir.z ); gi.Printf( "yaw: %f\n", yaw ); gi.Printf( "mode: %d\n", mode ); } void TurnTo::Begin( Actor &self ) { extraFrames = 0; if ( _useAnims ) { self.SetAnim( "idle" ); } } BehaviorReturnCode_t TurnTo::Evaluate( Actor &self ) { Vector delta; float ang; str anim_name; Vector targetPosition; Vector targetVelocity; if ( anim_done ) { if ( _useAnims ) { self.SetAnim( "idle" ); } anim_done = false; } switch( mode ) { case 1 : ang = angledist( yaw - self.angles.yaw() ); if ( fabs( ang ) < 1 ) { self.movementSubsystem->Accelerate( Vector( 0.0f, ang, 0.0f ) ); // If turned all the way wait for animation to finish if ( anim_done || ( self.animname == "idle" ) || !_useAnims ) return BEHAVIOR_SUCCESS; else return BEHAVIOR_EVALUATING; } targetPosition = self.origin + dir ; targetVelocity = vec_zero ; break; case 2 : if ( !ent ) { return BEHAVIOR_SUCCESS; } delta = ent->origin - self.origin; yaw = delta.toYaw(); targetPosition = ent->origin; targetVelocity = vec_zero; break; default : return BEHAVIOR_SUCCESS; } Vector newSteeringForce = self.movementSubsystem->SteerTowardsPoint( targetPosition, targetVelocity, self.movementSubsystem->getMoveDir(), self.movementSubsystem->getMoveSpeed() ); if ( _useAnims ) { if ( extraFrames < 1 ) { if ( useTurnAnim ) { if ( newSteeringForce[YAW] > 1.0f ) { anim_name = "turn_left"; anim_done = false; } else if ( newSteeringForce[YAW] < -1.0f ) { anim_name = "turn_right"; anim_done = false; } else if ( anim_done ) { anim_name = "idle"; } else { anim_name = self.animname; } } else { anim_name = "idle"; } if ( gi.Anim_NumForName( self.edict->s.modelindex, anim_name.c_str() ) != -1 ) { if ( anim_name != self.animname ) self.SetAnim( anim_name.c_str(), EV_Actor_NotifyBehavior ); } else { anim_done = true; } } } extraFrames++; if ( extraFrames > 4 || anim_name == "idle" ) { extraFrames = 5; self.movementSubsystem->Accelerate( newSteeringForce ); } return BEHAVIOR_EVALUATING; } void TurnTo::End( Actor &self ) { if ( _useAnims ) { self.SetAnim( "idle" ); } self.movementSubsystem->setMoveSpeed( 1.0 ); } void TurnTo::SetUseTurnAnim( bool useAnim ) { useTurnAnim = useAnim; } /**************************************************************************** RotateToEnemy Class Definition -- Primarily a convience wrapper for TurnTo ****************************************************************************/ CLASS_DECLARATION( Behavior, RotateToEnemy, NULL ) { { &EV_Behavior_Args, &RotateToEnemy::SetArgs }, { NULL, NULL } }; void RotateToEnemy::SetArgs ( Event *ev ) { if (ev->NumArgs() > 0 ) anim = ev->GetString(1); else anim = "idle"; if (ev->NumArgs() > 1 ) turnSpeed = ev->GetFloat(2); else turnSpeed = 10; } void RotateToEnemy::Begin ( Actor &self ) { } BehaviorReturnCode_t RotateToEnemy::Evaluate ( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; Vector dir = currentEnemy->origin - self.origin; float yaw_diff = AngleNormalize360( self.origin.toYaw() - dir.toYaw() ); if (yaw_diff < 180.0f ) self.angles[YAW]+=turnSpeed; if (yaw_diff > 180.0f ) self.angles[YAW]-=turnSpeed; self.setAngles(self.angles); if (self.sensoryPerception->InFOV(currentEnemy) ) return BEHAVIOR_SUCCESS; else return BEHAVIOR_EVALUATING; } void RotateToEnemy::End ( Actor &self ) { } /**************************************************************************** HeadWatch Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HeadWatch, NULL ) { { &EV_Behavior_Args, &HeadWatch::SetArgs }, { NULL, NULL } }; HeadWatch::HeadWatch() { max_speed = 15; forever = true; usingEyes = false; } void HeadWatch::SetArgs ( Event *ev ) { ent_to_watch = ev->GetEntity( 1 ); if ( ev->NumArgs() > 1 ) max_speed = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) forever = ev->GetBoolean( 3 ); } void HeadWatch::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void HeadWatch::Begin ( Actor &self ) { self.SetControllerTag( ACTOR_HEAD_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ) ); current_head_angles = self.GetControllerAngles( ACTOR_HEAD_TAG ); self.SetActorFlag( ACTOR_FLAG_TURNING_HEAD, true ); } BehaviorReturnCode_t HeadWatch::Evaluate ( Actor &self ) { Vector dir; Vector angles; int tag_num; Vector tag_pos; Vector watch_tag_pos; Vector angles_diff; Vector watch_position; Actor *act = NULL; Vector change; if ( ent_to_watch ) { tag_num = gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) return BEHAVIOR_SUCCESS; self.GetTag( "Bip01 Head", &tag_pos ); if ( ent_to_watch->isSubclassOf( Actor ) ) act = (Actor *)(Entity *)ent_to_watch; if ( act && ( act->watch_offset != vec_zero ) ) { MatrixTransformVector( act->watch_offset, ent_to_watch->orientation, watch_position ); watch_position += ent_to_watch->origin; } else { tag_num = gi.Tag_NumForName( ent_to_watch->edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) watch_position = ent_to_watch->centroid; else { ent_to_watch->GetTag( "Bip01 Head", &watch_tag_pos ); watch_position = watch_tag_pos; } } dir = watch_position - tag_pos; angles = dir.toAngles(); angles_diff = angles - self.angles; } else { angles_diff = vec_zero; } angles_diff[YAW] = AngleNormalize180( angles_diff[YAW] ); angles_diff[PITCH] = AngleNormalize180( angles_diff[PITCH] ); if (usingEyes) { if (angles_diff[YAW] > (self.minEyeYawAngle + (self.minEyeYawAngle * .5f )) && angles_diff[YAW] < (self.maxEyeYawAngle + (self.maxEyeYawAngle * .5f ))) angles_diff[YAW] = 0.0f; if (angles_diff[PITCH] > self.minEyePitchAngle && angles_diff[PITCH] < self.maxEyePitchAngle ) angles_diff[PITCH] = 0.0f; } // Make sure we don't turn neck too far if ( angles_diff[YAW] < -80.0f ) angles_diff[YAW] = -80.0f; else if ( angles_diff[YAW] > 80.0f ) angles_diff[YAW] = 80.0f; if ( angles_diff[PITCH] < -45.0f ) angles_diff[PITCH] = -45.0f; else if ( angles_diff[PITCH] > 45.0f ) angles_diff[PITCH] = 45.0f; angles_diff[ROLL] = 0.0f; // Make sure we don't change our head angles too much at once change = angles_diff - current_head_angles; float absYaw = fabs( change[ YAW ] ); float absPitch = fabs( change[ PITCH ] ); if( ( absYaw < fEpsilon() ) && ( absPitch < fEpsilon() ) ) { if( forever ) { self.SetControllerAngles( ACTOR_HEAD_TAG, current_head_angles ); return BEHAVIOR_EVALUATING; } else return BEHAVIOR_SUCCESS; } //change = vec_zero; bool isFinished = true; if( absYaw >= absPitch ) { float ratioPitchToYaw = change[ PITCH ] / change[ YAW ]; if( change[ YAW ] > max_speed ) { change[ YAW ] = max_speed; isFinished = false; } else if( change[ YAW ] < -max_speed ) { change[ YAW ] = -max_speed; isFinished = false; } change[ PITCH ] = change[ YAW ] * ratioPitchToYaw; } else if( absPitch > absYaw ) { float ratioYawToPitch = change[ YAW ] / change[ PITCH ]; if( change[ PITCH ] > max_speed ) { change[ PITCH ] = max_speed; isFinished = false; } else if( change[ PITCH ] < -max_speed ) { change[ PITCH ] = -max_speed; isFinished = false; } change[ YAW ] = change[ PITCH ] * ratioYawToPitch; } angles_diff = current_head_angles + change; angles_diff[ ROLL ] = 0.0f; /* if ( change[YAW] > max_speed ) angles_diff[YAW] = current_head_angles[YAW] + max_speed; else if ( change[YAW] < -max_speed ) angles_diff[YAW] = current_head_angles[YAW] - max_speed; if ( change[PITCH] > max_speed ) angles_diff[PITCH] = current_head_angles[PITCH] + max_speed; else if ( change[PITCH] < -max_speed ) angles_diff[PITCH] = current_head_angles[PITCH] - max_speed; */ self.SetControllerAngles( ACTOR_HEAD_TAG, angles_diff ); self.real_head_pitch = angles_diff[PITCH]; current_head_angles = angles_diff; if ( !ent_to_watch && ( current_head_angles == vec_zero ) ) return BEHAVIOR_SUCCESS; // if ( !forever && ( change[YAW] < max_speed ) && ( change[YAW] > -max_speed ) && ( change[PITCH] < max_speed ) && ( change[PITCH] > -max_speed ) ) if( !forever && isFinished ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void HeadWatch::useEyes ( qboolean moveEyes ) { usingEyes = moveEyes; } void HeadWatch::End ( Actor &self ) { // Snap head back into position if we have lost our target or we are doing a resethead self.SetControllerAngles( ACTOR_HEAD_TAG, vec_zero ); self.real_head_pitch = 0; self.SetActorFlag( ACTOR_FLAG_TURNING_HEAD, false ); } /**************************************************************************** HeadWatchEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HeadWatchEnemy, NULL ) { { &EV_Behavior_Args, &HeadWatchEnemy::SetArgs }, { NULL, NULL } }; HeadWatchEnemy::HeadWatchEnemy() { max_speed = 20; forever = true; usingEyes = false; threshold = 0; } void HeadWatchEnemy::SetArgs ( Event *ev ) { max_speed = ev->GetFloat( 1 ); if ( ev->NumArgs() > 1 ) forever = ev->GetBoolean( 2 ); if ( ev->NumArgs() > 2 ) threshold = ev->GetFloat( 3 ); } void HeadWatchEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void HeadWatchEnemy::Begin ( Actor &self ) { self.SetControllerTag( ACTOR_HEAD_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ) ); current_head_angles = self.GetControllerAngles( ACTOR_HEAD_TAG ); self.SetActorFlag( ACTOR_FLAG_TURNING_HEAD, true ); ent_to_watch = self.enemyManager->GetCurrentEnemy(); } BehaviorReturnCode_t HeadWatchEnemy::Evaluate ( Actor &self ) { Vector dir; Vector angles; int tag_num; Vector tag_pos; Vector watch_tag_pos; Vector angles_diff; Vector watch_position; Actor *act = NULL; Vector change; // Get our Torso Angles self.SetControllerTag( ACTOR_TORSO_TAG , gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Spine1" ) ); current_torso_angles = self.GetControllerAngles( ACTOR_TORSO_TAG ); //Reset our Controller Tag self.SetControllerTag( ACTOR_HEAD_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ) ); if ( !ent_to_watch ) { ent_to_watch = self.enemyManager->GetCurrentEnemy(); } if ( ent_to_watch ) { tag_num = gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) return BEHAVIOR_SUCCESS; self.GetTag( "Bip01 Head", &tag_pos ); if ( ent_to_watch->isSubclassOf( Actor ) ) act = (Actor *)(Entity *)ent_to_watch; if ( act && ( act->watch_offset != vec_zero ) ) { MatrixTransformVector( act->watch_offset, ent_to_watch->orientation, watch_position ); watch_position += ent_to_watch->origin; } else { tag_num = gi.Tag_NumForName( ent_to_watch->edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) watch_position = ent_to_watch->centroid; else { ent_to_watch->GetTag( "Bip01 Head", &watch_tag_pos ); watch_position = watch_tag_pos; } } dir = watch_position - tag_pos; angles = dir.toAngles(); angles_diff = angles - self.angles; } else { angles_diff = vec_zero; } angles_diff[YAW] = AngleNormalize180( angles_diff[YAW] ); angles_diff[PITCH] = AngleNormalize180( angles_diff[PITCH] ); float yaw_change = angles_diff[YAW]; float pitch_change = angles_diff[PITCH]; if ( threshold && ( yaw_change < threshold ) && ( yaw_change > -threshold ) && ( pitch_change < threshold ) && ( pitch_change > -threshold ) ) { if ( forever ) return BEHAVIOR_EVALUATING; else return BEHAVIOR_SUCCESS; } if (usingEyes) { if (angles_diff[YAW] > (self.minEyeYawAngle + (self.minEyeYawAngle * .5f )) && angles_diff[YAW] < (self.maxEyeYawAngle + (self.maxEyeYawAngle * .5f ))) angles_diff[YAW] = 0.0f; if (angles_diff[PITCH] > self.minEyePitchAngle && angles_diff[PITCH] < self.maxEyePitchAngle ) angles_diff[PITCH] = 0.0f; } // Make sure we don't turn neck too far if ( angles_diff[YAW] < -80.0f ) angles_diff[YAW] = -80.0f; else if ( angles_diff[YAW] > 80.0f ) angles_diff[YAW] = 80.0f; if ( angles_diff[PITCH] < -45.0f ) angles_diff[PITCH] = -45.0f; else if ( angles_diff[PITCH] > 45.0f ) angles_diff[PITCH] = 45.0f; angles_diff[ROLL] = 0.0f; // Make sure we don't change our head angles too much at once //current_head_angles[YAW] = current_head_angles[YAW] - current_torso_angles[YAW]; change = angles_diff - current_head_angles; if ( change[YAW] > max_speed ) angles_diff[YAW] = current_head_angles[YAW] + max_speed; else if ( change[YAW] < -max_speed ) angles_diff[YAW] = current_head_angles[YAW] - max_speed; if ( change[PITCH] > max_speed ) angles_diff[PITCH] = current_head_angles[PITCH] + max_speed; else if ( change[PITCH] < -max_speed ) angles_diff[PITCH] = current_head_angles[PITCH] - max_speed; Vector FinalAngles; FinalAngles = angles_diff; FinalAngles[YAW] = angles_diff[YAW] - current_torso_angles[YAW]; //self.SetControllerAngles( ACTOR_HEAD_TAG, angles_diff ); self.SetControllerAngles( ACTOR_HEAD_TAG, FinalAngles ); self.real_head_pitch = angles_diff[PITCH]; current_head_angles = angles_diff; /* if ( !ent_to_watch && ( current_head_angles == vec_zero ) ) return false; */ if ( !ent_to_watch ) return BEHAVIOR_EVALUATING; if ( !forever && ( change[YAW] < max_speed ) && ( change[YAW] > -max_speed ) && ( change[PITCH] < max_speed ) && ( change[PITCH] > -max_speed ) ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void HeadWatchEnemy::useEyes ( qboolean moveEyes ) { usingEyes = moveEyes; } void HeadWatchEnemy::End ( Actor &self ) { // Snap head back into position if we have lost our target or we are doing a resethead self.SetControllerAngles( ACTOR_HEAD_TAG, vec_zero ); self.real_head_pitch = 0; self.SetActorFlag( ACTOR_FLAG_TURNING_HEAD, false ); } /**************************************************************************** EyeWatch Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, EyeWatch, NULL ) { { &EV_Behavior_Args, &EyeWatch::SetArgs }, { NULL, NULL } }; EyeWatch::EyeWatch() { ent_to_watch = NULL; max_speed = 10; forever = true; threshold = 0; } void EyeWatch::SetArgs ( Event *ev ) { ent_to_watch = ev->GetEntity( 1 ); if ( ev->NumArgs() > 1 ) max_speed = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) forever = ev->GetBoolean( 3 ); if ( ev->NumArgs() > 3 ) threshold = ev->GetFloat( 4 ); } void EyeWatch::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void EyeWatch::Begin ( Actor &self ) { self.SetControllerTag( ACTOR_LEYE_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Face eyeballL" ) ); self.SetControllerTag( ACTOR_REYE_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Face eyeballR" ) ); current_left_eye_angles = self.GetControllerAngles( ACTOR_LEYE_TAG ); current_right_eye_angles = self.GetControllerAngles( ACTOR_REYE_TAG ); self.SetActorFlag( ACTOR_FLAG_MOVING_EYES, true ); } BehaviorReturnCode_t EyeWatch::Evaluate ( Actor &self ) { Vector dir; Vector angles; int tag_num; Vector tag_pos; Vector watch_tag_pos; Vector angles_diff; Vector watch_position; Actor *act = NULL; Vector change; if ( ent_to_watch ) { tag_num = gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) return BEHAVIOR_SUCCESS; self.GetTag( "Bip01 Head", &tag_pos ); if ( ent_to_watch->isSubclassOf( Actor ) ) act = (Actor *)(Entity *)ent_to_watch; if ( act && ( act->watch_offset != vec_zero ) ) { MatrixTransformVector( act->watch_offset, ent_to_watch->orientation, watch_position ); watch_position += ent_to_watch->origin; } else { tag_num = gi.Tag_NumForName( ent_to_watch->edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) watch_position = ent_to_watch->centroid; else { ent_to_watch->GetTag( "Bip01 Head", &watch_tag_pos ); watch_position = watch_tag_pos; } } dir = watch_position - tag_pos; if ( dir.length() < 32.0f ) { if ( !forever ) return BEHAVIOR_SUCCESS; else return BEHAVIOR_EVALUATING; } angles = dir.toAngles(); angles_diff = angles - self.angles; } else { angles_diff = vec_zero; } angles_diff[YAW] = AngleNormalize180( angles_diff[YAW] ); // Make sure we don't turn eyes too far if ( angles_diff[YAW] < self.minEyeYawAngle ) angles_diff[YAW] = self.minEyeYawAngle ; else if ( angles_diff[YAW] > self.maxEyeYawAngle ) angles_diff[YAW] = self.maxEyeYawAngle; if ( angles_diff[PITCH] < self.minEyePitchAngle ) angles_diff[PITCH] = self.minEyePitchAngle ; else if ( angles_diff[PITCH] > self.maxEyePitchAngle ) angles_diff[PITCH] = self.maxEyePitchAngle; angles_diff[ROLL] = 0; // Make sure we don't change our eye angles too much at once change = angles_diff - current_left_eye_angles; // check left eye -- both eyes will be the same if ( change[YAW] > max_speed ) angles_diff[YAW] = current_left_eye_angles[YAW] + max_speed; else if ( change[YAW] < -max_speed ) angles_diff[YAW] = current_left_eye_angles[YAW] - max_speed; if ( change[PITCH] > max_speed ) angles_diff[PITCH] = current_left_eye_angles[PITCH] + max_speed; else if ( change[PITCH] < -max_speed ) angles_diff[PITCH] = current_left_eye_angles[PITCH] - max_speed; self.SetControllerAngles( ACTOR_LEYE_TAG, angles_diff ); self.SetControllerAngles( ACTOR_REYE_TAG, angles_diff ); current_left_eye_angles = angles_diff; current_right_eye_angles = angles_diff; if ( !ent_to_watch && ( current_left_eye_angles == vec_zero ) ) return BEHAVIOR_SUCCESS; if ( !forever && ( change[YAW] < max_speed ) && ( change[YAW] > -max_speed ) && ( change[PITCH] < max_speed ) && ( change[PITCH] > -max_speed ) ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void EyeWatch::End ( Actor &self ) { // Snap head back into position if we have lost our target or we are doing a resethead self.SetControllerAngles( ACTOR_LEYE_TAG, vec_zero ); self.SetControllerAngles( ACTOR_REYE_TAG, vec_zero ); self.SetActorFlag( ACTOR_FLAG_MOVING_EYES, false ); } /**************************************************************************** EyeWatchEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, EyeWatchEnemy, NULL ) { { &EV_Behavior_Args, &EyeWatchEnemy::SetArgs }, { NULL, NULL } }; EyeWatchEnemy::EyeWatchEnemy() { ent_to_watch = NULL; max_speed = 10; forever = true; threshold = 0; } void EyeWatchEnemy::SetArgs ( Event *ev ) { max_speed = ev->GetFloat( 1 ); if ( ev->NumArgs() > 1 ) forever = ev->GetBoolean( 2 ); if ( ev->NumArgs() > 2 ) threshold = ev->GetFloat( 3 ); } void EyeWatchEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void EyeWatchEnemy::Begin ( Actor &self ) { self.SetControllerTag( ACTOR_LEYE_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Face eyeballL" ) ); self.SetControllerTag( ACTOR_REYE_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Face eyeballR" ) ); current_left_eye_angles = self.GetControllerAngles( ACTOR_LEYE_TAG ); current_right_eye_angles = self.GetControllerAngles( ACTOR_REYE_TAG ); self.SetActorFlag( ACTOR_FLAG_MOVING_EYES, true ); } BehaviorReturnCode_t EyeWatchEnemy::Evaluate ( Actor &self ) { Vector dir; Vector angles; int tag_num; Vector tag_pos; Vector watch_tag_pos; Vector angles_diff; Vector watch_position; Actor *act = NULL; Vector change; ent_to_watch = self.enemyManager->GetCurrentEnemy(); if ( ent_to_watch ) { tag_num = gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) return BEHAVIOR_SUCCESS; self.GetTag( "Bip01 Head", &tag_pos ); if ( ent_to_watch->isSubclassOf( Actor ) ) act = (Actor *)(Entity *)ent_to_watch; if ( act && ( act->watch_offset != vec_zero ) ) { MatrixTransformVector( act->watch_offset, ent_to_watch->orientation, watch_position ); watch_position += ent_to_watch->origin; } else { tag_num = gi.Tag_NumForName( ent_to_watch->edict->s.modelindex, "Bip01 Head" ); if ( tag_num < 0 ) watch_position = ent_to_watch->centroid; else { ent_to_watch->GetTag( "Bip01 Head", &watch_tag_pos ); watch_position = watch_tag_pos; } } dir = watch_position - tag_pos; angles = dir.toAngles(); angles_diff = angles - self.angles; } else { angles_diff = vec_zero; } angles_diff[YAW] = AngleNormalize180( angles_diff[YAW] ); float yaw_change = angles_diff[YAW]; float pitch_change = angles_diff[PITCH]; if ( threshold && ( yaw_change < threshold ) && ( yaw_change > -threshold ) && ( pitch_change < threshold ) && ( pitch_change > -threshold ) ) { if ( forever ) return BEHAVIOR_EVALUATING; else return BEHAVIOR_SUCCESS; } // Make sure we don't turn eyes too far if ( angles_diff[YAW] < self.minEyeYawAngle ) angles_diff[YAW] = self.minEyeYawAngle ; else if ( angles_diff[YAW] > self.maxEyeYawAngle ) angles_diff[YAW] = self.maxEyeYawAngle; if ( angles_diff[PITCH] < self.minEyePitchAngle ) angles_diff[PITCH] = self.minEyePitchAngle ; else if ( angles_diff[PITCH] > self.maxEyePitchAngle ) angles_diff[PITCH] = self.maxEyePitchAngle; angles_diff[ROLL] = 0; // Make sure we don't change our eye angles too much at once change = angles_diff - current_left_eye_angles; // check left eye -- both eyes will be the same if ( change[YAW] > max_speed ) angles_diff[YAW] = current_left_eye_angles[YAW] + max_speed; else if ( change[YAW] < -max_speed ) angles_diff[YAW] = current_left_eye_angles[YAW] - max_speed; if ( change[PITCH] > max_speed ) angles_diff[PITCH] = current_left_eye_angles[PITCH] + max_speed; else if ( change[PITCH] < -max_speed ) angles_diff[PITCH] = current_left_eye_angles[PITCH] - max_speed; self.SetControllerAngles( ACTOR_LEYE_TAG, angles_diff ); self.SetControllerAngles( ACTOR_REYE_TAG, angles_diff ); //self.real_head_pitch = angles_diff[PITCH]; current_left_eye_angles = angles_diff; current_right_eye_angles = angles_diff; //self.eyeposition = current_head_angles; if ( !ent_to_watch && ( current_left_eye_angles == vec_zero ) ) return BEHAVIOR_SUCCESS; if ( !forever && ( change[YAW] < max_speed ) && ( change[YAW] > -max_speed ) && ( change[PITCH] < max_speed ) && ( change[PITCH] > -max_speed ) ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void EyeWatchEnemy::End ( Actor &self ) { // Snap head back into position if we have lost our target or we are doing a resethead //if ( !ent_to_watch ) // { self.SetControllerAngles( ACTOR_LEYE_TAG, vec_zero ); self.SetControllerAngles( ACTOR_REYE_TAG, vec_zero ); //self.real_head_pitch = 0; // } self.SetActorFlag( ACTOR_FLAG_MOVING_EYES, false ); } /**************************************************************************** HeadAndEyeWatch Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HeadAndEyeWatch, NULL ) { { &EV_Behavior_Args, &HeadAndEyeWatch::SetArgs }, { NULL, NULL } }; HeadAndEyeWatch::HeadAndEyeWatch() { } void HeadAndEyeWatch::SetArgs ( Event *ev ) { headWatch.SetArgs(ev); eyeWatch.SetArgs(ev); headWatch.useEyes(true); } void HeadAndEyeWatch::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); headWatch.ShowInfo(self); eyeWatch.ShowInfo(self); } void HeadAndEyeWatch::Begin ( Actor &self ) { headWatch.Begin(self); eyeWatch.Begin(self); } BehaviorReturnCode_t HeadAndEyeWatch::Evaluate ( Actor &self ) { // if headWatch returns 0 and eyeWatch returns 0 -- returns 0 // if headWatch returns 1 and eyeWatch returns 0 -- returns 0 // if headWatch returns 0 and eyeWatch returns 1 -- returns 0 // if headWatch returns 1 and eyeWatch returns 1 -- returns 1 return BEHAVIOR_SUCCESS; } void HeadAndEyeWatch::End ( Actor &self ) { headWatch.End(self); eyeWatch.End(self); } /**************************************************************************** TorsoTurn Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TorsoTurn, NULL ) { { &EV_Behavior_Args, &TorsoTurn::SetArgs }, { NULL, NULL } }; void TorsoTurn::SetArgs ( Event *ev ) { turn_towards_enemy = ev->GetInteger( 1 ); speed = ev->GetFloat( 2 ); forever = ev->GetInteger( 3 ); if ( ev->NumArgs() > 3 ) tag_name = ev->GetString( 4 ); if ( ev->NumArgs() > 4 ) tolerance = ev->GetFloat( 5 ); else tolerance = 0; if ( ev->NumArgs() > 5 ) offset = ev->GetFloat( 6 ); else offset = 0; if ( ev->NumArgs() > 6 ) use_pitch = ev->GetBoolean( 7 ); else use_pitch = true; } void TorsoTurn::SetRequiredParameters( int TurnTowardsEnemy , int Speed , int Forever ) { turn_towards_enemy = TurnTowardsEnemy; speed = Speed; forever = Forever; } void TorsoTurn::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void TorsoTurn::Begin ( Actor &self ) { Vector controller_angles; self.SetControllerTag( ACTOR_TORSO_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Spine1" ) ); controller_angles = self.GetControllerAngles( ACTOR_TORSO_TAG ); current_yaw = controller_angles[YAW]; current_pitch = controller_angles[PITCH]; } BehaviorReturnCode_t TorsoTurn::Evaluate ( Actor &self ) { Vector dir; Vector angles; int tag_num; float yaw_diff; float pitch_diff; Vector new_angles; float yaw_change; float pitch_change; Vector tag_pos; Vector tag_forward; Vector tag_angles; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); tag_num = gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Spine1" ); if ( tag_num < 0 ) return BEHAVIOR_SUCCESS; // Determine the angle we want to go to if ( turn_towards_enemy ) { if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( tag_name.length() ) { self.GetTag( tag_name.c_str(), &tag_pos, &tag_forward ); tag_angles = tag_forward.toAngles(); dir = currentEnemy->centroid - tag_pos; } else { dir = currentEnemy->centroid - self.centroid; } angles = dir.toAngles(); angles[YAW] += offset; yaw_diff = AngleNormalize180( angles[YAW] - self.angles[YAW] ); pitch_diff = AngleNormalize180( angles[PITCH] - self.angles[PITCH] ); } else { yaw_diff = 0.0f; pitch_diff = 0.0f; } // Determine the angle change yaw_change = AngleNormalize180( yaw_diff - current_yaw ); pitch_change = AngleNormalize180( pitch_diff - current_pitch ); if ( tolerance && ( yaw_change < tolerance ) && ( yaw_change > -tolerance ) && ( pitch_change < tolerance ) && ( pitch_change > -tolerance ) ) { if ( forever ) return BEHAVIOR_EVALUATING; else return BEHAVIOR_SUCCESS; } // Make sure we don't change our torso angles too much at once if ( yaw_change > speed ) yaw_diff = current_yaw + speed; else if ( yaw_change < -speed ) yaw_diff = current_yaw - speed; if ( pitch_change > speed ) pitch_diff = current_pitch + speed; else if ( yaw_change < -speed ) pitch_diff = current_pitch - speed; // Determine our new angles new_angles[YAW] = yaw_diff; if ( use_pitch ) new_angles[PITCH] = pitch_diff; else new_angles[PITCH] = 0.0f; new_angles[ROLL] = 0.0f; // Make sure we don't turn too far if ( ( new_angles[YAW] > 100.0f ) || ( new_angles[YAW] < -100.0f ) ) { if (!forever ) return BEHAVIOR_SUCCESS; else { self.angles[YAW] = new_angles[YAW]; self.angles[ROLL] = 0; self.setAngles( self.angles ); return BEHAVIOR_EVALUATING; } } if ( new_angles[PITCH] > 45 || new_angles[PITCH] < -45 ) { if (!forever ) return BEHAVIOR_SUCCESS; else return BEHAVIOR_EVALUATING; } // Set our new angles self.SetControllerAngles( ACTOR_TORSO_TAG, new_angles ); current_yaw = yaw_diff; current_pitch = pitch_diff; // See if we are turned the correct direction now if ( !forever && (yaw_change < speed) && (yaw_change > -speed) && (pitch_change < speed) && (pitch_change > -speed) ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void TorsoTurn::End ( Actor &self ) { self.SetControllerAngles( ACTOR_TORSO_TAG, vec_zero ); } /**************************************************************************** TorsoWatchEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TorsoWatchEnemy, NULL ) { { &EV_Behavior_Args, &TorsoWatchEnemy::SetArgs }, { NULL, NULL } }; void TorsoWatchEnemy::SetArgs ( Event *ev ) { invert = false; speed = ev->GetFloat( 1 ); forever = ev->GetInteger( 2 ); threshold = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) offset = ev->GetFloat( 4 ); else offset = 0; if ( ev->NumArgs() > 4 ) use_pitch = ev->GetBoolean( 5 ); else use_pitch = true; if ( ev->NumArgs() > 5 ) invert = ev->GetBoolean( 6 ); if ( ev->NumArgs() > 6 ) reset = ev->GetBoolean ( 7 ); else reset = true; invertLegs = false; nextFlipTime = 0; } void TorsoWatchEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void TorsoWatchEnemy::Begin ( Actor &self ) { Vector controller_angles; self.SetControllerTag( ACTOR_TORSO_TAG, gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Spine1" ) ); controller_angles = self.GetControllerAngles( ACTOR_TORSO_TAG ); current_yaw = controller_angles[YAW]; current_pitch = controller_angles[PITCH]; self.SetActorFlag( ACTOR_FLAG_OUT_OF_TORSO_RANGE , false ); } BehaviorReturnCode_t TorsoWatchEnemy::Evaluate ( Actor &self ) { Vector dir; Vector angles; int tag_num; float yaw_diff; float pitch_diff; Vector new_angles; float yaw_change; float pitch_change; Vector tag_pos; Vector tag_forward; Vector tag_angles; tag_num = gi.Tag_NumForName( self.edict->s.modelindex, "Bip01 Spine1" ); if ( tag_num < 0 ) return BEHAVIOR_SUCCESS; // Determine the angle we want to go to Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) { reset = true; return BEHAVIOR_EVALUATING; } /* if ( invertLegs && self.GetActorFlag( ACTOR_FLAG_ANIM_DONE ) ) { self.SetAnim( "idle" ); invertLegs = false; } */ dir = currentEnemy->centroid - self.centroid; if (invert) dir *= -1.0f; angles = dir.toAngles(); angles[YAW] += offset; /* Vector controller_angles; controller_angles = self.GetControllerAngles( ACTOR_TORSO_TAG ); current_yaw = controller_angles[YAW]; current_pitch = controller_angles[PITCH]; */ yaw_diff = AngleNormalize180( angles[YAW] - self.angles[YAW] ); pitch_diff = AngleNormalize180( angles[PITCH] - self.angles[PITCH] ); //yaw_diff = AngleNormalize180( angles[YAW] - test[YAW] ); //pitch_diff = AngleNormalize180( angles[PITCH] - test[PITCH] ); // Determine the angle change yaw_change = AngleNormalize180( yaw_diff - current_yaw ); pitch_change = AngleNormalize180( pitch_diff - current_pitch ); /* if ( threshold && yaw_change < threshold && yaw_change > -threshold && pitch_change < threshold && pitch_change > -threshold ) { if ( forever ) return true; else return false; } */ // Make sure we don't change our torso angles too much at once if ( yaw_change > speed ) yaw_diff = current_yaw + speed; else if ( yaw_change < -speed ) yaw_diff = current_yaw - speed; if ( pitch_change > speed ) pitch_diff = current_pitch + speed; else if ( pitch_change < -speed ) pitch_diff = current_pitch - speed; // Determine our new angles new_angles[YAW] = yaw_diff; if ( use_pitch ) { new_angles[PITCH] = pitch_diff; } else new_angles[PITCH] = 0.0f; new_angles[ROLL] = 0.0f; // Make sure we don't turn too far if ( ( new_angles[YAW] > 100.0f ) || ( new_angles[YAW] < -100.0f ) ) { if ( !forever ) return BEHAVIOR_SUCCESS; else { if ( new_angles[YAW] < -100.0f || new_angles[YAW] > 100.0f ) { if ( nextFlipTime <= level.time ) { self.movementSubsystem->flipLegs(); invertLegs = true; nextFlipTime = level.time + .75; } return BEHAVIOR_EVALUATING; } } } if ( ( new_angles[PITCH] > 60.0f ) || ( new_angles[PITCH] < -60 ) ) { if ( !forever ) return BEHAVIOR_SUCCESS; else return BEHAVIOR_EVALUATING; } // Set our new angles self.SetControllerAngles( ACTOR_TORSO_TAG, new_angles ); current_yaw = yaw_diff; current_pitch = pitch_diff; // See if we are turned the correct direction now if ( !forever && (yaw_change < speed) && (yaw_change > -speed) && (pitch_change < speed) && (pitch_change > -speed) ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void TorsoWatchEnemy::End ( Actor &self ) { /* if ( invertLegs ) self.movementSubsystem->flipLegs(); */ self.movementSubsystem->setMovingBackwards( false ); if ( reset ) self.SetControllerAngles( ACTOR_TORSO_TAG, vec_zero ); } /**************************************************************************** GotoPathNode Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, GotoPathNode, NULL ) { { &EV_Behavior_AnimDone, &GotoPathNode::AnimDone }, { &EV_Behavior_Args, &GotoPathNode::SetArgs }, { NULL, NULL } }; GotoPathNode::GotoPathNode() { chase = NULL; usevec = false; movegoal = NULL; goal = vec_zero; goalent = NULL; state = 0; _followingEntity = false; } void GotoPathNode::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->IsVectorAt( 2 ) ) { goal = ev->GetVector( 2 ); usevec = true; } else { usevec = false; movegoal = thePathManager.FindNode( ev->GetString( 2 ) ); if ( !movegoal ) { goalent = ev->GetEntity( 2 ); } } if ( ev->NumArgs() > 2 ) entity_to_watch = ev->GetEntity( 3 ); } void GotoPathNode::AnimDone ( Event *ev ) { turnto.ProcessEvent( EV_Behavior_AnimDone ); } void GotoPathNode::SetGoal ( PathNode *node ) { usevec = false; movegoal = node; } void GotoPathNode::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nturnto:\n" ); turnto.ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase->ShowInfo( self ); gi.Printf( "\nstate: %d\n", state ); gi.Printf( "usevec: %d\n", usevec ); //gi.Printf( "time: %f\n", time ); gi.Printf( "anim: %s\n", anim.c_str() ); if ( goalent ) { gi.Printf( "\ngoalent: #%d '%s'\n", goalent->entnum, goalent->targetname.c_str() ); } else { gi.Printf( "\ngoalent: NULL\n" ); } gi.Printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z ); } void GotoPathNode::Begin ( Actor &self ) { Event *ev; Vector dir; dir = self.movementSubsystem->getAnimDir(); dir = dir.toAngles(); self.setAngles( dir ); turnto.Begin( self ); float radius=12.0f; if ( goalent ) { FollowPathToEntity *newFollowPath = new FollowPathToEntity(); newFollowPath->SetGoal( goalent, radius, self ); chase = newFollowPath; _followingEntity = true; } else if ( movegoal ) { FollowPathToPoint *newFollowPath = new FollowPathToPoint(); newFollowPath->SetGoal( movegoal->origin, radius, self ); chase = newFollowPath; } else { FollowPathToPoint *newFollowPath = new FollowPathToPoint(); newFollowPath->SetGoal( goal, radius, self ); chase = newFollowPath; } chase->Begin( self ); chase->Begin( self ); if ( anim.length() ) { self.SetAnim( anim ); } // Setup head watch stuff head_watch.Begin( self ); ev = new Event( EV_Behavior_Args ); ev->AddEntity( entity_to_watch ); head_watch.ProcessEvent( ev ); } BehaviorReturnCode_t GotoPathNode::Evaluate ( Actor &self ) { float yaw; if ( !usevec && !goalent && !movegoal ) { gi.DPrintf( "GotoPathNode::No goal\n" ); return BEHAVIOR_SUCCESS; } if ( entity_to_watch ) head_watch.Evaluate( self ); switch( state ) { case 0 : if ( chase->Evaluate( self ) == Steering::EVALUATING ) { break; } state = 1; self.SetAnim( "idle" ); // cascade down to case 1 // lint -fallthrough case 1 : if ( !movegoal ) { return BEHAVIOR_SUCCESS; } if ( movegoal->setangles ) { yaw = movegoal->angles.yaw(); turnto.SetDirection( yaw ); if ( turnto.Evaluate( self ) ) { break; } } if ( movegoal->animname == "" ) { self.SetAnim( "idle" ); return BEHAVIOR_SUCCESS; } self.SetAnim( movegoal->animname, EV_Actor_EndBehavior ); state = 2; break; case 2 : break; } return BEHAVIOR_EVALUATING; } void GotoPathNode::End ( Actor &self ) { chase->End( self ); head_watch.End( self ); } /**************************************************************************** Flee Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Flee, NULL ) { { &EV_Behavior_Args, &Flee::SetArgs }, { NULL, NULL } }; void Flee::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void Flee::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void Flee::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } chase.Begin( self ); FindFleeNode( self ); } void Flee::FindFleeNode ( Actor &self ) { int i; PathNode *start_node; PathNodeConnection *path; PathNode *current_node; int max_nodes_to_look_at; int nodes_looked_at; qboolean found; // Get closest node start_node = thePathManager.NearestNode( self.origin, &self ); // Find a random node that is connected to this node found = false; if ( start_node ) { max_nodes_to_look_at = (int)G_Random( 5.0f ) + 10; nodes_looked_at = 0; current_node = start_node; while( !found ) { nodes_looked_at++; if ( ( nodes_looked_at >= max_nodes_to_look_at ) && ( current_node != start_node ) ) { found = true; flee_node = current_node; } if ( current_node->NumberOfConnections() == 0 ) break; path = ¤t_node->GetConnection( (int)G_Random( (float)current_node->NumberOfConnections() ) ); current_node = thePathManager.GetNode( path->targetNodeIndex ); } } if ( !found ) { // If still not found, use old method for( i = 0; i < 5; i++ ) { flee_node = thePathManager.GetNode( ( int )G_Random( (float)thePathManager.NumNodes() + 1.0f ) ); if ( flee_node ) break; } } } BehaviorReturnCode_t Flee::Evaluate ( Actor &self ) { // Make sure we have somewhere to flee to if ( !flee_node ) return BEHAVIOR_SUCCESS; float radius=96.0f; chase.SetGoal( flee_node->origin, radius, self ); // Make a racket self.Chatter( "snd_panic", 3.0f ); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; // Try to get to flee node if ( chase.Evaluate( self ) != Steering::EVALUATING ) { // See if we are done fleeing if ( !self.sensoryPerception->CanSeeEntity(&self, currentEnemy , true, true )) return BEHAVIOR_SUCCESS; // Find a new spot to flee to FindFleeNode( self ); } return BEHAVIOR_EVALUATING; } void Flee::End ( Actor &self ) { chase.End( self ); } /**************************************************************************** PlayAnim Class Definition ****************************************************************************/ /* CLASS_DECLARATION( Behavior, PlayAnim, NULL ) { { &EV_Behavior_Args, SetArgs }, { NULL, NULL } }; void PlayAnim::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void PlayAnim::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nanim: %s\n", anim.c_str() ); } void PlayAnim::Begin ( Actor &self ) { if ( anim.length() ) { if ( !self.SetAnim( anim, EV_Actor_EndBehavior ) ) { self.PostEvent( EV_Actor_EndBehavior, 0.0f ); } } } BehaviorReturnCode_t PlayAnim::Evaluate ( Actor &self ) { return BEHAVIOR_EVALUATING; } void PlayAnim::End ( Actor &self ) { self.RemoveAnimDoneEvent(); } */ /**************************************************************************** FindCover Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FindCover, NULL ) { { &EV_Behavior_Args, &FindCover::SetArgs }, { NULL, NULL } }; void FindCover::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) crouch_anim = ev->GetString( 2 ); } PathNode *FindCover::FindCoverNode ( Actor &self ) { PathNode *bestnode; float bestdist; PathNode *desperatebestnode; float desperatebestdist; FindCoverPath find; Path *path; Vector delta; float dist; Vector pos; pos = self.origin; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance bestdist = 999999999.0f; bestnode = NULL; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance desperatebestdist = 999999999.0f; desperatebestnode = NULL; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return NULL; PathNode *node = NULL; for ( int i = 1 ; i <= thePathManager.NumberOfSpecialNodes(); i++ ) { node = thePathManager.GetSpecialNode( i ); if ( node && ( node->nodeflags & ( AI_DUCK | AI_COVER ) ) && ( ( node->occupiedTime <= level.time ) || ( node->entnum == self.entnum ) ) ) { // get the distance squared (faster than getting real distance) delta = node->origin - pos; dist = delta * delta; if ( ( dist < bestdist ) && ( !self.sensoryPerception->CanSeeEntity ( node->origin , currentEnemy , true, true ) ||//) )//|| ( ( node->nodeflags & AI_DUCK ) && !self.sensoryPerception->CanSeeEntity( node->origin - Vector( 0.0f, 0.0f, 32.0f ) , currentEnemy , true , true ) ) ) ) { bestnode = node; bestdist = dist; } else if ( ( dist < desperatebestdist ) && ( desperatebestdist > ( 64.0f * 64.0f ) ) ) { desperatebestnode = node; desperatebestdist = dist; } } } if ( !bestnode ) { bestnode = desperatebestnode; } if ( bestnode ) { find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; path = find.FindPath( self.origin, bestnode->origin ); if ( path ) { node = path->End(); // Mark node as occupied for a short time node->occupiedTime = level.time + 1.5f; node->entnum = self.entnum; float radius=96.0f; chase.SetGoal( node->origin, radius, self ); delete path; path = NULL; return node; } } return NULL; } void FindCover::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); gi.Printf( "\nstate: %d\n", state ); gi.Printf( "anim: %s\n", anim.c_str() ); gi.Printf( "nextsearch: %f\n", nextsearch ); } void FindCover::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } if ( !crouch_anim.length() ) { crouch_anim = "crouch_down"; } movegoal = NULL; state = 0; } BehaviorReturnCode_t FindCover::Evaluate ( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !movegoal ) { state = 0; } switch( state ) { case 0 : // Checking for cover chase.Begin( self ); movegoal = FindCoverNode( self ); if ( !movegoal ) { // Couldn't find any! return BEHAVIOR_SUCCESS; } // Found cover, going to it if ( anim.length() && ( ( anim != self.animname ) || self.newanim.length() ) ) { self.SetAnim( anim ); } state = 1; nextsearch = level.time + 1.0f; // lint -fallthrough case 1 : if ( chase.Evaluate( self ) == Steering::EVALUATING ) { if ( nextsearch < level.time ) { state = 0; } return BEHAVIOR_EVALUATING; } // Reached cover if ( self.sensoryPerception->CanSeeEntity ( &self , currentEnemy , true , true ) ) { state = 0; } if ( movegoal->nodeflags & AI_DUCK ) { // ducking self.SetAnim( crouch_anim.c_str() ); } else { // standing self.SetAnim( "idle" ); } chase.End( self ); return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } void FindCover::End ( Actor &self ) { chase.End( self ); } /**************************************************************************** FindFlee Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FindFlee, NULL ) { { &EV_Behavior_Args, &FindFlee::SetArgs }, { NULL, NULL } }; void FindFlee::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } PathNode *FindFlee::FindFleeNode ( Actor &self ) { PathNode *bestnode; float bestdist; PathNode *desperatebestnode; float desperatebestdist; FindFleePath find; Path *path; Vector delta; float dist; Vector pos; pos = self.origin; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance bestdist = 999999999.0f; bestnode = NULL; // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance desperatebestdist = 999999999.0f; desperatebestnode = NULL; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return NULL; PathNode *node = NULL; for ( int i = 1 ; i <= thePathManager.NumberOfSpecialNodes(); i++ ) { node = thePathManager.GetSpecialNode( i ); if ( node && ( node->nodeflags & AI_FLEE ) && ( ( node->occupiedTime <= level.time ) || ( node->entnum == self.entnum ) ) ) { // get the distance squared (faster than getting real distance) delta = node->origin - pos; dist = delta * delta; if ( ( dist < bestdist ) && !self.sensoryPerception->CanSeeEntity( node->origin , currentEnemy , true, true ) ) { bestnode = node; bestdist = dist; } else if ( ( dist < desperatebestdist ) && ( desperatebestdist > ( 64.0f * 64.0f ) ) ) { desperatebestnode = node; desperatebestdist = dist; } } } if ( !bestnode ) { bestnode = desperatebestnode; } if ( bestnode ) { find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; path = find.FindPath( self.origin, bestnode->origin ); if ( path ) { node = path->End(); // Mark node as occupied for a short time node->occupiedTime = level.time + 1.5f; node->entnum = self.entnum; float radius=96.0f; chase.SetGoal( node->origin, radius, self ); delete path; path = NULL; return node; } } return NULL; } void FindFlee::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); gi.Printf( "\nstate: %d\n", state ); gi.Printf( "anim: %s\n", anim.c_str() ); gi.Printf( "nextsearch: %f\n", nextsearch ); } void FindFlee::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } movegoal = NULL; state = 0; } BehaviorReturnCode_t FindFlee::Evaluate ( Actor &self ) { if ( !movegoal ) { state = 0; } Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; switch( state ) { case 0 : // Checking for flee node chase.Begin( self ); movegoal = FindFleeNode( self ); if ( !movegoal ) { // Couldn't find any! return BEHAVIOR_SUCCESS; } // Found flee node, going to it if ( anim.length() && ( anim != self.animname || self.newanim.length() ) ) { self.SetAnim( anim ); } state = 1; nextsearch = level.time + 1.0f; // lint -fallthrough case 1 : if ( chase.Evaluate( self ) == Steering::EVALUATING ) { if ( nextsearch < level.time ) { state = 0; } return BEHAVIOR_EVALUATING; } if ( self.sensoryPerception ) { // Reached cover if ( self.sensoryPerception->CanSeeEntity( &self , currentEnemy , true , true ) ) { state = 0; } else { // standing self.SetAnim( "idle" ); chase.End( self ); return BEHAVIOR_SUCCESS; } } break; } return BEHAVIOR_EVALUATING; } void FindFlee::End ( Actor &self ) { chase.End( self ); } /**************************************************************************** FindEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FindEnemy, NULL ) { { &EV_Behavior_Args, &FindEnemy::SetArgs }, { NULL, NULL } }; void FindEnemy::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void FindEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); gi.Printf( "\nstate: %d\n", state ); gi.Printf( "nextsearch: %f\n", nextsearch ); gi.Printf( "anim: %s\n", anim.c_str() ); } void FindEnemy::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } movegoal = NULL; lastSearchNode = NULL; state = 0; } PathNode *FindEnemy::FindClosestSightNode ( Actor &self ) { PathNode *bestnode; float bestdist; Vector delta; float dist; Vector pos; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( currentEnemy ) { pos = currentEnemy->origin; } else { pos = self.origin; } // greater than ( 8192 * sqr(2) ) ^ 2 -- maximum squared distance bestdist = 999999999; bestnode = NULL; for ( int i = 1 ; i <= thePathManager.NumberOfSpecialNodes(); i++ ) { PathNode *node = thePathManager.GetSpecialNode( i ); if ( node && ( ( node->occupiedTime <= level.time ) || ( node->entnum != self.entnum ) ) ) { // get the distance squared (faster than getting real distance) delta = node->origin - pos; dist = delta * delta; if ( ( dist < bestdist ) && self.sensoryPerception->CanSeeEntity( node->origin, currentEnemy , true , true ) ) { bestnode = node; bestdist = dist; } } } return bestnode; } BehaviorReturnCode_t FindEnemy::Evaluate ( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( nextsearch < level.time ) { // check if we should search for the first time if ( !lastSearchNode ) { state = 0; } else { // search less often if we're far away nextsearch = self.DistanceTo( currentEnemy ) * ( 1.0f / 512.0f ); if ( nextsearch < 1.0f ) { nextsearch = 1.0f; } nextsearch += level.time; // don't search again if our enemy hasn't moved very far if ( currentEnemy->WithinDistance( lastSearchPos, 256.0f ) ) { state = 0; } } } switch( state ) { case 0 : // Searching for enemy chase.Begin( self ); lastSearchPos = currentEnemy->origin; movegoal = thePathManager.NearestNode( lastSearchPos, &self ); if ( !movegoal ) { movegoal = thePathManager.NearestNode( lastSearchPos, &self, false ); } lastSearchNode = movegoal; if ( movegoal ) { Path *path; FindEnemyPath find; PathNode *from; find.heuristic.self = &self; find.heuristic.setSize( self.size ); find.heuristic.entnum = self.entnum; from = thePathManager.NearestNode( self.origin, &self ); if ( ( from == movegoal ) && ( self.DistanceTo( from->origin ) < 8.0f ) ) { movegoal = NULL; from = NULL; } if ( from ) { path = find.FindPath( from, movegoal ); if ( path ) { float radius=96.0f; chase.SetGoal( movegoal->origin, radius, self ); delete path; path = NULL; } else { movegoal = NULL; } } } if ( !movegoal ) { if ( self.sensoryPerception->CanSeeEntity( &self , currentEnemy , true , true ) || ( !currentEnemy->groundentity && !self.waterlevel ) ) { float radius=96.0f; chase.SetGoal( currentEnemy->origin, radius, self ); } else { // Couldn't find enemy // since we can't reach em // clear out enemy state self.enemyManager->ClearCurrentEnemy(); return BEHAVIOR_SUCCESS; } } // Found enemy, going to it if ( anim.length() && ( anim != self.animname || self.newanim.length() ) ) { self.SetAnim( anim ); } state = 1; // lint -fallthrough case 1 : if ( self.CanAttack( currentEnemy, false ) ) { // Reached enemy chase.End( self ); return BEHAVIOR_SUCCESS; } if ( chase.Evaluate( self ) != Steering::EVALUATING ) { state = 0; nextsearch = 0; } break; } return BEHAVIOR_EVALUATING; } void FindEnemy::End ( Actor &self ) { chase.End( self ); } /**************************************************************************** AimAndShoot Class Definition ****************************************************************************/ #define START_AIM 0 #define AIM 1 #define FIRE 2 CLASS_DECLARATION( Behavior, AimAndShoot, NULL ) { { &EV_Behavior_Args, &AimAndShoot::SetArgs }, { &EV_Behavior_AnimDone, &AimAndShoot::AnimDone }, { NULL, NULL } }; AimAndShoot::AimAndShoot() { maxshots = 1; numshots = 0; aim_time = 0.0f; enemy_health = 0; animdone = false; } void AimAndShoot::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\naim:\n" ); aim.ShowInfo( self ); gi.Printf( "\nmode: %d\n", mode ); gi.Printf( "maxshots: %d\n", maxshots ); gi.Printf( "numshots: %d\n", numshots ); gi.Printf( "animdone: %d\n", animdone ); } void AimAndShoot::Begin ( Actor &self ) { if ( aimanim.length() ) { self.SetAnim( aimanim.c_str() ); mode = START_AIM; } else { self.SetAnim( "idle" ); mode = AIM; } } void AimAndShoot::SetMaxShots ( int num ) { maxshots = ( num / 2 ) + (int)G_Random( (float)num ); } void AimAndShoot::SetArgs ( Event *ev ) { fireanim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { maxshots = ev->GetInteger ( 2 ); } if ( ev->NumArgs() > 2 ) { aimanim = ev->GetString ( 3 ); } int turnTowardsEnemy = ev->GetInteger( 3 ); int speed = ev->GetInteger( 4 ); torsoTurn.SetRequiredParameters( turnTowardsEnemy, speed, 0 ); } void AimAndShoot::AnimDone ( Event *ev ) { animdone = true; } BehaviorReturnCode_t AimAndShoot::Evaluate ( Actor &self ) { Vector dir; Vector angles; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); switch( mode ) { case START_AIM : if ( !currentEnemy || !self.CanAttack( currentEnemy, false )) return BEHAVIOR_SUCCESS; animdone = false; if ( aimanim.length() ) self.SetAnim( aimanim.c_str() ); // save off time, in case we aim for too long aim_time = level.time + 1.0f; mode = AIM; // lint -fallthrough case AIM : if ( aimanim.length() ) { if ( !currentEnemy || ( aim_time < level.time ) ) return BEHAVIOR_SUCCESS; aim.SetTarget( currentEnemy ); if ( aim.Evaluate( self ) ) { break; } } else { if ( self.IsEntityAlive( currentEnemy ) ) { dir = currentEnemy->centroid - self.origin; angles = dir.toAngles(); self.angles[YAW] = angles[YAW]; self.setAngles( self.angles ); } } // don't go into our firing animation until our weapon is ready, and we are on target if ( currentEnemy && self.CanAttack( currentEnemy, true ) ) { animdone = false; self.Chatter( "snd_inmysights", 5.0f ); self.SetAnim( fireanim.c_str(), EV_Actor_NotifyBehavior ); enemy_health = currentEnemy->health; mode = 2; } else if ( !currentEnemy || currentEnemy->deadflag || ( currentEnemy->health <= 0.0f ) || !self.CanAttack( currentEnemy, false ) ) { // either our enemy is dead, or we can't shoot the enemy from here return BEHAVIOR_SUCCESS; } break; case FIRE : // Fire if ( animdone ) { aim.SetTarget( currentEnemy ); aim.Evaluate( self ); self.times_done++; if ( !currentEnemy || ( currentEnemy->health < enemy_health ) ) { self.Chatter( "snd_attacktaunt", 4.0f ); } else { self.Chatter( "snd_missed", 7.0f ); } animdone = false; numshots++; if ( ( numshots >= maxshots ) || !currentEnemy || currentEnemy->deadflag || ( currentEnemy->health <= 0.0f ) || !self.CanAttack( currentEnemy, false ) ) { // either we're out of shots, our enemy is dead, or we can't shoot the enemy from here return BEHAVIOR_SUCCESS; } else if ( !self.CanAttack( currentEnemy, false ) ) { // weapon not ready or not aimed at enemy, so just keep trying to get enemy in our sights if ( aimanim.length() ) { self.SetAnim( aimanim.c_str() ); } // // save off time, in case we aim for too long // aim_time = level.time + 1.0f; mode = 1; } else { // keep firing self.SetAnim( fireanim.c_str(), EV_Actor_NotifyBehavior ); enemy_health = currentEnemy->health; } } break; } return BEHAVIOR_EVALUATING; } void AimAndShoot::End ( Actor &self ) { aim.End( self ); //self.SetAnim( "idle" ); } /**************************************************************************** AimAndMelee Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, AimAndMelee, NULL ) { { &EV_Behavior_Args, &AimAndMelee::SetArgs }, { &EV_Behavior_AnimDone, &AimAndMelee::AnimDone }, { NULL, NULL } }; AimAndMelee::AimAndMelee() { maxshots = 1; anim_name = "melee"; mode = 0; numshots = 0; animdone = false; } void AimAndMelee::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\naim:\n" ); aim.ShowInfo( self ); gi.Printf( "\nmode: %d\n", mode ); gi.Printf( "maxshots: %d\n", maxshots ); gi.Printf( "numshots: %d\n", numshots ); gi.Printf( "animdone: %d\n", animdone ); } void AimAndMelee::Begin ( Actor &self ) { self.SetAnim( "idle" ); } void AimAndMelee::SetArgs ( Event *ev ) { anim_name = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { maxshots = ev->GetInteger( 2 ); } } void AimAndMelee::AnimDone ( Event *ev ) { animdone = true; } BehaviorReturnCode_t AimAndMelee::Evaluate ( Actor &self ) { Vector dir; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); switch( mode ) { case 0 : if ( !currentEnemy ) { return BEHAVIOR_SUCCESS; } if ( self.IsEntityAlive( currentEnemy ) ) { dir = currentEnemy->centroid - self.origin; self.angles[YAW] = dir.toYaw(); self.setAngles( self.angles ); } numshots++; animdone = false; // melee self.SetAnim( anim_name.c_str() , EV_Actor_NotifyBehavior ); self.Chatter( "snd_attacktaunt", 4.0f ); mode = 1; // lint -fallthrough case 1 : // finish up the attack if ( animdone ) { self.times_done++; if ( numshots >= maxshots ) { return BEHAVIOR_SUCCESS; } mode = 0; } break; } return BEHAVIOR_EVALUATING; } void AimAndMelee::End ( Actor &self ) { aim.End( self ); } /**************************************************************************** FallToDeath Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FallToDeath, NULL ) { { &EV_Behavior_Args, &FallToDeath::SetArgs }, { NULL, NULL } }; FallToDeath::FallToDeath() { } void FallToDeath::SetArgs ( Event *ev ) { // Get the Parameters forwardmove = ev->GetFloat( 1 ); sidemove = ev->GetFloat( 2 ); speed = ev->GetFloat( 3 ); startAnim = ev->GetString( 4 ); fallAnim = ev->GetString( 5 ); deathAnim = ev->GetString( 6 ); if ( ev->NumArgs() > 6 ) impulse_time = ( ev->GetFloat( 7 ) ) + level.time; else impulse_time = 0; animdone = false; state = 0; did_impulse = false; } void FallToDeath::Begin ( Actor &self ) { //Initialize our stuff self.SetAnim( startAnim.c_str() , EV_Anim_Done ); self.ChangeAnim(); } BehaviorReturnCode_t FallToDeath::Evaluate ( Actor &self ) { if ( !did_impulse && ( level.time >= impulse_time ) ) { Vector( 0.0f, self.angles.y, 0.0f ).AngleVectors( &yaw_forward, &yaw_left ); self.velocity = ( yaw_forward * forwardmove ) - ( yaw_left * sidemove ); distance = self.velocity.length(); self.velocity *= speed / distance; time = distance / speed; self.velocity[ 2 ] = sv_currentGravity->integer * time * 0.5f; did_impulse = true; } animdone = self.GetActorFlag( ACTOR_FLAG_ANIM_DONE ); self.SetActorFlag( ACTOR_FLAG_ANIM_DONE, false ); switch( state ) { case 0: if ( did_impulse ) state = 1; // this is here so that we at least hit this function at least once // this gaves the character the chance to leave the ground, nulling out // self.groundentity break; case 1: if ( animdone ) { animdone = false; self.SetAnim( fallAnim.c_str(), EV_Anim_Done ); state = 2; } if ( self.groundentity ) state = 2; break; case 2: // // wait for the character to hit the ground // if ( self.groundentity ) { // // if we have an anim, we go to state 3 // animdone = false; self.SetAnim( deathAnim.c_str(), EV_Actor_Dead ); state = 3; } break; case 3: // // we are on the ground and waiting for our landing animation to finish // if ( animdone ) { return BEHAVIOR_SUCCESS; } break; } return BEHAVIOR_EVALUATING; } void FallToDeath::End ( Actor &self ) { Event *ev; ev = new Event( EV_Killed ); ev->AddEntity( NULL ); ev->AddFloat( 250.0f ); ev->AddEntity( NULL ); ev->AddInteger( MOD_FALLING ); ev->AddInteger( 1 ); self.PostEvent( ev , 0.0f ); } /**************************************************************************** JumpToPathNode Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, JumpToPathNode, NULL ) { { &EV_Behavior_Args, &JumpToPathNode::SetArgs }, { NULL, NULL } }; void JumpToPathNode::SetArgs( Event *ev ) { movegoal = thePathManager.FindNode( ev->GetString( 1 ) ); if ( movegoal ) jump.SetGoal( movegoal->origin ); else { Entity *entity = ev->GetEntity( 1 ); if ( entity ) jump.SetEntity( entity ); } if ( ev->NumArgs() > 1 ) jump.SetLaunchAngle( ev->GetFloat( 2 ) ); } void JumpToPathNode::Begin( Actor &self ) { jump.Begin( self ); } BehaviorReturnCode_t JumpToPathNode::Evaluate( Actor &self ) { if ( jump.Evaluate( self ) != Steering::SUCCESS ) return BEHAVIOR_EVALUATING; return BEHAVIOR_SUCCESS; } void JumpToPathNode::End( Actor &self ) { jump.End( self ); } /**************************************************************************** LeapToEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, LeapToEnemy, NULL ) { { &EV_Behavior_Args, &LeapToEnemy::SetArgs }, { NULL, NULL } }; void LeapToEnemy::SetArgs ( Event *ev ) { jump.SetLaunchAngle( ev->GetFloat( 1 ) ); } void LeapToEnemy::Begin ( Actor &self ) { Entity* ent = self.enemyManager->GetCurrentEnemy(); jump.SetEntity(ent); jump.Begin(self); } BehaviorReturnCode_t LeapToEnemy::Evaluate ( Actor &self ) { if ( jump.Evaluate( self ) == Steering::EVALUATING ) return BEHAVIOR_EVALUATING; return BEHAVIOR_SUCCESS; } void LeapToEnemy::End ( Actor &self ) { jump.End(self); } /**************************************************************************** FlyToPoint Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyToPoint, NULL ) { { NULL, NULL } }; FlyToPoint::FlyToPoint() { turn_speed = 10.0; old_turn_speed = turn_speed; speed = 480.0; random_allowed = true; force_goal = false; adjustYawAndRoll = true; offsetOrigin = false; avoidtime = 0; stuck = 0; use_temp_goal = false; old_forward_speed = 0; } void FlyToPoint::SetTurnSpeed( float new_turn_speed ) { turn_speed = new_turn_speed; } void FlyToPoint::SetGoalPoint( const Vector &goal_point ) { if ( goal_point != goal ) avoidtime = 0; goal = goal_point; } void FlyToPoint::SetRandomAllowed( qboolean allowed ) { random_allowed = allowed; } void FlyToPoint::SetSpeed( float speed_value ) { speed = speed_value; } void FlyToPoint::ForceGoal( void ) { force_goal = true; } void FlyToPoint::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyToPoint::Begin ( Actor &self ) { avoidtime = 0; old_forward_speed = self.movementSubsystem->getForwardSpeed(); stuck = 0; use_temp_goal = false; } BehaviorReturnCode_t FlyToPoint::Evaluate ( Actor &self ) { trace_t trace; Vector dir; Vector ang; float time; float length; float old_yaw; float old_pitch; float x_offset = 0; float y_offset = 0; //float z_offset = 0; if (offsetOrigin) { x_offset = self.centroid.x - self.origin.x; y_offset = self.centroid.y - self.origin.y; //z_offset = self.centroid.z - self.origin.z; self.origin.x+=x_offset; self.origin.y+=y_offset; //self.origin.z+=z_offset; } if ( self.movementSubsystem->getLastMove() != STEPMOVE_OK ) stuck++; else stuck = 0; if ( stuck > 1 || ( avoidtime <= level.time ) ) { time = G_Random( .3f ) + .3f; use_temp_goal = false; if ( !force_goal ) { trace = G_Trace( self.origin, self.mins, self.maxs, goal, &self, self.edict->clipmask, false, "FlyToPoint" ); if ( ( trace.fraction < 0.5f ) || ( stuck > 2 ) ) { old_turn_speed = self.movementSubsystem->getTurnSpeed(); self.movementSubsystem->setTurnSpeed( 60 ); temp_goal = ChooseRandomDirection( self, goal, &time, MASK_WATER, false ); self.movementSubsystem->setTurnSpeed( old_turn_speed ); use_temp_goal = true; avoidtime = level.time + time; stuck = 0; } else { goal = trace.endpos; avoidtime = level.time + time; } } else { avoidtime = level.time + time; } if ( use_temp_goal ) dir = temp_goal - self.origin; else dir = goal - self.origin; length = dir.length(); dir.normalize(); ang = dir.toAngles(); if ( ( length > 150.0f ) && random_allowed && !use_temp_goal ) { //ang[YAW] += G_Random( 20 ) - 5.0; //ang[PITCH] += G_Random( 20 ) - 5.0; } target_angle = ang; target_angle[YAW] = AngleNormalize360( target_angle[YAW] ); target_angle[PITCH] = AngleNormalize360( target_angle[PITCH] ); } if ( (self.angles[YAW] != target_angle[YAW]) || (self.angles[PITCH] != target_angle[PITCH]) ) { self.movementSubsystem->setForwardSpeed( speed * 0.8f ); } else { self.movementSubsystem->setForwardSpeed( speed ); } old_yaw = self.angles[YAW]; old_pitch = self.angles[PITCH]; ang[YAW] = LerpAngle( self.angles[YAW], target_angle[YAW], turn_speed ); ang[PITCH] = LerpAngle( self.angles[PITCH], target_angle[PITCH], turn_speed ); ang[ROLL] = 0; if( adjustYawAndRoll ) { if ( ( AngleDelta( ang[YAW], old_yaw ) > 0.0f ) && ( ( ang[ROLL] > 315.0f ) || ( ang[ROLL] <= 45.0f ) ) ) { ang[ROLL] += 5.0f; } else if ( ( AngleDelta( ang[YAW], old_yaw ) < 0.0f ) && ( ( ang[ROLL] < 45.0f ) || ( ang[ROLL] >= 315.0f ) ) ) { ang[ROLL] -= 5.0f; } else { if ( ang[ROLL] != 0.0f ) { if ( ang[ROLL] < 5.0f || ang[ROLL] > 355.0f ) { ang[ROLL] = 0.0f; } else { if ( ang[ROLL] < 180.0f ) ang[ROLL] += 5.0f; else ang[ROLL] -= 5.0f; } } } } ang[YAW] = AngleNormalize360( ang[YAW] ); ang[PITCH] = AngleNormalize360( ang[PITCH] ); ang[ROLL] = AngleNormalize360( ang[ROLL] ); // Don't get stuck if still turning if ( ( AngleDelta( ang[YAW], old_yaw ) > .5 ) || ( AngleDelta( ang[YAW], old_yaw ) < -.5 ) || ( AngleDelta( ang[PITCH], old_pitch ) > .5 ) || ( AngleDelta( ang[PITCH], old_pitch ) < -.5 ) ) { stuck = 0; } self.setAngles( ang ); if (offsetOrigin) { self.origin.x-=x_offset; self.origin.y-=y_offset; //self.origin.z-=z_offset; } return BEHAVIOR_EVALUATING; } float FlyToPoint::LerpAngle( float old_angle, float new_angle, float lerp_amount ) { float diff; float abs_diff; float lerp_angle; new_angle = AngleNormalize360( new_angle ); old_angle = AngleNormalize360( old_angle ); diff = new_angle - old_angle; if ( diff > 180.0f ) { diff -= 360.0f; } if ( diff < -180.0f ) { diff += 360.0f; } lerp_angle = old_angle; abs_diff = diff; if ( abs_diff < 0.0f ) { abs_diff = -abs_diff; } if ( abs_diff < lerp_amount ) { lerp_amount = abs_diff; } if ( diff < 0.0f ) { lerp_angle -= lerp_amount; } else if ( diff > 0.0f ) { lerp_angle += lerp_amount; } lerp_angle = AngleNormalize360( lerp_angle ); return lerp_angle; } void FlyToPoint::End ( Actor &self ) { self.movementSubsystem->setForwardSpeed( old_forward_speed ); } /**************************************************************************** FlyCloseToEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCloseToEnemy, NULL ) { { &EV_Behavior_Args, &FlyCloseToEnemy::SetArgs }, { NULL, NULL } }; FlyCloseToEnemy::FlyCloseToEnemy() { speed = 0; turn_speed = 10.0; anim = "fly"; adjustPitch = true; next_goal_time = 0; } void FlyCloseToEnemy::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) turn_speed = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); if (ev->NumArgs() > 3 ) fly.setAdjustYawAndRoll( ev->GetBoolean( 4 ) ); if (ev->NumArgs() > 4 ) adjustPitch = ev->GetBoolean( 5 ); } void FlyCloseToEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCloseToEnemy::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); if ( speed ) fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyCloseToEnemy::Evaluate ( Actor &self ) { Vector goal; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) ) return BEHAVIOR_SUCCESS; if ( next_goal_time <= level.time ) { goal = currentEnemy->centroid; if (!adjustPitch) { goal.z = self.origin.z; } //goal[0] += G_Random( 30 ) - 15.0; //goal[1] += G_Random( 30 ) - 15.0; //goal[2] += G_Random( 60 ) - 30.0; fly.SetGoalPoint( goal ); next_goal_time = level.time + .5f; } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyCloseToEnemy::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyCloseToPlayer Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCloseToPlayer, NULL ) { { &EV_Behavior_Args, &FlyCloseToPlayer::SetArgs }, { NULL, NULL } }; FlyCloseToPlayer::FlyCloseToPlayer() { speed = 0; turn_speed = 10.0; anim = "fly"; next_goal_time = 0; } void FlyCloseToPlayer::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) turn_speed = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); } void FlyCloseToPlayer::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCloseToPlayer::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); if ( speed ) fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyCloseToPlayer::Evaluate ( Actor &self ) { Vector goal; Entity *player; player = g_entities[ 0 ].entity; if ( !self.IsEntityAlive( player ) ) return BEHAVIOR_SUCCESS; if ( next_goal_time <= level.time ) { goal = player->centroid; //goal[0] += G_Random( 30 ) - 15.0; //goal[1] += G_Random( 30 ) - 15.0; //goal[2] += G_Random( 60 ) - 30.0; fly.SetGoalPoint( goal ); next_goal_time = level.time + .5f; } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyCloseToPlayer::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyCloseToParent Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCloseToParent, NULL ) { { &EV_Behavior_Args, &FlyCloseToParent::SetArgs }, { NULL, NULL } }; FlyCloseToParent::FlyCloseToParent() { speed = 0.0f; turn_speed = 10.0f; anim = "fly"; next_goal_time = 0.0f; } void FlyCloseToParent::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) turn_speed = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); } void FlyCloseToParent::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCloseToParent::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); if ( speed ) fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyCloseToParent::Evaluate ( Actor &self ) { Vector goal; if ( !self.spawnparent || !self.spawnparent->isSubclassOf( Actor ) ) return BEHAVIOR_SUCCESS; if ( next_goal_time <= level.time ) { Actor* act; act = (Actor*)self.spawnparent; goal = act->centroid; fly.SetGoalPoint( goal ); next_goal_time = level.time + .5f; } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyCloseToParent::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyDescend Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyDescend, NULL ) { { &EV_Behavior_Args, &FlyDescend::SetArgs }, { NULL, NULL } }; FlyDescend::FlyDescend() { anim = "fly"; height = 500; speed = 0; next_height_check = level.time + 2.0f; last_check_height = 0.0f; } void FlyDescend::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) height = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); } void FlyDescend::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyDescend::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return; fly.Begin( self ); height = currentEnemy->origin.z; goal = self.origin; goal.z = height; fly.SetTurnSpeed( 1.0f ); fly.SetGoalPoint( goal ); last_check_height = self.origin.z; if ( speed ) fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyDescend::Evaluate ( Actor &self ) { if ( self.origin.z <= height || !self.enemyManager->GetCurrentEnemy()) { return BEHAVIOR_SUCCESS; } if ( next_height_check < level.time ) { if ( self.origin.z < ( last_check_height + 25.0f ) ) return BEHAVIOR_SUCCESS; next_height_check = level.time + 2.0f; last_check_height = self.origin.z; } if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) fly.SetGoalPoint( goal ); fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyDescend::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyWander Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyWander, NULL ) { { &EV_Behavior_Args, &FlyWander::SetArgs }, { NULL, NULL } }; FlyWander::FlyWander() { turn_speed = 10.0; anim = "fly"; change_course_time = 5.0; speed = 200; try_to_go_up = false; next_change_course_time = 0; original_z = 0; } void FlyWander::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) speed = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) turn_speed = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) change_course_time = ev->GetFloat( 4 ); if ( ev->NumArgs() > 4 ) try_to_go_up = ev->GetBoolean( 5 ); if (ev->NumArgs() > 5 ) fly.setAdjustYawAndRoll( ev->GetBoolean( 6 ) ); if (ev->NumArgs() > 6 ) fly.setOffsetOrigin( ev->GetBoolean ( 7 ) ); } void FlyWander::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyWander::Begin ( Actor &self ) { original_z = self.origin.z; if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyWander::Evaluate ( Actor &self ) { trace_t trace; Vector dir; float length; int goal_try; Vector temp_goal; float max_dist = 0.0f; dir = goal - self.origin; length = dir.length(); if ( ( next_change_course_time <= level.time ) || ( length < 100.0f ) ) //self.lastmove != STEPMOVE_OK ) { for( goal_try = 0 ; goal_try < 5 ; goal_try++ ) { temp_goal = self.origin; temp_goal[0] += G_Random( 10000.0f ) - 5000.0f; temp_goal[1] += G_Random( 10000.0f ) - 5000.0f; if ( try_to_go_up ) temp_goal[2] += G_Random( 1000.0f ) - 250.0f; else temp_goal[2] += G_Random( 100.0f ) - 50.0f; trace = G_Trace( self.origin, self.mins, self.maxs, temp_goal, &self, self.edict->clipmask, false, "FlyWander" ); temp_goal = trace.endpos; dir = temp_goal - self.origin; length = dir.length(); if ( length > max_dist ) { max_dist = length; goal = temp_goal; if ( length > 1000.0f ) break; } } fly.SetGoalPoint( goal ); next_change_course_time = level.time + change_course_time; } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyWander::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyToNode Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyToNode, NULL ) { { &EV_Behavior_Args, &FlyToNode::SetArgs }, { NULL, NULL } }; FlyToNode::FlyToNode() { turn_speed = 10.0; anim = "fly"; speed = 200; NumberOfNodes = 0; } void FlyToNode::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { speed = ev->GetFloat( 2 ); } if ( ev->NumArgs() > 2 ) { turn_speed = ev->GetFloat( 3 ); } if ( ev->NumArgs() > 3 ) { NodeType = ev->GetString( 4 ); } if ( ev->NumArgs() > 4 ) { NumberOfNodes = ev->GetInteger( 5 ); } } void FlyToNode::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyToNode::Begin ( Actor &self ) { if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyToNode::Evaluate ( Actor &self ) { Vector dir; Vector angles; str pathnode_name; PathNode *goal; Vector teleport_position; Vector attack_position; int bestNode = 0; float bestDistance = -1; for(int i = 0; i <= NumberOfNodes; i++) { pathnode_name = NodeType + i; goal = thePathManager.FindNode( pathnode_name ); if( goal ) { float distanceTest = self.origin.length() - goal->origin.length(); if ( bestDistance < 0.0f ) { bestDistance = distanceTest; bestNode = i; } else if (distanceTest < bestDistance ) { bestDistance = distanceTest; bestNode = i; } } } pathnode_name = NodeType + bestNode; goal = thePathManager.FindNode( pathnode_name ); if ( !goal ) { gi.WDPrintf( "Can't find position %s\n", pathnode_name.c_str() ); return BEHAVIOR_SUCCESS; } fly.SetGoalPoint( goal->origin ); fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyToNode::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyToRandomNode Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyToRandomNode, NULL ) { { &EV_Behavior_Args, &FlyToRandomNode::SetArgs }, { NULL, NULL } }; FlyToRandomNode::FlyToRandomNode() { turn_speed = 10.0; anim = "fly"; speed = 200; CurrentNode = -1; NeedNextNode = true; NumberOfNodes = 0; goal = NULL; } void FlyToRandomNode::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { speed = ev->GetFloat( 2 ); } if ( ev->NumArgs() > 2 ) { turn_speed = ev->GetFloat( 3 ); } if ( ev->NumArgs() > 3 ) { NodeType = ev->GetString( 4 ); } if ( ev->NumArgs() > 4 ) { NumberOfNodes = ev->GetInteger( 5 ); } } void FlyToRandomNode::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyToRandomNode::Begin ( Actor &self ) { if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyToRandomNode::Evaluate ( Actor &self ) { str pathnode_name; Vector dir; if(NeedNextNode) { int x = (int)G_Random( (float)NumberOfNodes ); if (x == CurrentNode) x++; CurrentNode = x; pathnode_name = NodeType + CurrentNode; goal = thePathManager.FindNode( pathnode_name ); NeedNextNode = false; } if ( !goal ) { gi.WDPrintf( "Can't find position %s\n", pathnode_name.c_str() ); return BEHAVIOR_SUCCESS; } fly.SetGoalPoint( goal->origin ); fly.Evaluate( self ); dir = self.origin - goal->origin; if ( dir.length() < 100.0f ) { NeedNextNode = true; } return BEHAVIOR_EVALUATING; } void FlyToRandomNode::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyToNodeNearestPlayer Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyToNodeNearestPlayer, NULL ) { { &EV_Behavior_Args, &FlyToNodeNearestPlayer::SetArgs }, { NULL, NULL } }; FlyToNodeNearestPlayer::FlyToNodeNearestPlayer() { turn_speed = 10.0; anim = "fly"; speed = 200; CurrentNode = -1; NeedNextNode = true; NumberOfNodes = 0; goal = NULL; } void FlyToNodeNearestPlayer::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { speed = ev->GetFloat( 2 ); } if ( ev->NumArgs() > 2 ) { turn_speed = ev->GetFloat( 3 ); } if ( ev->NumArgs() > 3 ) { NodeType = ev->GetString( 4 ); } if ( ev->NumArgs() > 4 ) { NumberOfNodes = ev->GetInteger( 5 ); } } void FlyToNodeNearestPlayer::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyToNodeNearestPlayer::Begin ( Actor &self ) { if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyToNodeNearestPlayer::Evaluate ( Actor &self ) { str pathnode_name; Vector dir; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if(NeedNextNode) { int ClosestNode = 0; float CheckLen = -1; PathNode* Pnode = 0; for ( int i = 1; i <= NumberOfNodes; i++ ) { pathnode_name = NodeType + i; Pnode = thePathManager.FindNode(pathnode_name); if ( !Pnode ) { gi.WDPrintf( "Can't find Pathnode %s\n", pathnode_name.c_str() ); return BEHAVIOR_SUCCESS; } if( currentEnemy) { dir = Pnode->origin - currentEnemy->centroid; } else { Entity* player = g_entities[0].entity; dir = Pnode->origin - player->centroid; } if ( CheckLen < 0.0f ) { CheckLen = dir.length(); } else { if(dir.length() < CheckLen) { CheckLen = dir.length(); ClosestNode = i; } } } pathnode_name = NodeType + ClosestNode; goal = thePathManager.FindNode( pathnode_name ); NeedNextNode = false; } fly.SetGoalPoint( goal->origin ); fly.Evaluate( self ); dir = self.origin - goal->origin; if ( dir.length() < 100.0f ) { NeedNextNode = true; } return BEHAVIOR_EVALUATING; } void FlyToNodeNearestPlayer::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyNodePath Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyNodePath, NULL ) { { &EV_Behavior_Args, &FlyNodePath::SetArgs }, { NULL, NULL } }; FlyNodePath::FlyNodePath() { turn_speed = 10.0f; anim = "fly"; speed = 200.0f; CurrentNode = -1; NeedNextNode = true; NumberOfNodes = 0; goal = NULL; } void FlyNodePath::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { speed = ev->GetFloat( 2 ); } if ( ev->NumArgs() > 2 ) { turn_speed = ev->GetFloat( 3 ); } if ( ev->NumArgs() > 3 ) { NodeType = ev->GetString( 4 ); } if ( ev->NumArgs() > 4 ) { NumberOfNodes = ev->GetInteger( 5 ); } } void FlyNodePath::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyNodePath::Begin ( Actor &self ) { if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); fly.SetTurnSpeed( turn_speed ); fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyNodePath::Evaluate ( Actor &self ) { str pathnode_name; Vector dir; if(NeedNextNode) { CurrentNode++; if(CurrentNode > NumberOfNodes) CurrentNode = 0; pathnode_name = NodeType + CurrentNode; goal = thePathManager.FindNode( pathnode_name ); NeedNextNode = false; } if ( !goal ) { gi.WDPrintf( "Can't find position %s\n", pathnode_name.c_str() ); return BEHAVIOR_SUCCESS; } fly.SetGoalPoint( goal->origin ); fly.Evaluate( self ); dir = self.origin - goal->origin; if ( dir.length() < 100.0f ) { NeedNextNode = true; } if ( (CurrentNode == NumberOfNodes) && NeedNextNode ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void FlyNodePath::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyCircle Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCircle, NULL ) { { &EV_Behavior_Args, &FlyCircle::SetArgs }, { NULL, NULL } }; FlyCircle::FlyCircle() { anim = "fly"; fly_clockwise = true; circle_player = false; original_z = 0.0f; } void FlyCircle::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); fly_clockwise = ev->GetBoolean( 2 ); if (ev->NumArgs() > 2 ) fly.setAdjustYawAndRoll( ev->GetBoolean( 3 ) ); if (ev->NumArgs() > 3 ) circle_player = ev->GetBoolean ( 4 ); else circle_player = false; if (ev->NumArgs() > 4 ) fly.SetSpeed( ev->GetFloat( 5 ) ); } void FlyCircle::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCircle::Begin ( Actor &self ) { original_z = self.origin.z; if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( 5.0f ); } BehaviorReturnCode_t FlyCircle::Evaluate ( Actor &self ) { Vector goal; trace_t trace; Vector dir; Vector angle; Vector left; qboolean too_far = false; Vector new_dir; Vector fly_dir; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) && !circle_player ) { return BEHAVIOR_SUCCESS; } if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) { fly.SetTurnSpeed( 5.0f ); if (circle_player) { Player *player = NULL; Player *temp_player = NULL; // Make sure the player is alive and well for(int i = 0; i < game.maxclients; i++) { temp_player = GetPlayer(i); // don't target while player is not in the game or he's in notarget if ( temp_player && !( temp_player->flags & FL_NOTARGET ) ) { player = temp_player; break; } } if ( !player ) return BEHAVIOR_SUCCESS; dir = player->centroid - self.origin; dir.z = 0.0f; if ( dir.length() > ( ( self.origin.z - player->centroid.z ) / .5f ) ) { too_far = true; } } else { dir = currentEnemy->centroid - self.origin; dir.z = 0.0f; if ( dir.length() > ( ( self.origin.z - currentEnemy->centroid.z ) / 2.0f ) ) { too_far = true; } } angle = dir.toAngles(); angle.AngleVectors( NULL, &left, NULL ); if ( fly_clockwise ) fly_dir = left; else fly_dir = left * -1.0f; dir.normalize(); if ( too_far ) { new_dir = ( fly_dir * 0.5f ) + ( dir * 0.5f ); new_dir.normalize(); } else { new_dir = fly_dir; } //goal = self.origin + new_dir * 200; goal = self.origin + ( new_dir * 700.0f ); trace = G_Trace( self.origin, self.mins, self.maxs, goal, &self, self.edict->clipmask, false, "FlyCircle" ); if ( trace.fraction < 1.0f ) { if ( too_far ) trace.fraction /= 2.0f; new_dir = ( fly_dir * trace.fraction ) + ( dir * ( 1.0f - trace.fraction ) ); new_dir.normalize(); //goal = self.origin + new_dir * 200; goal = self.origin + ( new_dir * 700.0f ); } else { goal = trace.endpos; } fly.SetGoalPoint( goal ); } else { fly.SetTurnSpeed( 20.0f ); } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyCircle::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyStrafe Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyStrafe, NULL ) { { &EV_Behavior_Args, &FlyStrafe::SetArgs }, { NULL, NULL } }; FlyStrafe::FlyStrafe() { anim = "fly"; } void FlyStrafe::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); speed = ev->GetFloat( 2 ); right = ev->GetBoolean( 3 ); roll = ev->GetFloat( 4 ); } void FlyStrafe::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyStrafe::Begin ( Actor &self ) { self.SetAnim( anim ); self.movementSubsystem->setForwardSpeed( speed ); /* if ( right ) self.FlightDirection = FLY_RIGHT; else self.FlightDirection = FLY_LEFT; */ } BehaviorReturnCode_t FlyStrafe::Evaluate ( Actor &self ) { Vector dir; Vector delta; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; dir = currentEnemy->centroid - self.origin; dir = dir.toAngles(); if ( right ) dir[ROLL] -= roll; else dir[ROLL] += roll; self.setAngles( dir ); return BEHAVIOR_EVALUATING; } void FlyStrafe::End ( Actor &self ) { //self.FlightDirection = FLY_FORWARD; } /**************************************************************************** FlyCircleRandomPoint Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCircleRandomPoint, NULL ) { { &EV_Behavior_Args, &FlyCircleRandomPoint::SetArgs }, { NULL, NULL } }; FlyCircleRandomPoint::FlyCircleRandomPoint() { anim = "fly"; //original_z = 0.0f; fly_clockwise = true; change_course_time = 5; try_to_go_up = false; next_change_course_time = 0; } void FlyCircleRandomPoint::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); fly_clockwise = ev->GetBoolean( 2 ); if ( ev->NumArgs() > 2 ) change_course_time = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) try_to_go_up = ev->GetBoolean( 4 ); if (ev->NumArgs() > 4 ) fly.setAdjustYawAndRoll( ev->GetBoolean( 5 ) ); if (ev->NumArgs() > 5 ) fly.SetSpeed( ev->GetFloat( 6 ) ); } void FlyCircleRandomPoint::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCircleRandomPoint::Begin ( Actor &self ) { //original_z = self.origin.z; if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( 5.0f ); fly.ForceGoal(); } BehaviorReturnCode_t FlyCircleRandomPoint::Evaluate ( Actor &self ) { trace_t trace; Vector dir; Vector angle; Vector left; qboolean too_far = false; Vector new_dir; Vector fly_dir; float length; int goal_try; Vector temp_goal; float max_dist = 0; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) ) { return BEHAVIOR_SUCCESS; } if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) { fly.SetTurnSpeed( 5.0f ); if ( next_change_course_time <= level.time ) //self.lastmove != STEPMOVE_OK ) { dir = goal - self.origin; dir.z = 0; length = dir.length(); fly_clockwise = !fly_clockwise; for( goal_try = 0 ; goal_try < 5 ; goal_try++ ) { temp_goal = self.origin; //temp_goal[0] += G_Random( 10000 ) - 2500.0; //temp_goal[1] += G_Random( 10000 ) - 2500.0; temp_goal[0] += G_Random( 10000.0f ) + 10000.0f; temp_goal[1] += G_Random( 10000.0f ) + 10000.0f; if ( try_to_go_up ) temp_goal[2] += G_Random( 1000.0f ) - 250.0f; else temp_goal[2] += G_Random( 100.0f ) - 50.0f; trace = G_Trace( self.origin, self.mins, self.maxs, temp_goal, &self, self.edict->clipmask, false, "FlyCircleRandomPoint" ); temp_goal = trace.endpos; dir = temp_goal - self.origin; length = dir.length(); if ( length > max_dist ) { max_dist = length; goal = temp_goal; if ( length > 1000.0f ) break; } } next_change_course_time = (level.time + change_course_time); } if ( dir.length() > ( (self.origin.z - goal.z ) / 2.0f ) ) { too_far = true; } angle = dir.toAngles(); angle.AngleVectors( NULL, &left, NULL ); if ( fly_clockwise ) fly_dir = left; else fly_dir = left * -1.0f; dir.normalize(); if ( too_far ) { new_dir = ( fly_dir * 0.5f ) + ( dir * 0.5f ); new_dir.normalize(); } else { new_dir = fly_dir; } goal = self.origin + ( new_dir * 30.0f ); //goal = self.origin + new_dir * 100; trace = G_Trace( self.origin, self.mins, self.maxs, goal, &self, self.edict->clipmask, false, "FlyCircleRandomPoint" ); if ( trace.fraction < 1.0f ) { if ( too_far ) trace.fraction /= 2.0f; new_dir = ( fly_dir * trace.fraction ) + ( dir * ( 1.0f - trace.fraction ) ); new_dir.normalize(); //goal = self.origin + new_dir * 200; goal = self.origin + ( new_dir * 100.0f ); } else { goal = trace.endpos; } fly.SetGoalPoint( goal ); } else { fly.SetTurnSpeed( 30.0f ); } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyCircleRandomPoint::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyDive Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyDive, NULL ) { { &EV_Behavior_Args, &FlyDive::SetArgs }, { NULL, NULL } }; FlyDive::FlyDive() { anim = "fly"; speed = 2000; damage = 0; } void FlyDive::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { speed = ev->GetFloat( 2 ); } if ( ev->NumArgs() > 2 ) { damage = ev->GetFloat( 3 ); } } void FlyDive::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyDive::Begin ( Actor &self ) { if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return; if ( !self.IsEntityAlive( currentEnemy ) ) { return; } goal = currentEnemy->centroid - self.origin; goal.normalize(); goal *= 10000.0f; goal += self.origin; self.movementSubsystem->setDiveDir( self.origin ); fly.SetGoalPoint( goal ); fly.SetTurnSpeed( 100.0f ); fly.SetSpeed( speed ); fly.SetRandomAllowed( false ); fly.ForceGoal(); } BehaviorReturnCode_t FlyDive::Evaluate ( Actor &self ) { trace_t trace; Vector dir; Entity *hit_entity; qboolean stuck; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) ) return BEHAVIOR_SUCCESS; if ( self.origin.z < ( currentEnemy->origin.z + 50.0f ) ) return BEHAVIOR_SUCCESS; if ( (self.movementSubsystem->getLastMove() == STEPMOVE_STUCK ) || ( self.movementSubsystem->getLastMove() == STEPMOVE_BLOCKED_BY_WATER ) ) { stuck = true; dir = self.movementSubsystem->getMoveDir() * 100.0f; trace = G_Trace( self.origin, self.mins, self.maxs, self.origin + dir, &self, self.edict->clipmask, false, "FlyDive" ); if ( trace.entityNum != ENTITYNUM_NONE ) { hit_entity = G_GetEntity( trace.entityNum ); // Damage entity hit //if ( hit_entity->isSubclassOf( Sentient ) ) if ( hit_entity->takedamage ) { //hit_entity->Damage( &self, &self, damage, Vector (0, 0, 0), Vector (0, 0, 0), Vector (0, 0, 0), 0, 0, MOD_CRUSH ); dir.normalize(); hit_entity->Damage( &self, &self, damage, self.centroid, dir, vec_zero, 0, 0, MOD_CRUSH ); self.AddStateFlag( STATE_FLAG_MELEE_HIT ); stuck = false; } } // Make sure we really are still stuck if ( trace.fraction > 0.05f ) stuck = false; self.angles[PITCH] = 0.0f; self.setAngles( self.angles ); if ( stuck ) self.AddStateFlag( STATE_FLAG_STUCK ); return BEHAVIOR_SUCCESS; } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyDive::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyCharge Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCharge, NULL ) { { &EV_Behavior_Args, &FlyCharge::SetArgs }, { NULL, NULL } }; FlyCharge::FlyCharge() { anim = "fly"; speed = 2000; damage = 0; } void FlyCharge::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) { speed = ev->GetFloat( 2 ); } if ( ev->NumArgs() > 2 ) { damage = ev->GetFloat( 3 ); } } void FlyCharge::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCharge::Begin ( Actor &self ) { if ( anim.length() ) self.SetAnim( anim ); fly.Begin( self ); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return; if ( !self.IsEntityAlive( currentEnemy ) ) { return; } goal = currentEnemy->centroid - self.origin; goal.normalize(); goal *= 10000.0f; goal += self.origin; goal.z = self.origin.z; self.movementSubsystem->setDiveDir( self.origin ); fly.SetGoalPoint( goal ); fly.SetTurnSpeed( 100.0f ); fly.SetSpeed( speed ); fly.SetRandomAllowed( false ); fly.ForceGoal(); } BehaviorReturnCode_t FlyCharge::Evaluate ( Actor &self ) { trace_t trace; Vector dir; Entity *hit_entity; qboolean stuck; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) ) return BEHAVIOR_SUCCESS; if ( ( self.movementSubsystem->getLastMove() == STEPMOVE_STUCK ) || ( self.movementSubsystem->getLastMove() == STEPMOVE_BLOCKED_BY_WATER ) ) { stuck = true; dir = self.movementSubsystem->getMoveDir() * 100.0f; trace = G_Trace( self.origin, self.mins, self.maxs, self.origin + dir, &self, self.edict->clipmask, false, "FlyDive" ); if ( trace.entityNum != ENTITYNUM_NONE ) { hit_entity = G_GetEntity( trace.entityNum ); // Damage entity hit //if ( hit_entity->isSubclassOf( Sentient ) ) if ( hit_entity->takedamage ) { //hit_entity->Damage( &self, &self, damage, Vector (0, 0, 0), Vector (0, 0, 0), Vector (0, 0, 0), 0, 0, MOD_CRUSH ); dir.normalize(); hit_entity->Damage( &self, &self, damage, self.centroid, dir, vec_zero, 0, 0, MOD_CRUSH ); self.AddStateFlag( STATE_FLAG_MELEE_HIT ); stuck = false; } } // Make sure we really are still stuck if ( trace.fraction > 0.05f ) stuck = false; self.angles[PITCH] = 0.0f; self.setAngles( self.angles ); if ( stuck ) self.AddStateFlag( STATE_FLAG_STUCK ); return BEHAVIOR_SUCCESS; } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyCharge::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlyClimb Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyClimb, NULL ) { { &EV_Behavior_Args, &FlyClimb::SetArgs }, { NULL, NULL } }; FlyClimb::FlyClimb() { anim = "fly"; height = 500; speed = 0; collision_buffer = 0; next_height_check = level.time + 2.0; last_check_height = 0.0f; } void FlyClimb::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) height = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); if ( ev->NumArgs() > 3 ) collision_buffer = ev->GetFloat( 1 ); } void FlyClimb::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyClimb::Begin ( Actor &self ) { Vector forward; trace_t trace; if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( currentEnemy ) height = currentEnemy->origin.z + height; else height = self.origin.z + height; self.angles.AngleVectors( &forward ); //Okay, here's what we are attempting. We are first going to get our initial point, //this will be our forward vector * height, then add the the height to the the Z. //Next we will trace out, to see how far along that vector we got. We will use our //trace.endpos as a goal position //Now, we don't want to use this goal position as a final vector, because if we ran into //something, then we will smack it when we get there... this would be bad, so, we run a //a vector from our origin to our goal position and get the length, we then shave some off of //this length and multiply it by another vector and add it to the origin, this will get us //our final position goal = forward * height; goal.z = height; goal += self.origin; trace = G_Trace( self.centroid, self.mins, self.maxs, goal, NULL, MASK_SOLID, false, "flyclimb" ); goal = trace.endpos; Vector dir; dir = self.origin - goal; float length; length = dir.length(); if ( collision_buffer ) length -= collision_buffer; else length -= (length / 10.0f); if ( length > 0.0f ) { goal = forward * length; goal.z = length; goal += self.origin; } //if ( !self.divedir.x && !self.divedir.y && !self.divedir.z ) // { // goal = forward * height; // goal.z = height; // goal += self.origin; // trace = G_Trace( self.centroid, self.mins, self.maxs, goal, NULL, MASK_SOLID, false, "flyclimb" ); // } //else // { // goal = self.divedir; // goal.z = height; // } fly.SetTurnSpeed( 10.0f ); fly.SetGoalPoint( goal ); last_check_height = self.origin.z; if ( speed ) fly.SetSpeed( speed ); } BehaviorReturnCode_t FlyClimb::Evaluate ( Actor &self ) { if ( self.origin.z >= height ) { return BEHAVIOR_SUCCESS; } if ( next_height_check < level.time ) { if ( self.origin.z < ( last_check_height + 25.0f ) ) return BEHAVIOR_SUCCESS; next_height_check = level.time + 2.0f; last_check_height = self.origin.z; } if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) fly.SetGoalPoint( goal ); fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void FlyClimb::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** FlySplinePath Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlySplinePath, NULL ) { { &EV_Behavior_Args, &FlySplinePath::SetArgs }, { NULL, NULL } }; FlySplinePath::FlySplinePath() { ent = NULL; clamp = true; ignoreAngles = false; splineAngles = true; havePath = false; startTime = 0.0f; } void FlySplinePath::SetArgs ( Event *ev ) { ent = ev->GetEntity( 1 ); ignoreAngles = ev->GetBoolean( 2 ); splineAngles = ev->GetBoolean( 3 ); clamp = ev->GetBoolean( 4 ); } void FlySplinePath::CreatePath ( SplinePath *path, splinetype_t type ) { SplinePath *node; splinePath.Clear(); splinePath.SetType( type ); node = path; while( node != NULL ) { splinePath.AppendControlPoint( node->origin, node->angles, node->speed ); node = node->GetNext(); if ( node == path ) { break; } } } void FlySplinePath::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlySplinePath::Begin ( Actor &self ) { if ( ent ) havePath = true; else return; if ( ent->isSubclassOf( SplinePath ) ) { SplinePath* path; path = (SplinePath*)(Entity*)ent; if ( clamp ) CreatePath( path, SPLINE_CLAMP ); else CreatePath( path, SPLINE_LOOP ); currentNode = path; if (currentNode->thread != "" ) { if ( !ExecuteThread( currentNode->thread ) ) { gi.Error( ERR_DROP, "FlySplinePath could not run thread '%s' from info_splinepath '%s'\n", currentNode->thread.c_str(), currentNode->targetname.c_str() ); } } if ( currentNode->triggertarget != "" ) { Entity *ent; Event *event; ent = NULL; do { ent = G_FindTarget( ent, currentNode->triggertarget.c_str() ); if ( !ent ) { break; } event = new Event( EV_Activate ); Actor* actorPtr = &self; Entity* entPtr = (Entity*)actorPtr; event->AddEntity( entPtr ); ent->PostEvent( event, 0.0f ); } while ( 1 ); } } startTime = level.time + self.currentSplineTime; } BehaviorReturnCode_t FlySplinePath::Evaluate ( Actor &self ) { Vector goal; Vector dir; Vector angles; if(!havePath) return BEHAVIOR_SUCCESS; goal = splinePath.Eval( (level.time - startTime) ); if ( goal == oldGoal ) { self.currentSplineTime = 0; return BEHAVIOR_SUCCESS; } oldGoal = goal; dir = goal - self.origin; angles = dir.toAngles(); self.setOrigin(goal); self.setAngles(angles); return BEHAVIOR_EVALUATING; } void FlySplinePath::End ( Actor &self ) { self.currentSplineTime = level.time - startTime; } /**************************************************************************** Land Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Land, NULL ) { { &EV_Behavior_Args, &Land::SetArgs }, { NULL, NULL } }; Land::Land() { anim = "fly"; } void Land::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void Land::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void Land::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } self.velocity = Vector(0, 0, -40); } BehaviorReturnCode_t Land::Evaluate ( Actor &self ) { self.angles[PITCH] = 0.0f; self.angles[ROLL] = 0.0f; self.setAngles( self.angles ); self.velocity.z -= 20.0f; if ( self.velocity.z < -200.0f ) { self.velocity.z = -200.0f; } if ( !self.groundentity ) return BEHAVIOR_EVALUATING; return BEHAVIOR_SUCCESS; } void Land::End ( Actor &self ) { } /**************************************************************************** Vertical Takeoff Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, VerticalTakeOff, NULL ) { { &EV_Behavior_Args, &VerticalTakeOff::SetArgs }, { NULL, NULL } }; VerticalTakeOff::VerticalTakeOff() { anim = "fly"; speed = 0; height = 0; } void VerticalTakeOff::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); speed = ev->GetFloat( 2 ); height = ev->GetFloat( 3 ); } void VerticalTakeOff::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void VerticalTakeOff::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } self.velocity.x = 0; self.velocity.y = 0; self.velocity.z = speed; } BehaviorReturnCode_t VerticalTakeOff::Evaluate ( Actor &self ) { self.angles[PITCH] = 0.0f; self.angles[ROLL] = 0.0f; self.setAngles( self.angles ); self.velocity.z += 20.0f; if ( self.velocity.z > 200.0f ) { self.velocity.z = 200.0f; } if( self.origin.z >= height ) return BEHAVIOR_SUCCESS; else return BEHAVIOR_EVALUATING; } void VerticalTakeOff::End ( Actor &self ) { } /**************************************************************************** Hover Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Hover, NULL ) { { &EV_Behavior_Args, &Hover::SetArgs }, { NULL, NULL } }; Hover::Hover() { anim = "fly"; } void Hover::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void Hover::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void Hover::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); } BehaviorReturnCode_t Hover::Evaluate ( Actor &self ) { //self.angles[PITCH] = 0; self.angles[ROLL] = 0; self.setAngles( self.angles ); self.movementSubsystem->setForwardSpeed( 0 ); return BEHAVIOR_EVALUATING; } void Hover::End ( Actor &self ) { fly.End(self); } /**************************************************************************** Wander Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Wander, NULL ) { { &EV_Behavior_Args, &Wander::SetArgs }, { NULL, NULL } }; void Wander::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void Wander::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nseek:\n" ); } void Wander::Begin ( Actor &self ) { avoidtime = 0; avoidvec = vec_zero; if ( anim.length() ) { self.SetAnim( anim ); } } BehaviorReturnCode_t Wander::Evaluate ( Actor &self ) { if ( ( self.movementSubsystem->getLastMove() != STEPMOVE_OK ) || ( avoidtime <= level.time ) ) { Vector dir; Vector ang; float time; time = 5.0f; //self.Chatter( "snd_idle", 4 ); avoidvec = ChooseRandomDirection( self, avoidvec, &time, 0, false ); self.movementSubsystem->setMovingBackwards( false ); avoidtime = level.time + time; } float maxSpeed = 100.0f; if ( self.movementSubsystem->getMoveSpeed() != 1.0f ) { maxSpeed = self.movementSubsystem->getMoveSpeed(); } self.movementSubsystem->Accelerate( self.movementSubsystem->SteerTowardsPoint( avoidvec, vec_zero, self.movementSubsystem->getMoveDir(), maxSpeed) ); return BEHAVIOR_EVALUATING; } void Wander::End ( Actor &self ) { } /**************************************************************************** GetCloseToEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, GetCloseToEnemy, NULL ) { { &EV_Behavior_Args, &GetCloseToEnemy::SetArgs }, { NULL, NULL } }; void GetCloseToEnemy::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) forever = ev->GetBoolean( 2 ); else forever = true; } void GetCloseToEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void GetCloseToEnemy::Begin ( Actor &self ) { Vector dir; dir = self.movementSubsystem->getAnimDir(); dir = dir.toAngles(); self.setAngles( dir ); if ( !anim.length() ) { anim = "run"; } if ( anim != self.animname || self.newanim.length() ) { self.SetAnim( anim, EV_Actor_NotifyBehavior ); } chase.Begin( self ); wander.Begin( self ); next_think_time = 0; } BehaviorReturnCode_t GetCloseToEnemy::Evaluate ( Actor &self ) { qboolean result; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( next_think_time <= level.time ) { if ( self.groundentity && ( self.groundentity->s.number == currentEnemy->entnum ) ) { wander.Evaluate( self ); result = true; } else { float radius=96.0f; chase.SetGoal( currentEnemy, radius, self ); result = chase.Evaluate( self ); } if ( self.GetActorFlag( ACTOR_FLAG_SIMPLE_PATHFINDING ) ) next_think_time = level.time + ( 2.0f * FRAMETIME ); else next_think_time = 0.0f; } else result = true; if ( !forever && !result ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void GetCloseToEnemy::End ( Actor &self ) { chase.End( self ); wander.End( self ); } /**************************************************************************** GetCloseToPlayer Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, GetCloseToPlayer, NULL ) { { &EV_Behavior_Args, &GetCloseToPlayer::SetArgs }, { NULL, NULL } }; void GetCloseToPlayer::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) forever = ev->GetBoolean( 2 ); else forever = true; if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); else speed = 100; } void GetCloseToPlayer::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void GetCloseToPlayer::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } if ( anim != self.animname || self.newanim.length() ) { self.SetAnim( anim, EV_Actor_NotifyBehavior ); } chase.Begin( self ); wander.Begin( self ); self.movementSubsystem->setForwardSpeed( speed ); next_think_time = 0; } BehaviorReturnCode_t GetCloseToPlayer::Evaluate ( Actor &self ) { qboolean result; Player *player = NULL; Player *temp_player = NULL; // Make sure the player is alive and well for(int i = 0; i < game.maxclients; i++) { temp_player = GetPlayer(i); // don't target while player is not in the game or he's in notarget if( temp_player && !( temp_player->flags & FL_NOTARGET ) ) { player = temp_player; break; } } if ( !player ) return BEHAVIOR_SUCCESS; if ( next_think_time <= level.time ) { if ( self.groundentity && ( self.groundentity->s.number == player->entnum ) ) { wander.Evaluate( self ); result = true; } else { float radius=96.0f; chase.SetGoal( player, radius, self ); result = chase.Evaluate( self ); } if ( self.GetActorFlag( ACTOR_FLAG_SIMPLE_PATHFINDING ) ) next_think_time = level.time + ( 2.0f * FRAMETIME ); else next_think_time = 0.0f; } else result = true; if ( !forever && !result ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void GetCloseToPlayer::End ( Actor &self ) { chase.End( self ); wander.End( self ); self.movementSubsystem->setForwardSpeed( 0 ); } /**************************************************************************** RetreatFromEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, RetreatFromEnemy, NULL ) { { &EV_Behavior_Args, &RetreatFromEnemy::SetArgs }, { NULL, NULL } }; void RetreatFromEnemy::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) forever = ev->GetBoolean( 2 ); else forever = true; } void RetreatFromEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void RetreatFromEnemy::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } if ( anim != self.animname || self.newanim.length() ) { self.SetAnim( anim, EV_Actor_NotifyBehavior ); } chase.Begin( self ); wander.Begin( self ); next_think_time = 0; } BehaviorReturnCode_t RetreatFromEnemy::Evaluate ( Actor &self ) { qboolean result; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( next_think_time <= level.time ) { if ( self.groundentity && ( self.groundentity->s.number == currentEnemy->entnum ) ) { wander.Evaluate( self ); result = true; } else { float currentZ = self.centroid.z; Vector FleeVec = (self.centroid - currentEnemy->centroid) + self.centroid; FleeVec.z = currentZ; trace_t trace; trace = G_Trace( self.centroid, self.mins, self.maxs, FleeVec, NULL, MASK_SOLID, false, "RetreatFromEnemy" ); float radius=96.0f; chase.SetGoal( trace.endpos, radius, self ); result = chase.Evaluate( self ); } if ( self.GetActorFlag( ACTOR_FLAG_SIMPLE_PATHFINDING ) ) next_think_time = level.time + ( 2.0f * FRAMETIME ); else next_think_time = 0.0f; } else result = true; if ( !forever && !result ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void RetreatFromEnemy::End ( Actor &self ) { chase.End( self ); wander.End( self ); } /**************************************************************************** GotoDeadEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, GotoDeadEnemy, NULL ) { { &EV_Behavior_Args, &GotoDeadEnemy::SetArgs }, { NULL, NULL } }; void GotoDeadEnemy::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void GotoDeadEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void GotoDeadEnemy::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } if ( anim != self.animname || self.newanim.length() ) { self.SetAnim( anim, EV_Actor_NotifyBehavior ); } chase.Begin( self ); } BehaviorReturnCode_t GotoDeadEnemy::Evaluate ( Actor &self ) { qboolean result; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !currentEnemy->deadflag ) return BEHAVIOR_SUCCESS; float radius=96.0f; chase.SetGoal( currentEnemy->origin, radius, self ); result = chase.Evaluate( self ); if ( !result ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void GotoDeadEnemy::End ( Actor &self ) { chase.End( self ); } /**************************************************************************** Investigate Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Investigate, NULL ) { { &EV_Behavior_Args, &Investigate::SetArgs }, { NULL, NULL } }; #define INVESTIGATE_MODE_INVESTIGATE 0 #define INVESTIGATE_MODE_LOOKAROUND 1 #define INVESTIGATE_MODE_RETURN 2 #define INVESTIGATE_MODE_TURN2 3 Investigate::Investigate() { investigate_time = 10.0f; moveanim = "run"; lookaroundanim = "idle"; curioustime = 0.0f; lookaroundtime = 2.0f; mode = INVESTIGATE_MODE_INVESTIGATE; return_to_original_location = true; start_yaw = 0.0f; } void Investigate::SetArgs ( Event *ev ) { moveanim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) investigate_time = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) return_to_original_location = ev->GetBoolean( 3 ); else return_to_original_location = true; if( ev->NumArgs() > 3 ) lookaroundanim = ev->GetString( 4 ); if( ev->NumArgs() > 4 ) lookaroundtime = ev->GetFloat( 5 ); } void Investigate::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); gi.Printf( "\nanim: %s\n", moveanim.c_str() ); gi.Printf( "curioustime: %f\n", curioustime ); gi.Printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z ); } void Investigate::Begin( Actor &self ) { // Note: the state machine for this character needs to use "forgetenemies" or it will get // stuck in a loop. Vector trace_start_pos; Vector trace_end_pos; PathNode *goal_node; // Find the goal position if( self.sensoryPerception->GetLastSoundType() == SOUNDTYPE_NONE ) { EntityPtr enemy = self.enemyManager->GetCurrentEnemy(); if( enemy != NULL ) { // Didn't hear a sound... must've seen someone goal = enemy->origin; } else { // Didn't hear or see anything? wtf?! Just hang out where you are, ai. goal = self.origin; } } else { // Heard a sound goal = self.sensoryPerception->GetNoisePosition(); } // Try to find a pathnode close to the goal position to ensure good pathfinding goal_node = self.NearestNodeInPVS( goal ); if ( goal_node ) { goal = goal_node->origin; } // Let anyone else who cares know that this guy is investigating self.SetActorFlag( ACTOR_FLAG_INVESTIGATING, true ); // Only search for a set amount of time before returning to non-investigative state curioustime = level.time + investigate_time; // Make some sounds? self.Chatter( "snd_investigate" ); // Setup movement code to move toward the goal position chase.Begin( self ); chase.SetGoal( goal, 96.0f, self ); // Turn toward ??? turnto.Begin( self ); // Don't start movement anim if it's so close there will be no actual movement if ( moveanim.length() && ( Vector::DistanceXY( goal, self.origin ) >= 100.0f ) ) { self.SetAnim( moveanim ); } start_pos = self.origin; start_yaw = self.angles[YAW]; } BehaviorReturnCode_t Investigate::Evaluate( Actor &self ) { Vector dir; Vector angles; Steering::ReturnValue steeringResult; switch ( mode ) { // Go to spot where the noise was heard case INVESTIGATE_MODE_INVESTIGATE: // Go to next mode if time elapsed or pathing failed steeringResult = chase.Evaluate( self ); if ( (steeringResult != Steering::EVALUATING) || curioustime < level.time ) { mode = INVESTIGATE_MODE_LOOKAROUND; self.SetAnim( lookaroundanim ); lookaroundtime_end = level.time + lookaroundtime; } else { // Go to next mode if close enough to goal position float dist = Vector::DistanceXY( goal, self.origin ); if( dist < 100.0f ) { mode = INVESTIGATE_MODE_LOOKAROUND; self.SetAnim( lookaroundanim ); lookaroundtime_end = level.time + lookaroundtime; } } break; // look around for stuff for a second case INVESTIGATE_MODE_LOOKAROUND: if( level.time > lookaroundtime_end ) { curioustime = level.time + (investigate_time * 2.0f ); mode = INVESTIGATE_MODE_RETURN; self.SetAnim( moveanim ); } break; // go back from whence you came, foul beast case INVESTIGATE_MODE_RETURN : if ( !return_to_original_location ) return BEHAVIOR_SUCCESS; // Return back to our original position chase.SetGoal( start_pos, 96.0f, self ); if ( chase.Evaluate( self ) != Steering::EVALUATING ) mode = INVESTIGATE_MODE_TURN2; if ( curioustime < level.time ) return BEHAVIOR_SUCCESS; break; // got stuck going back to start position, just face original direction? case INVESTIGATE_MODE_TURN2 : self.SetAnim( "idle" ); // Turn back to our original direction turnto.SetDirection( start_yaw ); if ( !turnto.Evaluate( self ) ) return BEHAVIOR_SUCCESS; break; } return BEHAVIOR_EVALUATING; } void Investigate::End ( Actor &self ) { self.SetActorFlag( ACTOR_FLAG_INVESTIGATING, false ); self.SetActorFlag( ACTOR_FLAG_NOISE_HEARD, false ); chase.End( self ); turnto.End( self ); } /**************************************************************************** TurnInvestigate Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TurnInvestigate, NULL ) { { &EV_Behavior_Args, &TurnInvestigate::SetArgs }, { NULL, NULL } }; void TurnInvestigate::SetArgs ( Event *ev ) { left_anim = ev->GetString( 1 ); right_anim = ev->GetString( 2 ); turn_speed = ev->GetFloat( 3 ); } void TurnInvestigate::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "goal: ( %f, %f, %f )\n", goal.x, goal.y, goal.z ); } void TurnInvestigate::Begin ( Actor &self ) { goal = self.sensoryPerception->GetNoisePosition(); self.SetActorFlag( ACTOR_FLAG_INVESTIGATING, true ); self.SetAnim( "idle" ); } BehaviorReturnCode_t TurnInvestigate::Evaluate ( Actor &self ) { str turn_anim_name; Vector dir; self.angles.AngleVectors( &dir ); Vector newSteeringForce = self.movementSubsystem->SteerTowardsPoint( goal, vec_zero, dir, 1.0f); // See if we have turned all of the way to the noise position if ( ( newSteeringForce[YAW] < .5f ) && ( newSteeringForce[YAW] > -.5f ) ) return BEHAVIOR_SUCCESS; // Make sure we are not turning faster than out turn speed if ( newSteeringForce[YAW] > turn_speed ) { newSteeringForce[YAW] = turn_speed; } if ( newSteeringForce[YAW] < -turn_speed ) { newSteeringForce[YAW] = -turn_speed; } newSteeringForce[PITCH] = 0.0f; newSteeringForce[ROLL] = 0.0f; // Set the correct animation (left or right) if ( newSteeringForce[YAW] > 0.0f ) turn_anim_name = left_anim; else turn_anim_name = right_anim; if ( turn_anim_name != self.animname ) self.SetAnim( turn_anim_name ); // Actually turn here self.movementSubsystem->Accelerate( newSteeringForce ); return BEHAVIOR_EVALUATING; } void TurnInvestigate::End ( Actor &self ) { self.SetActorFlag( ACTOR_FLAG_INVESTIGATING, false ); self.SetActorFlag( ACTOR_FLAG_NOISE_HEARD, false ); } /**************************************************************************** TurnToEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TurnToEnemy, NULL ) { { &EV_Behavior_Args, &TurnToEnemy::SetArgs }, { &EV_Behavior_AnimDone, &TurnToEnemy::AnimDone }, { NULL, NULL } }; void TurnToEnemy::SetArgs ( Event *ev ) { left_anim = ev->GetString( 1 ); right_anim = ev->GetString( 2 ); turn_speed = ev->GetFloat( 3 ); forever = ev->GetBoolean( 4 ); if ( ev->NumArgs() > 4 ) use_last_known_position = ev->GetBoolean( 5 ); else use_last_known_position = false; } void TurnToEnemy::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void TurnToEnemy::Begin ( Actor &self ) { self.SetAnim( "idle" ); } void TurnToEnemy::AnimDone ( Event *ev ) { anim_done = true; } BehaviorReturnCode_t TurnToEnemy::Evaluate ( Actor &self ) { Entity *currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; Vector dir; self.angles.AngleVectors( &dir ); Vector targetPosition=currentEnemy->origin; if ( use_last_known_position ) { targetPosition = self.last_known_enemy_pos; } Vector newSteeringForce = self.movementSubsystem->SteerTowardsPoint( targetPosition, vec_zero, dir, 1.0f ); // See if we have turned all of the way to the enemy position if ( ( newSteeringForce[YAW] < .5f ) && ( newSteeringForce[YAW] > -.5f ) ) newSteeringForce[YAW] = 0.0f; // Make sure we are not turning faster than out turn speed if ( newSteeringForce[YAW] > turn_speed ) { newSteeringForce[YAW] = turn_speed; } if ( newSteeringForce[YAW] < -turn_speed ) { newSteeringForce[YAW] = -turn_speed; } newSteeringForce[PITCH] = 0.0f; newSteeringForce[ROLL] = 0.0f; // Set the correct animation (left or right) str turn_anim_name; if ( newSteeringForce[YAW] > 0.0f ) turn_anim_name = left_anim; else if ( newSteeringForce[YAW] < 0.0f ) turn_anim_name = right_anim; else if ( anim_done ) turn_anim_name = "idle"; else turn_anim_name = self.animname.c_str(); if ( turn_anim_name != self.animname ) self.SetAnim( turn_anim_name, EV_Actor_NotifyBehavior ); // Actually turn here self.movementSubsystem->Accelerate( newSteeringForce ); // See if we have turned all of the way to the enemy position if ( ( newSteeringForce[YAW] < turn_speed ) && ( newSteeringForce[YAW] > -turn_speed ) && !forever ) return BEHAVIOR_SUCCESS; anim_done = false; return BEHAVIOR_EVALUATING; } void TurnToEnemy::End ( Actor &self ) { } /**************************************************************************** PickupEntity Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, PickupEntity, NULL ) { { &EV_Behavior_Args, &PickupEntity::SetArgs }, { &EV_Behavior_AnimDone, &PickupEntity::AnimDone }, { NULL, NULL } }; void PickupEntity::SetArgs ( Event *ev ) { ent_to_pickup = ev->GetEntity( 1 ); pickup_anim_name = ev->GetString( 2 ); } void PickupEntity::Begin ( Actor &self ) { anim_done = false; self.pickup_ent = ent_to_pickup; self.SetAnim( pickup_anim_name.c_str(), EV_Actor_NotifyBehavior ); } void PickupEntity::AnimDone ( Event *ev ) { anim_done = true; } BehaviorReturnCode_t PickupEntity::Evaluate ( Actor &self ) { if ( !ent_to_pickup ) return BEHAVIOR_SUCCESS; if ( anim_done ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void PickupEntity::End ( Actor &self ) { self.SetAnim( "idle" ); self.pickup_ent = NULL; } /**************************************************************************** ThrowEntity Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, ThrowEntity, NULL ) { { &EV_Behavior_Args, &ThrowEntity::SetArgs }, { &EV_Behavior_AnimDone, &ThrowEntity::AnimDone }, { NULL, NULL } }; void ThrowEntity::SetArgs ( Event *ev ) { throw_anim_name = ev->GetString( 1 ); } void ThrowEntity::Begin ( Actor &self ) { anim_done = false; self.SetAnim( throw_anim_name, EV_Actor_NotifyBehavior ); } void ThrowEntity::AnimDone ( Event *ev ) { anim_done = true; } BehaviorReturnCode_t ThrowEntity::Evaluate ( Actor &self ) { if ( anim_done ) return BEHAVIOR_SUCCESS; return BEHAVIOR_EVALUATING; } void ThrowEntity::End ( Actor &self ) { self.SetAnim( "idle" ); } /**************************************************************************** ***************************************************************************** Behaviors for specific creatures ***************************************************************************** ****************************************************************************/ /**************************************************************************** BurrowAttack Class Definition ****************************************************************************/ #define BURROW_MODE_MOVING 0 #define BURROW_MODE_ATTACK 1 CLASS_DECLARATION( Behavior, BurrowAttack, NULL ) { { NULL, NULL } }; void BurrowAttack::SetArgs ( Event *ev ) { use_last_known_position = ev->GetBoolean( 1 ); } void BurrowAttack::Begin ( Actor &self ) { Vector attack_dir; Vector start_pos; Vector end_pos; trace_t trace; if ( self.animname != "idle_down" ) self.SetAnim( "idle_down" ); // Setup our goal point Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( currentEnemy ) { if ( use_last_known_position ) goal = self.last_known_enemy_pos; else goal = currentEnemy->origin; start_pos = goal + Vector( "0 0 10" ); end_pos = start_pos + Vector( "0 0 -250" ); trace = G_Trace( start_pos, vec_zero, vec_zero, end_pos, NULL, MASK_DEADSOLID, false, "BurrowAttack::Begin" ); goal = trace.endpos; } // Setup our starting point, a little ways in front of our origin attack_dir = goal - self.origin; too_close = false; if ( attack_dir.length() < 300.0f ) too_close = true; attack_dir.normalize(); attack_origin = self.origin + ( attack_dir * 100.0f ); burrow_mode = BURROW_MODE_MOVING; stage = self.stage; burrow_speed = 80.0f; if ( stage == 3 ) { attacks_left = 2; } else if ( stage == 4 ) { attacks_left = 3 + (int)G_Random( 3.0f ); burrow_speed = 120.0f; } } BehaviorReturnCode_t BurrowAttack::Evaluate ( Actor &self ) { Vector attack_dir; Vector new_origin; float total_dist; Vector start_pos; Vector end_pos; Entity *dirt; trace_t trace; Vector temp_angles; int cont; Vector temp_endpos; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( too_close ) return BEHAVIOR_SUCCESS; switch ( burrow_mode ) { case BURROW_MODE_MOVING : attack_dir = goal - attack_origin; total_dist = attack_dir.length(); attack_dir.normalize(); if ( total_dist < burrow_speed ) { new_origin = goal; } else { new_origin = attack_origin + ( attack_dir * burrow_speed ); total_dist = burrow_speed; } // Spawn in dirt or water start_pos = attack_origin + attack_dir + Vector( "0 0 10" ); end_pos = start_pos + Vector( "0 0 -250" ); trace = G_Trace( start_pos, vec_zero, vec_zero, end_pos, NULL, MASK_DEADSOLID | MASK_WATER, false, "BurrowAttack" ); temp_endpos = trace.endpos; temp_endpos -= Vector( "0 0 5" ); cont = gi.pointcontents( temp_endpos, 0 ); dirt = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); dirt->setOrigin( trace.endpos ); temp_angles = vec_zero; if (cont & MASK_WATER) dirt->setModel( "fx_splashsmall.tik" ); else dirt->setModel( "fx_dirtcloud.tik" ); dirt->setAngles( temp_angles ); dirt->animate->RandomAnimate( "idle" ); dirt->PostEvent( EV_Remove, 5.0f ); attack_origin = new_origin; if ( attack_origin == goal ) { // Got to our goal position, do attack if ( stage == 1 ) { SpawnArm( self, leg1, attack_origin + Vector( " 25 25 0" ), "attack1", 0.0f ); SpawnArm( self, leg2, attack_origin + Vector( " 25 -25 0" ), "attack1", 0.0f ); SpawnArm( self, leg3, attack_origin + Vector( "-25 25 0" ), "attack1", 0.0f ); SpawnArm( self, leg4, attack_origin + Vector( "-25 -25 0" ), "attack1", 0.0f ); } else if ( stage == 2 ) { SpawnArm( self, leg1, attack_origin + Vector( " 25 0 0" ), "attack2", 0.0f ); SpawnArm( self, leg2, attack_origin + Vector( "-25 25 0" ), "attack2", 120.0f ); SpawnArm( self, leg3, attack_origin + Vector( "-25 -25 0" ), "attack2", 240.0f ); } else { SpawnArm( self, leg1, attack_origin, "attack1", 0.0f ); } burrow_mode = BURROW_MODE_ATTACK; } break; case BURROW_MODE_ATTACK : // Wait until all of the legs are done if ( !leg1 && !leg2 && !leg3 && !leg4 ) { if ( self.animname != "idle_down" ) self.SetAnim( "idle_down" ); if ( ( stage == 1 ) || ( stage == 2 ) ) { return BEHAVIOR_SUCCESS; } else { attacks_left--; if ( attacks_left > 0 ) { if ( use_last_known_position ) if ( self.sensoryPerception->CanSeeEntity( &self , currentEnemy , true , true ) ) goal = currentEnemy->origin; else return BEHAVIOR_SUCCESS; else goal = currentEnemy->origin; burrow_mode = BURROW_MODE_MOVING; } else { return BEHAVIOR_SUCCESS; } } } break; } return BEHAVIOR_EVALUATING; } void BurrowAttack::SpawnArm ( Actor &self, EntityPtr &leg, const Vector &original_arm_origin, const char *anim_name, float angle ) { Vector angles; trace_t trace; Vector start_pos; Vector end_pos; str anim_to_play; Entity *leg_animate_ptr; Vector dir; Entity *dirt; Vector arm_origin; // Find correct spot to spawn arm_origin = original_arm_origin; arm_origin[2] = -575.0f; start_pos = arm_origin + Vector( "0 0 64" ); end_pos = arm_origin - Vector( "0 0 100" ); //trace = G_Trace( start_pos, Vector(-5, -5, 0), Vector(5, 5, 0), end_pos, NULL, MASK_DEADSOLID, false, "BurrowAttack" ); trace = G_Trace( start_pos, Vector(-10.0f, -10.0f, 0.0f), Vector(10.0f, 10.0f, 0.0f), end_pos, NULL, MASK_DEADSOLID, false, "BurrowAttack" ); arm_origin = trace.endpos; // Make sure can spawn here end_pos = arm_origin + Vector( "0 0 50" ); trace = G_Trace( arm_origin, Vector(-10.0f, -10.0f, 0.0f), Vector(10.0f, 10.0f, 0.0f), end_pos, NULL, MASK_DEADSOLID, false, "BurrowAttack" ); if ( ( trace.fraction < 1.0f ) || trace.startsolid || trace.allsolid ) { if ( trace.entityNum == ENTITYNUM_WORLD || !( trace.ent && trace.ent->entity && trace.ent->entity->takedamage ) ) return; } // Spawn some dirt dirt = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); dirt->setOrigin( arm_origin ); dirt->setModel( "fx_dirtcloud.tik" ); dirt->setAngles( vec_zero ); dirt->animate->RandomAnimate( "idle" ); dirt->PostEvent( EV_Remove, 5.0f ); // Spawn leg leg = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); leg->setModel( "vmama_arm.tik" ); leg->setOrigin( arm_origin ); leg->ProcessPendingEvents(); //leg->edict->clipmask = MASK_MONSTERSOLID; leg->setContents( 0 ); leg->setSolidType( SOLID_NOT ); leg->PostEvent( EV_BecomeNonSolid, 0.0f ); angles = vec_zero; angles[YAW] = angle; leg->setAngles( angles ); anim_to_play = anim_name; // See if we should get stuck or not if ( strcmp( anim_name, "attack1" ) == 0 ) { end_pos = arm_origin + Vector( "0 0 250" ); leg_animate_ptr = leg; trace = G_Trace( arm_origin, Vector(-5.0f, -5.0f, 0.0f), Vector(5.0f, 5.0f, 0.0f), end_pos, leg_animate_ptr, MASK_DEADSOLID, false, "BurrowAttack" ); if ( trace.fraction != 1.0f ) { if ( self.animname != "struggle" ) self.SetAnim( "struggle" ); anim_to_play = "getstuck"; } } // Damage entities in way if ( strcmp( anim_name, "attack1" ) == 0 ) { start_pos = arm_origin; end_pos = arm_origin + Vector( "0 0 250" ); dir = Vector ( G_CRandom( 5.0f ), G_CRandom( 5.0f ), 10.0f ); } else { start_pos = arm_origin + Vector( "0 0 10" ); angles.AngleVectors( &dir ); end_pos = start_pos + ( dir * 250.0f ); } leg_animate_ptr = leg; MeleeAttack( start_pos, end_pos, 50.0f, &self, MOD_IMPALE, 10.0f, 0.0f, 0.0f, 100.0f ); leg->animate->RandomAnimate( anim_to_play.c_str(), EV_Remove ); } void BurrowAttack::End ( Actor &self ) { } /**************************************************************************** CircleEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, CircleEnemy, NULL ) { { &EV_Behavior_Args, &CircleEnemy::SetArgs }, { NULL, NULL } }; void CircleEnemy::SetArgs ( Event *ev ) { center_part_name = ev->GetString( 1 ); } void CircleEnemy::Begin ( Actor &self ) { ent_to_circle = self.enemyManager->GetCurrentEnemy(); last_angle_change = 0; } float CircleEnemy::GetAngleDiff ( const Actor &self, const Actor *center_actor, const Vector &origin ) { Vector dir; Vector enemy_angles; Vector actor_angles; float angle_diff; if ( !ent_to_circle ) return 0.0f; dir = ent_to_circle->origin - center_actor->origin; enemy_angles = dir.toAngles(); dir = origin - center_actor->origin; actor_angles = dir.toAngles(); angle_diff = AngleDelta( actor_angles[YAW], enemy_angles[YAW] ); return angle_diff; } #define MAX_CIRCLE_ACCELERATION .125 #define MAX_CIRCLE_VELOCITY 10 BehaviorReturnCode_t CircleEnemy::Evaluate ( Actor &self ) { Vector dir; Actor *center_actor; Vector actor_angles; float angle_diff; float other_angle_diff; float abs_angle_diff; float other_abs_angle_diff = 180.0f; float angle_change = MAX_CIRCLE_VELOCITY; float length; float real_angle_change; Actor *other; if ( !ent_to_circle ) return BEHAVIOR_SUCCESS; center_actor = self.FindPartActor( center_part_name.c_str() ); if ( !center_actor ) return BEHAVIOR_SUCCESS; angle_diff = GetAngleDiff( self, center_actor, self.origin ); if ( angle_diff < 0.0f ) abs_angle_diff = -angle_diff; else abs_angle_diff = angle_diff; other = self.FindPartActor( self.part_name ); if ( other ) { other_angle_diff = GetAngleDiff( self, center_actor, other->origin ); if ( other_angle_diff < 0.0f ) other_abs_angle_diff = -other_angle_diff; else other_abs_angle_diff = other_angle_diff; } if ( abs_angle_diff < other_abs_angle_diff ) { // Turn towards enemy if ( abs_angle_diff < angle_change ) angle_change = abs_angle_diff; if ( angle_diff < 0.0f ) real_angle_change = angle_change; else real_angle_change = -angle_change; } else { // Turn away from enemy if ( 180.0f - abs_angle_diff < angle_change ) angle_change = 180.0f - abs_angle_diff; if ( angle_diff < 0.0f ) real_angle_change = -angle_change; else real_angle_change = angle_change; } if ( ( real_angle_change < 1.0f ) && ( real_angle_change > -1.0f ) ) real_angle_change = 0.0f; if ( real_angle_change > 0.0f ) { if ( real_angle_change > ( last_angle_change + MAX_CIRCLE_ACCELERATION ) ) real_angle_change = last_angle_change + MAX_CIRCLE_ACCELERATION; } else if ( real_angle_change < 0.0f ) { if ( real_angle_change < ( last_angle_change - MAX_CIRCLE_ACCELERATION ) ) real_angle_change = last_angle_change - MAX_CIRCLE_ACCELERATION; } last_angle_change = real_angle_change; dir = self.origin - center_actor->origin; length = dir.length(); actor_angles = dir.toAngles(); actor_angles[YAW] += real_angle_change; // Find new position actor_angles.AngleVectors( &dir, NULL, NULL ); dir *= length; dir.z = 0.0f; self.setOrigin( center_actor->origin + dir ); // Set the actors angle to look at the center dir[0] = -dir[0]; dir[1] = -dir[1]; dir[2] = -dir[2]; self.angles[YAW] = dir.toYaw(); self.setAngles( self.angles ); return BEHAVIOR_EVALUATING; } void CircleEnemy::End ( Actor &self ) { } /**************************************************************************** CircleCurrentEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, CircleCurrentEnemy, NULL ) { { &EV_Behavior_Args, &CircleCurrentEnemy::SetArgs }, { NULL, NULL } }; void CircleCurrentEnemy::SetArgs ( Event *ev ) { anim = "walk"; radius = 0; maintainDistance = true; clockwise = true; if (ev->NumArgs() > 0 ) anim = ev->GetString( 1 ); if (ev->NumArgs() > 1 ) radius = ev->GetFloat( 2 ); if (ev->NumArgs() > 2 ) maintainDistance = ev->GetBoolean( 3 ); if (ev->NumArgs() > 3 ) clockwise = ev->GetBoolean( 4 ); } void CircleCurrentEnemy::Begin ( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return; self.SetAnim(anim); dirToEnemy = self.origin - currentEnemy->origin; if(!radius) radius = dirToEnemy.length(); stuck = 0; stuckCheck = 0; if (clockwise) { turnAngle = 90; } else turnAngle = -90; oldAngle = 0; angleAdjusted = false; } BehaviorReturnCode_t CircleCurrentEnemy::Evaluate ( Actor &self ) { Vector dir; Vector dirAngles; Vector Angles; float dirYaw; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; dirToEnemy = currentEnemy->origin - self.origin; dirYaw = dirToEnemy.toYaw(); if(!stuck) { float len = dirToEnemy.length(); if(len > radius && maintainDistance && !angleAdjusted) { oldAngle = turnAngle; turnAngle*=.5f; angleAdjusted = true; } if(len <= radius && angleAdjusted) { turnAngle = oldAngle; angleAdjusted = false; } } dirYaw+=turnAngle; self.angles[YAW] = dirYaw; if ( self.movementSubsystem->getLastMove() != STEPMOVE_OK ) { if(stuck >= 2) { if(stuck < 3 ) { turnAngle = -turnAngle; } else { self.angles[YAW] = dirYaw + angleStep; angleStep += 45.0f; } } stuck++; } stuckCheck++; if(stuckCheck > stuck) { stuckCheck = 0; stuck = 0; angleStep = 45.0f; } self.setAngles(self.angles); return BEHAVIOR_EVALUATING; } void CircleCurrentEnemy::End ( Actor &self ) { } /**************************************************************************** ChaoticDodge Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, ChaoticDodge, NULL ) { { &EV_Behavior_Args, &ChaoticDodge::SetArgs }, { NULL, NULL } }; void ChaoticDodge::SetArgs ( Event *ev ) { anim = "walk"; stuck = 0; time = .5; changeTime = 0; turnspeed = 45; adjusting = false; turnAngle = 0; if (ev->NumArgs() > 0 ) anim = ev->GetString( 1 ); if (ev->NumArgs() > 1 ) time = ev->GetFloat( 2 ); if (ev->NumArgs() > 2 ) turnspeed = ev->GetFloat( 3 ); } void ChaoticDodge::Begin ( Actor &self ) { self.SetAnim(anim); } BehaviorReturnCode_t ChaoticDodge::Evaluate ( Actor &self ) { float dirYaw = GetNewYaw(); float CurrentYaw = self.origin.toYaw(); if ( self.movementSubsystem->getLastMove() != STEPMOVE_OK ) { stuck++; turnAngle += 30.0f; if (stuck > 3 ) { self.angles[YAW]+=turnAngle; self.setAngles(self.angles); time=1; changeTime = level.time + time; stuck = 0; turnAngle = 0.0f; } } if ( level.time <= changeTime ) return BEHAVIOR_EVALUATING; dirYaw+=CurrentYaw; self.angles[YAW] = dirYaw; self.setAngles(self.angles); time = G_Random ( .15f ) +.15f; changeTime = level.time + time; return BEHAVIOR_EVALUATING; } float ChaoticDodge::GetNewYaw ( void ) { float dirYaw = G_Random( 20.0f ) + turnspeed; float randomSide = G_Random(); if (randomSide < .5f ) randomSide = 1.0f; else randomSide = -1.0f; dirYaw*=randomSide; return dirYaw; } void ChaoticDodge::End ( Actor &self ) { } /**************************************************************************** ShockWater Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, ShockWater, NULL ) { { NULL, NULL } }; ShockWater::ShockWater() { left_beam = NULL; right_beam = NULL; center_beam = NULL; already_started = false; } void ShockWater::Begin ( Actor &self ) { } BehaviorReturnCode_t ShockWater::Evaluate ( Actor &self ) { Vector left_tag_orig; Vector right_tag_orig; Vector end_pos; Vector center_point; Actor *center_actor; trace_t trace; Entity *hit_entity; Vector diff_vect; float diff; Vector dir; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; center_actor = self.FindPartActor( "body" ); if ( !center_actor ) return BEHAVIOR_SUCCESS; if ( ( self.newanimnum == -1 ) && !already_started ) { // Get tag positions self.GetTag( "tag_left", &left_tag_orig ); self.GetTag( "tag_right", &right_tag_orig ); // Get end position end_pos = left_tag_orig + right_tag_orig; end_pos *= .5f; end_pos[2] -= 120.0f; dir = end_pos - self.origin; dir.z = 0.0f; dir *= 0.5f; end_pos += dir; // Add the left and right beams left_beam = CreateBeam( NULL, "emap1", left_tag_orig, end_pos, 10, 1.5f, 0.2f ); right_beam = CreateBeam( NULL, "emap1", right_tag_orig, end_pos, 10, 1.5f, 0.2f ); center_point = center_actor->origin; trace = G_Trace( center_point, vec_zero, vec_zero, end_pos, &self, MASK_SHOT, false, "ShockAttack" ); if ( ( trace.fraction < 1.0f ) && ( trace.entityNum != center_actor->entnum ) ) { hit_entity = G_GetEntity( trace.entityNum ); if ( hit_entity ) { center_point = hit_entity->origin; } } else { // Shock head center_actor->AddStateFlag( STATE_FLAG_IN_PAIN ); center_actor->SpawnEffect( "fx_elecstrike.tik", center_actor->origin, vec_zero, 2.0f ); center_actor->Sound( "sound/weapons/sword/electric/hitmix2.wav", 0, 1.0f, 500.0f ); } // create the center beam center_beam = CreateBeam( NULL, "emap1", end_pos, center_point, 20, 3.0f, 0.2f ); // Damage player if in water diff_vect = currentEnemy->origin - center_actor->origin; diff_vect[2] = 0.0f; diff = diff_vect.length(); //if ( diff < 240 && self.currentEnemy->groundentity ) if ( ( diff < 350.0f ) && currentEnemy->groundentity ) { currentEnemy->Damage( &self, &self, 10, Vector (0.0f, 0.0f, 0.0f), Vector (0.0f, 0.0f, 0.0f), Vector (0.0f, 0.0f, 0.0f), 0, 0, MOD_ELECTRICWATER ); } already_started = true; } return BEHAVIOR_EVALUATING; } void ShockWater::End ( Actor &self ) { delete left_beam; delete right_beam; delete center_beam; } /**************************************************************************** Shock Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Shock, NULL ) { { &EV_Behavior_Args, &Shock::SetArgs }, { NULL, NULL } }; Shock::Shock() { beam = NULL; damage = 10; already_started = false; random_angle = 0; beamShader = "emap1"; z_offset = 100; } void Shock::SetArgs ( Event *ev ) { tag_name = ev->GetString( 1 ); beamShader = ev->GetString( 2 ); if ( ev->NumArgs() > 3 ) { damage = ev->GetInteger( 3 ); } if ( ev->NumArgs() > 3 ) { random_angle = ev->GetFloat( 4 ); } if ( ev->NumArgs() > 4 ) z_offset = ev->GetFloat( 5 ); } void Shock::Begin ( Actor &self ) { } BehaviorReturnCode_t Shock::Evaluate ( Actor &self ) { Vector tag_orig; Vector angles; Vector end_pos; trace_t trace; Vector dir; float yaw_diff; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( ( self.newanimnum == -1 ) && !already_started ) { // Get tag position if ( tag_name.length() == 0 ) { return BEHAVIOR_SUCCESS; } self.GetTag( tag_name.c_str(), &tag_orig ); // See if the enemy is in front of us dir = currentEnemy->centroid - self.centroid; angles = dir.toAngles(); yaw_diff = AngleNormalize180( angles[YAW] - self.angles[YAW] ); //if ( ( yaw_diff < 60.0f ) && ( yaw_diff > -60.0f ) ) // { // The enemy is in front of us angles[YAW] += G_CRandom( random_angle ); angles.AngleVectors( &end_pos, NULL, NULL ); end_pos *= 500.0f; end_pos += tag_orig; end_pos.z -= z_offset; // } /* else { // Get end position angles = self.angles; angles[YAW] += G_Random( random_angle ) - ( random_angle / 2.0f ); angles[PITCH] = 0.0f; angles[ROLL] = 0.0f; angles.AngleVectors( &end_pos, NULL, NULL ); end_pos *= 500.0f; end_pos += tag_orig; end_pos.z -= z_offset; } */ trace = G_Trace( tag_orig, Vector (-15.0f, -15.0f, -15.0f), Vector (15.0f, 15.0f, 15.0f), end_pos, &self, MASK_SHOT, false, "ShockAttack" ); if ( ( trace.fraction < 1.0f ) && ( trace.entityNum == currentEnemy->entnum ) ) { end_pos = currentEnemy->centroid; dir = end_pos - tag_orig; dir.normalize(); currentEnemy->Damage( &self, &self, damage, vec_zero, dir, vec_zero, 0, 0, MOD_ELECTRIC ); } // Add the beam beam = CreateBeam( NULL, beamShader.c_str(), tag_orig, end_pos, 2, 1.5f, 0.25f ); already_started = true; } else if ( already_started ) { self.GetTag( tag_name.c_str(), &tag_orig ); if ( beam ) beam->setOrigin( tag_orig ); } return BEHAVIOR_EVALUATING; } void Shock::End ( Actor &self ) { if ( beam ) { beam->ProcessEvent( EV_Remove ); beam = NULL; } } /**************************************************************************** MultiShock Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, MultiShock, NULL ) { { &EV_Behavior_Args, &MultiShock::SetArgs }, { NULL, NULL } }; MultiShock::MultiShock() { beam1 = NULL; beam2 = NULL; damage = 10; already_started = false; random_angle = 0; beamShader = "emap1"; z_offset = 100; } void MultiShock::SetArgs ( Event *ev ) { tag_name1 = ev->GetString( 1 ); tag_name2 = ev->GetString( 2 ); beamShader = ev->GetString( 3 ); damage = ev->GetFloat( 4 ); random_angle = ev->GetFloat( 5 ); z_offset = ev->GetFloat( 6 ); } void MultiShock::Begin ( Actor &self ) { } BehaviorReturnCode_t MultiShock::Evaluate ( Actor &self ) { Vector tag1_orig; Vector tag2_orig; Vector angles; Vector end_pos1; Vector end_pos2; trace_t trace; Vector dir; float yaw_diff; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; //if ( self.newanimnum == -1 && !already_started ) if ( !already_started ) { // Get tag position if ( tag_name1.length() == 0 ) return BEHAVIOR_SUCCESS; if ( tag_name2.length() == 0 ) return BEHAVIOR_SUCCESS; self.GetTag( tag_name1.c_str(), &tag1_orig ); self.GetTag( tag_name2.c_str(), &tag2_orig ); // See if the enemy is in front of us dir = currentEnemy->origin - self.origin; angles = dir.toAngles(); yaw_diff = AngleNormalize180( angles[YAW] - self.angles[YAW] ); if ( ( yaw_diff < 60.0f ) && ( yaw_diff > -60.0f ) ) { // The enemy is in front of us angles[YAW] += G_CRandom( random_angle ); angles.AngleVectors( &end_pos1, NULL, NULL ); end_pos1 *= 500.0f; end_pos1 += tag1_orig; end_pos1.z -= z_offset; angles.AngleVectors( &end_pos2, NULL, NULL ); end_pos2 *= 500.0f; end_pos2 += tag1_orig; end_pos2.z -= z_offset; } else { // Get end position angles = self.angles; angles[YAW] += G_Random( random_angle ) - ( random_angle / 2.0f ); angles[PITCH] = 0.0f; angles[ROLL] = 0.0f; angles.AngleVectors( &end_pos1, NULL, NULL ); end_pos1 *= 500.0f; end_pos1 += tag1_orig; end_pos1.z -= z_offset; angles.AngleVectors( &end_pos2, NULL, NULL ); end_pos2 *= 500.0f; end_pos2 += tag1_orig; end_pos2.z -= z_offset; } trace = G_Trace( tag1_orig, Vector (-15.0f, -15.0f, -15.0f), Vector (15.0f, 15.0f, 15.0f), end_pos1, &self, MASK_SHOT, false, "ShockAttack" ); if ( ( trace.fraction < 1.0f ) && ( trace.entityNum == currentEnemy->entnum ) ) { end_pos1 = currentEnemy->centroid; dir = end_pos1 - tag1_orig; dir.normalize(); currentEnemy->Damage( &self, &self, damage, vec_zero, dir, vec_zero, 0, 0, MOD_ELECTRIC ); } // Add the beam beam1 = CreateBeam( NULL, beamShader.c_str(), tag1_orig, end_pos1, 20, 1.5f, 0.2f ); trace = G_Trace( tag2_orig, Vector (-15.0f, -15.0f, -15.0f), Vector (15.0f, 15.0f, 15.0f), end_pos2, &self, MASK_SHOT, false, "ShockAttack" ); if ( ( trace.fraction < 1.0f ) && ( trace.entityNum == currentEnemy->entnum ) ) { end_pos2 = currentEnemy->centroid; dir = end_pos2 - tag2_orig; dir.normalize(); currentEnemy->Damage( &self, &self, damage, vec_zero, dir, vec_zero, 0, 0, MOD_ELECTRIC ); } // Add the beam beam2 = CreateBeam( NULL, beamShader.c_str(), tag2_orig, end_pos2, 20, 1.5f, 0.2f ); already_started = true; } else { self.GetTag( tag_name1.c_str(), &tag1_orig ); self.GetTag( tag_name2.c_str(), &tag2_orig ); if ( beam1 ) beam1->setOrigin( tag1_orig ); if ( beam2 ) beam2->setOrigin( tag2_orig ); } return BEHAVIOR_EVALUATING; } void MultiShock::End ( Actor &self ) { if ( beam1 ) { beam1->ProcessEvent( EV_Remove ); beam1 = NULL; } if ( beam2 ) { beam2->ProcessEvent( EV_Remove ); beam2 = NULL; } } /**************************************************************************** ShockDown Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, ShockDown, NULL ) { { &EV_Behavior_Args, &ShockDown::SetArgs }, { NULL, NULL } }; ShockDown::ShockDown() { beam = NULL; damage = 10; already_started = false; beamShader = "emap1"; } void ShockDown::SetArgs ( Event *ev ) { tag_name = ev->GetString( 1 ); beamShader = ev->GetString( 2 ); if ( ev->NumArgs() > 3 ) { damage = ev->GetInteger( 3 ); } } void ShockDown::Begin ( Actor &self ) { } BehaviorReturnCode_t ShockDown::Evaluate ( Actor &self ) { Vector tag_orig; Vector angles; Vector end_pos; trace_t trace; Vector dir; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( ( self.newanimnum == -1 ) && !already_started ) { // Get tag position if ( tag_name.length() == 0 ) { return BEHAVIOR_SUCCESS; } self.GetTag( tag_name.c_str(), &tag_orig ); end_pos = self.origin; end_pos.z -= 10000.0f; trace = G_Trace( tag_orig, Vector (-15.0f, -15.0f, -15.0f), Vector (15.0f, 15.0f, 15.0f), end_pos, &self, MASK_SHOT, false, "ShockAttack" ); end_pos = trace.endpos; dir = end_pos - tag_orig; angles = dir.toAngles(); if ( ( trace.fraction < 1.0f ) && ( trace.entityNum == currentEnemy->entnum ) ) { end_pos = currentEnemy->centroid; dir = end_pos - tag_orig; dir.normalize(); currentEnemy->Damage( &self, &self, damage, vec_zero, dir, vec_zero, 0, 0, MOD_ELECTRIC ); } // Add the beam beam = CreateBeam( NULL, beamShader.c_str(), tag_orig, end_pos, 20, 1.5f, 0.2f ); already_started = true; } else if ( already_started ) { self.GetTag( tag_name.c_str(), &tag_orig ); if ( beam ) beam->setOrigin( tag_orig ); } return BEHAVIOR_EVALUATING; } void ShockDown::End ( Actor &self ) { if ( beam ) { beam->ProcessEvent( EV_Remove ); beam = NULL; } } /**************************************************************************** CircleAttack Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, CircleAttack, NULL ) { { &EV_Behavior_Args, &CircleAttack::SetArgs }, { NULL, NULL } }; CircleAttack::CircleAttack() { next_time = 0.0f; current_direction = 1; number_of_attacks = 1; } void CircleAttack::SetArgs ( Event *ev ) { command = ev->GetString( 1 ); direction = ev->GetString( 2 ); } Actor *CircleAttack::FindClosestPart ( const Actor &self, float angle ) { float closest_diff = 360.0f; int i; part_t *part; Entity *partent; Actor *partact; Vector dir; Vector angles; float angle_diff; Actor *closest_part = NULL; for( i = 1 ; i <= self.parts.NumObjects(); i++ ) { part = &self.parts.ObjectAt( i ); partent = part->ent; partact = ( Actor * )partent; if ( partact && ( partact->part_name == "smallarm" ) ) { dir = partact->origin - self.origin; angles = dir.toAngles(); angle_diff = AngleDelta( angles[ YAW ], angle ); if ( angle_diff < 0.0f ) { angle_diff = -angle_diff; } if ( angle_diff < closest_diff ) { closest_part = partact; closest_diff = angle_diff; } } } return closest_part; } void CircleAttack::Begin ( Actor &self ) { float random_direction; Actor *closest_part; // Pick which arm to start with random_direction = G_Random( 360.0f ); closest_part = FindClosestPart( self, random_direction ); if ( closest_part != NULL ) { first_part = closest_part; current_part = closest_part; closest_part->command = command; next_time = level.time + 0.2f; } current_direction = 1; if ( direction == "counterclockwise" ) { current_direction = 0; } } BehaviorReturnCode_t CircleAttack::Evaluate ( Actor &self ) { Entity *current_part_entity; Actor *current_part_actor; Vector dir; Vector angles; Actor *closest_part; if ( level.time >= next_time ) { current_part_entity = current_part; current_part_actor = ( Actor * )current_part_entity; // Find the next part dir = current_part_actor->origin - self.origin; angles = dir.toAngles(); if ( direction == "changing" ) { if ( number_of_attacks >= 20 ) { return BEHAVIOR_SUCCESS; } if ( G_Random( 1.0f ) < .3f ) { current_direction = !current_direction; } } if ( current_direction == 1 ) { angles[YAW] -= 360.0f / 8.0f; } else { angles[YAW] += 360.0f / 8.0f; } closest_part = FindClosestPart( self, angles[YAW] ); if ( ( closest_part == first_part ) && ( direction != "changing" ) ) { return BEHAVIOR_SUCCESS; } current_part = closest_part; closest_part->command = command; next_time = level.time + 0.2f; number_of_attacks++; } return BEHAVIOR_EVALUATING; } void CircleAttack::End ( Actor &self ) { } /**************************************************************************** DragEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, DragEnemy, NULL ) { { &EV_Behavior_Args, &DragEnemy::SetArgs }, { NULL, NULL } }; void DragEnemy::SetArgs ( Event *ev ) { tag_name = ev->GetString( 1 ); damage = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) drop = ev->GetBoolean( 3 ); else drop = false; } void DragEnemy::Begin ( Actor &self ) { Actor *body; Vector dir; Vector angles; ent_to_drag = self.enemyManager->GetCurrentEnemy(); if ( !ent_to_drag ) return; attached = false; if ( damage > 0.0f && !drop ) { ent_to_drag->Damage( &self, &self, damage, vec_zero, vec_zero, vec_zero, 0, 0, MOD_EAT ); } body = self.FindPartActor( "body" ); if ( body ) { dir = body->origin - self.origin; angles = dir.toAngles(); target_yaw = angles[ YAW ]; last_turn_time = level.time; } else { target_yaw = self.angles[ YAW ]; } } BehaviorReturnCode_t DragEnemy::Evaluate ( Actor &self ) { Vector orig; str anim_name; if ( !ent_to_drag ) return BEHAVIOR_SUCCESS; if ( drop && ( damage > 0.0f ) ) { if ( self.GetTag( tag_name.c_str(), &orig ) ) { if ( ent_to_drag->isClient() ) { orig[ 2 ] -= 85.0f; } else { offset[ 2 ] = ( ent_to_drag->absmin[ 2 ] - ent_to_drag->absmax[ 2 ] ) * 0.5f; if ( offset[ 2 ] < -40.0f ) { offset[ 2 ] -= 25.0f; } orig += offset; } ent_to_drag->setOrigin( orig ); } ent_to_drag->Damage( &self, &self, damage, vec_zero, vec_zero, vec_zero, 0, 0, MOD_EAT ); } if ( ent_to_drag->deadflag ) return BEHAVIOR_SUCCESS; anim_name = self.animate->AnimName(); if ( ( anim_name == "raise" ) && ( self.newanim == "" ) ) { if ( !attached ) { Event *ev; if ( damage > 0.0f ) { if ( ent_to_drag->isClient() ) { offset[ 2 ] -= 85.0f; } else { offset[ 2 ] = ( ent_to_drag->absmin[ 2 ] - ent_to_drag->absmax[ 2 ] ) * 0.5f; if ( offset[ 2 ] < -40.0f ) { offset[ 2 ] -= 25.0f; } } ev = new Event( EV_Attach ); ev->AddEntity( &self ); ev->AddString( tag_name.c_str() ); ev->AddInteger( false ); ev->AddVector( offset ); ent_to_drag->ProcessEvent( ev ); ent_to_drag->flags |= FL_PARTIAL_IMMOBILE; } attached = true; } if ( target_yaw != self.angles[ YAW ] ) { float yaw_diff = target_yaw - self.angles[ YAW ]; if ( yaw_diff > 180.0f ) { target_yaw -= 360.0f; yaw_diff -= 360.0f; } if ( yaw_diff < -180.0f ) { target_yaw += 360.0f; yaw_diff += 360.0f; } if ( yaw_diff < 0.0f ) { self.angles[ YAW ] -= 90.0f * (level.time - last_turn_time); if ( self.angles[ YAW ] < 0.0f ) { self.angles[ YAW ] += 360.0f; } if ( self.angles[ YAW ] < target_yaw ) { self.angles[ YAW ] = target_yaw; } self.setAngles( self.angles ); } else if ( yaw_diff > 0.0f ) { self.angles[ YAW ] += 90.0f * (level.time - last_turn_time); if ( self.angles[ YAW ] > 360.0f ) { self.angles[ YAW ] -= 360.0f; } if ( self.angles[ YAW ] > target_yaw ) { self.angles[ YAW ] = target_yaw; } self.setAngles( self.angles ); } last_turn_time = level.time; } } return BEHAVIOR_EVALUATING; } void DragEnemy::End ( Actor &self ) { Vector orig; Event *ev; trace_t trace; if ( !ent_to_drag ) return; if ( drop || ( strcmp( self.currentState->getName(), "SUICIDE" ) == 0 ) ) { ent_to_drag->flags &= ~FL_PARTIAL_IMMOBILE; ev = new Event( EV_Detach ); ent_to_drag->ProcessEvent( ev ); ent_to_drag->setSolidType( SOLID_BBOX ); if ( self.GetTag( tag_name.c_str(), &orig ) ) { trace = G_Trace( orig - Vector( "0 0 100" ), ent_to_drag->mins, ent_to_drag->maxs, orig, ent_to_drag, ent_to_drag->edict->clipmask, false, "DragEnemy" ); if ( trace.allsolid ) gi.WDPrintf( "Dropping entity into a solid!\n" ); ent_to_drag->setOrigin( trace.endpos ); /* if ( ent_to_drag->isClient() ) { offset[2] = -85; } else { offset[2] = ( ent_to_drag->absmin[2] - ent_to_drag->absmax[2] ) * 0.5; if ( offset[2] < -40 ) { offset[2] -= 25; } } ent_to_drag->setOrigin( orig + offset ); */ } } ent_to_drag->velocity = Vector(0, 0, -20); } /**************************************************************************** PickupEnemy Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, PickupEnemy, NULL ) { { &EV_Behavior_Args, &PickupEnemy::SetArgs }, { NULL, NULL } }; void PickupEnemy::SetArgs ( Event *ev ) { tag_name = ev->GetString( 1 ); damage = ev->GetFloat( 2 ); if ( ev->NumArgs() > 2 ) drop = ev->GetBoolean( 3 ); else drop = false; } void PickupEnemy::Begin ( Actor &self ) { Actor *body; Vector dir; Vector angles; ent_to_drag = self.enemyManager->GetCurrentEnemy(); if ( !ent_to_drag ) return; attached = false; if ( ( damage > 0.0f ) && !drop ) { ent_to_drag->Damage( &self, &self, damage, vec_zero, vec_zero, vec_zero, 0, 0, MOD_EAT ); } body = self.FindPartActor( "body" ); if ( body ) { dir = body->origin - self.origin; angles = dir.toAngles(); target_yaw = angles[ YAW ]; last_turn_time = level.time; } else { target_yaw = self.angles[ YAW ]; } } BehaviorReturnCode_t PickupEnemy::Evaluate ( Actor &self ) { Vector orig; str anim_name; if ( !ent_to_drag ) return BEHAVIOR_SUCCESS; if ( drop && ( damage > 0.0f ) ) { if ( self.GetTag( tag_name.c_str(), &orig ) ) { if ( ent_to_drag->isClient() ) { orig[ 2 ] -= 85.0f; } else { offset[ 2 ] = ( ent_to_drag->absmin[ 2 ] - ent_to_drag->absmax[ 2 ] ) * 0.5f; if ( offset[ 2 ] < -40.0f ) { offset[ 2 ] -= 25.0f; } orig += offset; } ent_to_drag->setOrigin( orig ); } ent_to_drag->Damage( &self, &self, damage, vec_zero, vec_zero, vec_zero, 0, 0, MOD_EAT ); } if ( ent_to_drag->deadflag ) return BEHAVIOR_SUCCESS; anim_name = self.animate->AnimName(); if ( !attached ) { Event *ev; if ( damage > 0.0f ) { if ( ent_to_drag->isClient() ) { offset[ 2 ] -= 85.0f; } else { offset[ 2 ] = ( ent_to_drag->absmin[ 2 ] - ent_to_drag->absmax[ 2 ] ) * 0.5f; if ( offset[ 2 ] < -40.0f ) { offset[ 2 ] -= 25.0f; } } ev = new Event( EV_Attach ); ev->AddEntity( &self ); ev->AddString( tag_name.c_str() ); ev->AddInteger( false ); ev->AddVector( offset ); ent_to_drag->ProcessEvent( ev ); ent_to_drag->flags |= FL_PARTIAL_IMMOBILE; } attached = true; } if ( target_yaw != self.angles[ YAW ] ) { float yaw_diff = target_yaw - self.angles[ YAW ]; if ( yaw_diff > 180.0f ) { target_yaw -= 360.0f; yaw_diff -= 360.0f; } if ( yaw_diff < -180.0f ) { target_yaw += 360.0f; yaw_diff += 360.0f; } if ( yaw_diff < 0.0f ) { self.angles[ YAW ] -= 90.0f * (level.time - last_turn_time); if ( self.angles[ YAW ] < 0.0f ) { self.angles[ YAW ] += 360.0f; } if ( self.angles[ YAW ] < target_yaw ) { self.angles[ YAW ] = target_yaw; } self.setAngles( self.angles ); } else if ( yaw_diff > 0.0f ) { self.angles[ YAW ] += 90.0f * (level.time - last_turn_time); if ( self.angles[ YAW ] > 360.0f ) { self.angles[ YAW ] -= 360.0f; } if ( self.angles[ YAW ] > target_yaw ) { self.angles[ YAW ] = target_yaw; } self.setAngles( self.angles ); } last_turn_time = level.time; } return BEHAVIOR_EVALUATING; } void PickupEnemy::End ( Actor &self ) { Vector orig; Event *ev; trace_t trace; if ( !ent_to_drag ) return; if ( drop || ( strcmp( self.currentState->getName(), "SUICIDE" ) == 0 ) ) { ent_to_drag->flags &= ~FL_PARTIAL_IMMOBILE; ev = new Event( EV_Detach ); ent_to_drag->ProcessEvent( ev ); ent_to_drag->setSolidType( SOLID_BBOX ); if ( self.GetTag( tag_name.c_str(), &orig ) ) { trace = G_Trace( orig - Vector( "0 0 100" ), ent_to_drag->mins, ent_to_drag->maxs, orig, ent_to_drag, ent_to_drag->edict->clipmask, false, "DragEnemy" ); if ( trace.allsolid ) gi.WDPrintf( "Dropping entity into a solid!\n" ); ent_to_drag->setOrigin( trace.endpos ); /* if ( ent_to_drag->isClient() ) { offset[2] = -85; } else { offset[2] = ( ent_to_drag->absmin[2] - ent_to_drag->absmax[2] ) * 0.5; if ( offset[2] < -40 ) { offset[2] -= 25; } } ent_to_drag->setOrigin( orig + offset ); */ } } ent_to_drag->velocity = Vector(0, 0, -20); } /**************************************************************************** Teleport Class Definition ****************************************************************************/ #define TELEPORT_BEHIND 0 #define TELEPORT_TOLEFT 1 #define TELEPORT_TORIGHT 2 #define TELEPORT_INFRONT 3 #define TELEPORT_NUMBER_OF_POSITIONS 4 CLASS_DECLARATION( Behavior, Teleport, NULL ) { { NULL, NULL } }; void Teleport::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void Teleport::Begin ( Actor &self ) { } qboolean Teleport::TestPosition ( Actor &self, int test_pos, Vector &good_position, qboolean use_enemy_dir ) { Vector test_position; Vector enemy_angles; Vector enemy_forward; Vector enemy_left; trace_t trace; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return false; // Get the position to test if ( use_enemy_dir ) { // Get the enemy direction info enemy_angles = currentEnemy->angles; enemy_angles.AngleVectors( &enemy_forward, &enemy_left ); test_position = currentEnemy->origin; if ( test_pos == TELEPORT_BEHIND ) test_position -= enemy_forward * 135.0f; else if ( test_pos == TELEPORT_INFRONT ) test_position += enemy_forward * 135.0f; else if ( test_pos == TELEPORT_TOLEFT ) test_position += enemy_left * 135.0f; else test_position -= enemy_left * 135.0f; } else { test_position = currentEnemy->origin; if ( test_pos == TELEPORT_BEHIND ) test_position += Vector(-110, 0, 0); else if ( test_pos == TELEPORT_INFRONT ) test_position += Vector(110, 0, 0); else if ( test_pos == TELEPORT_TOLEFT ) test_position += Vector(0, -110, 0); else test_position += Vector(0, 110, 0); } test_position += Vector(0, 0, 64); // Test to see if we can fit at the new position trace = G_Trace( test_position, self.mins, self.maxs, test_position - Vector( "0 0 1000" ), &self, self.edict->clipmask, false, "Teleport::TestPosition" ); if ( trace.allsolid || trace.startsolid ) //if ( trace.allsolid ) return false; // Make sure we can see the enemy from this position if ( !self.IsEntityAlive( currentEnemy ) || !self.sensoryPerception->CanSeeEntity( Vector( trace.endpos ), currentEnemy , true , true ) ) return false; // This is a good position good_position = trace.endpos; return true; } BehaviorReturnCode_t Teleport::Evaluate ( Actor &self ) { int current_position; float random_number; Vector teleport_position; qboolean teleport_position_found; Vector new_position; int i; Vector dir; Vector angles; // Make sure we stll have an enemy to teleport near Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; // Default the teleport position to where we are now teleport_position = self.origin; teleport_position_found = false; // Determine which position to try first random_number = G_Random(); if ( random_number < .5f ) current_position = TELEPORT_BEHIND; else if ( random_number < .7f ) current_position = TELEPORT_TOLEFT; else if ( random_number < .9f ) current_position = TELEPORT_TORIGHT; else current_position = TELEPORT_INFRONT; // Try positions for( i = 0 ; i < TELEPORT_NUMBER_OF_POSITIONS ; i++ ) { // Test this position if ( TestPosition( self, current_position, new_position, true ) ) { teleport_position = new_position; teleport_position_found = true; break; } // Try the next position current_position++; if ( current_position >= TELEPORT_NUMBER_OF_POSITIONS ) current_position = 0; } if ( !teleport_position_found ) { // Try again with not using the enemies angles if ( current_position >= TELEPORT_NUMBER_OF_POSITIONS ) current_position = 0; for( i = 0 ; i < TELEPORT_NUMBER_OF_POSITIONS ; i++ ) { // Test this position if ( TestPosition( self, current_position, new_position, false ) ) { teleport_position = new_position; teleport_position_found = true; break; } // Try the next position current_position++; if ( current_position >= TELEPORT_NUMBER_OF_POSITIONS ) current_position = 0; } } // Do teleport stuff if ( teleport_position_found ) { self.setOrigin( teleport_position ); self.NoLerpThisFrame(); dir = currentEnemy->origin - teleport_position; angles = dir.toAngles(); angles[ROLL] = 0; angles[PITCH] = 0; self.setAngles( angles ); } return BEHAVIOR_SUCCESS; } void Teleport::End ( Actor &self ) { } /**************************************************************************** TeleportToPlayer Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TeleportToPlayer, NULL ) { { NULL, NULL } }; void TeleportToPlayer::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void TeleportToPlayer::Begin ( Actor &self ) { } qboolean TeleportToPlayer::TestPosition ( Actor &self, int test_pos, Vector &good_position, Entity* player, qboolean use_player_dir ) { Vector test_position; Vector player_angles; Vector player_forward; Vector player_left; trace_t trace; // Get the position to test test_position = player->origin; if ( use_player_dir ) { // Get the player direction info player_angles = player->angles; player_angles.AngleVectors( &player_forward, &player_left ); // Check Behind the Player test_position -= player_forward * 75.0f; } else { // Check Behind the Player test_position += Vector(-60, 0, 0); } // Final Tweaking test_position += Vector(0, 0, 64); // Test to see if we can fit at the new position trace = G_Trace( test_position, self.mins, self.maxs, test_position - Vector( "0 0 250" ), &self, self.edict->clipmask, false, "Teleport::TestPosition" ); if ( trace.allsolid || trace.startsolid ) return false; if ( trace.fraction == 1.0 ) return false; // Make sure we can see the Player from this position if ( !self.IsEntityAlive( player ) || !self.sensoryPerception->CanSeeEntity( Vector( trace.endpos ), player , true , true ) ) return false; // This is a good position good_position = trace.endpos; return true; } BehaviorReturnCode_t TeleportToPlayer::Evaluate ( Actor &self ) { int current_position; Vector teleport_position; qboolean teleport_position_found; Vector new_position; int i; Vector dir; Vector angles; Player *player = NULL; Player *temp_player = NULL; // Make sure the player is alive and well for(i = 0; i < game.maxclients; i++) { player = GetPlayer(i); // don't target while player is not in the game or he's in notarget if ( temp_player && !( temp_player->flags & FL_NOTARGET ) ) { player = temp_player; break; } } if ( !player ) return BEHAVIOR_SUCCESS; // Default the teleport position to where we are now teleport_position = self.origin; teleport_position_found = false; // Always teleport BEHIND the player - - we don't want him to see us pop in. current_position = TELEPORT_BEHIND; // Test this position if ( TestPosition( self, current_position, new_position, player, true ) ) { teleport_position = new_position; teleport_position_found = true; } else { if ( TestPosition( self, current_position, new_position, player, false ) ) { teleport_position = new_position; teleport_position_found = true; } } // Do teleport stuff if ( teleport_position_found ) { self.setOrigin( teleport_position ); self.NoLerpThisFrame(); dir = player->origin - teleport_position; angles = dir.toAngles(); angles[ROLL] = 0.0f; angles[PITCH] = 0.0f; self.setAngles( angles ); } return BEHAVIOR_SUCCESS; } void TeleportToPlayer::End ( Actor &self ) { } /**************************************************************************** TeleportToPosition Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, TeleportToPosition, NULL ) { { &EV_Behavior_Args, &TeleportToPosition::SetArgs }, { NULL, NULL } }; void TeleportToPosition::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void TeleportToPosition::SetArgs ( Event *ev ) { teleport_position_name = ev->GetString( 1 ); number_of_teleport_positions = ev->GetInteger( 2 ); } void TeleportToPosition::Begin ( Actor &self ) { } BehaviorReturnCode_t TeleportToPosition::Evaluate ( Actor &self ) { Vector dir; Vector angles; trace_t trace; str pathnode_name; PathNode *goal; Vector teleport_position; Vector attack_position; float half_height; //Entity *effect; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); // Get the pathnode name to teleport to pathnode_name = teleport_position_name; pathnode_name += (int)G_Random( (float)number_of_teleport_positions ) + 1 ; // Find the path node goal = thePathManager.FindNode( pathnode_name ); if ( !goal ) { gi.WDPrintf( "Can't find position %s\n", pathnode_name.c_str() ); return BEHAVIOR_SUCCESS; } // Set the teleport position teleport_position = goal->origin; // Kill anything at this position half_height = self.maxs.z / 2.0f; attack_position = teleport_position; attack_position.z += half_height; MeleeAttack( attack_position, attack_position, 10000.0f, &self, MOD_TELEFRAG, self.maxs.x, -half_height, half_height, 0 ); // Test to see if we can fit at the new position trace = G_Trace( teleport_position + Vector( "0 0 64" ), self.mins, self.maxs, teleport_position - Vector( "0 0 128" ), &self, MASK_PATHSOLID, false, "TeleportToPosition" ); //trace = G_Trace( teleport_position, self.mins, self.maxs, teleport_position, &self, MASK_PATHSOLID, false, "TeleportToPosition" ); if ( trace.allsolid ) { gi.WDPrintf( "Failed to teleport to %s\n", goal->targetname.c_str() ); return BEHAVIOR_SUCCESS; } teleport_position = trace.endpos; // Do teleport stuff /* // Spawn in teleport effect at old positiond effect = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); effect->setModel( "fx_teleport3.tik" ); effect->setOrigin( self.origin ); effect->setScale( 2.0f ); effect->setSolidType( SOLID_NOT ); effect->animate->RandomAnimate( "idle", EV_Remove ); //effect->Sound( "snd_teleport" ); */ // Set new position self.setOrigin( teleport_position ); // Spawn in teleport effect at new position /* effect = new Entity( ENTITY_CREATE_FLAG_ANIMATE ); effect->setModel( "fx_teleport3.tik" ); effect->setOrigin( self.origin ); effect->setScale( 2.0f ); effect->setSolidType( SOLID_NOT ); effect->animate->RandomAnimate( "idle", EV_Remove ); //effect->Sound( "snd_teleport" ); */ self.NoLerpThisFrame(); if ( currentEnemy ) { dir = currentEnemy->origin - teleport_position; angles = dir.toAngles(); angles[ROLL] = 0.0f; angles[PITCH] = 0.0f; self.setAngles( angles ); } return BEHAVIOR_SUCCESS; } void TeleportToPosition::End ( Actor &self ) { } /**************************************************************************** GhostAttack Class Definition ****************************************************************************/ #define GHOST_ATTACK_START 0 #define GHOST_ATTACK_END 1 #define GHOST_ATTACK_SPEED 350.0f CLASS_DECLARATION( Behavior, GhostAttack, NULL ) { { &EV_Behavior_Args, &GhostAttack::SetArgs }, { NULL, NULL } }; void GhostAttack::SetArgs ( Event *ev ) { real_attack = ev->GetBoolean( 1 ); } void GhostAttack::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void GhostAttack::Begin ( Actor &self ) { mode = GHOST_ATTACK_START; fly.Begin( self ); fly.SetTurnSpeed( 25.0f ); fly.SetRandomAllowed( false ); fly.SetSpeed( GHOST_ATTACK_SPEED ); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( currentEnemy ) attack_position = currentEnemy->centroid; else attack_position = self.origin; if ( !real_attack ) { attack_position[0] += G_CRandom( 300.0f ); attack_position[1] += G_CRandom( 300.0f ); attack_position[2] += G_Random( 100.0f ); } fly.SetGoalPoint( attack_position ); attack_dir = attack_position - self.origin; if ( attack_dir == vec_zero ) attack_dir = Vector(1, 1, 0); attack_dir.normalize(); self.Sound( "snd_fadein", CHAN_VOICE ); } BehaviorReturnCode_t GhostAttack::Evaluate ( Actor &self ) { Vector dir; float dist; float zdist; Vector new_pos; float new_alpha; float light_radius; float r, g, b; qboolean success; Vector start; Vector end; Event *event; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( mode == GHOST_ATTACK_START ) { // Move closer to the enemy fly.Evaluate( self ); // Get the new distance info dir = attack_position - self.origin; dist = dir.length(); zdist = dir.z; if ( zdist < 0.0f ) zdist = -zdist; dir.z = 0.0f; // If we are close enough change to shootable_only if ( real_attack && ( dist < 200.0f ) ) { // Attackable now self.setSolidType( SOLID_BBOX ); self.setContents( CONTENTS_SHOOTABLE_ONLY ); event = new Event( EV_Actor_SetTargetable ); event->AddInteger( 1 ); self.ProcessEvent( event ); } // If we are close enough damage enemy and goto end mode start = self.origin; end = self.origin + ( attack_dir * 1.0f ); if ( real_attack ) { success = MeleeAttack( start, end, 7.5f, &self, MOD_LIFEDRAIN, 32.0f, 0.0f, 64.0f, 0.0f ); self.AddStateFlag( STATE_FLAG_MELEE_HIT ); } else success = false; if ( success || ( dist <= GHOST_ATTACK_SPEED / 40.0f ) ) { // Attack mode is done go to retreat mode if ( self.attack_blocked && ( self.attack_blocked_time == level.time ) ) { Vector retreat_angles; dir = attack_dir * -1.0f; retreat_angles = dir.toAngles(); retreat_angles[YAW] += G_CRandom( 45.0f ); retreat_angles[PITCH] += G_CRandom( 30.0f ); retreat_angles.AngleVectors( &dir ); dir *= 500.0f; self.setAngles( retreat_angles ); } else { dir = attack_dir; dir.z = 0.0f; dir.normalize(); dir *= 1000.0f; dir.z = 300.0f; } retreat_position = self.origin + dir; fly.SetGoalPoint( retreat_position ); mode = GHOST_ATTACK_END; } // Fade in depending on how close we are to attack position if ( dist > 400.0f ) new_alpha = 0.0f; else new_alpha = ( 400.0f - dist ) / 400.0f; if ( new_alpha > 0.4f ) new_alpha = 0.4f; r = new_alpha / 0.4f; g = new_alpha / 0.4f; b = new_alpha / 0.4f; light_radius = 0.0f; G_SetConstantLight( &self.edict->s.constantLight, &r, &g, &b, &light_radius ); self.setAlpha( new_alpha ); } else { // Move away from enemy fly.Evaluate( self ); // Get the new distance info dir = attack_position - self.origin; dist = dir.length(); if ( real_attack && ( dist > 200.0f ) ) { // Not attackable again self.setSolidType( SOLID_NOT ); self.setContents( 0 ); event = new Event( EV_Actor_SetTargetable ); event->AddInteger( 0 ); self.ProcessEvent( event ); } // Fade out depending on how far we are from the attack position if ( dist > 400.0f ) new_alpha = 0.0f; else new_alpha = ( 400.0f - dist ) / 400.0f; if ( new_alpha > 0.4f ) new_alpha = 0.4f; r = new_alpha / 0.4f; g = new_alpha / 0.4f; b = new_alpha / 0.4f; light_radius = 0.0f; G_SetConstantLight( &self.edict->s.constantLight, &r, &g, &b, &light_radius ); self.setAlpha( new_alpha ); // See if we are far enough to be done if ( new_alpha == 0.0f ) return BEHAVIOR_SUCCESS; } return BEHAVIOR_EVALUATING; } void GhostAttack::End ( Actor &self ) { // Make sure we can't be shot any more self.setSolidType( SOLID_NOT ); self.setContents( 0 ); fly.End( self ); } /**************************************************************************** Levitate Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, Levitate, NULL ) { { &EV_Behavior_Args, &Levitate::SetArgs }, { NULL, NULL } }; void Levitate::SetArgs ( Event *ev ) { distance = ev->GetFloat( 1 ); speed = ev->GetFloat( 2 ); } void Levitate::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void Levitate::Begin ( Actor &self ) { final_z = self.origin.z + distance; } BehaviorReturnCode_t Levitate::Evaluate ( Actor &self ) { trace_t trace; Vector start; Vector end; start = self.origin; end = self.origin; if ( final_z < self.origin.z ) { end.z -= speed; if ( end.z < final_z ) end.z = final_z; } else { end.z += speed; if ( end.z > final_z ) end.z = final_z; } trace = G_Trace( start, self.mins, self.maxs, end, &self, self.edict->clipmask, false, "Levitate" ); if ( trace.fraction != 1.0 ) return BEHAVIOR_SUCCESS; if ( end.z == final_z ) return BEHAVIOR_SUCCESS; self.setOrigin( trace.endpos ); return BEHAVIOR_EVALUATING; } void Levitate::End ( Actor &self ) { } /**************************************************************************** ***************************************************************************** Utility functions ***************************************************************************** ****************************************************************************/ Vector ChooseRandomDirection ( Actor &self, const Vector &previousdir, float *time, int disallowcontentsmask, qboolean usepitch ) { //static float x[ 9 ] = { 0, 22, -22, 45, -45, 0, 22, -22, 45 }; static float x[ 5 ] = { 0, 22, -22, 0, 22 }; Vector dir; Vector ang; Vector bestdir; Vector newdir; Vector step; Vector endpos; Vector groundend; float bestfraction; trace_t trace; trace_t groundtrace; int i; int k; int t; int u; int contents; Vector centroid; float random_yaw; float movespeed; //if ( self.movespeed != 1 ) if ( self.movementSubsystem->getMoveSpeed() != 1.0f ) movespeed = self.movementSubsystem->getMoveSpeed(); else movespeed = 100.0f; centroid = self.centroid - self.origin; step = Vector( 0.0f, 0.0f, STEPSIZE ); bestfraction = -1.0f; bestdir = self.origin; random_yaw = G_Random( 360.0f ); for( i = 0; i <= 8; i++ ) { t = (int)random_yaw + ( i * 45 ); ang.y = self.angles.y + (float)t; if ( usepitch ) { u = ( int )G_Random( 5.0f ); //for( k = 0; k < 5; k++ ) for( k = 0; k < 3; k++ ) { ang.x = x[ k + u ]; ang.AngleVectors( &dir, NULL, NULL ); dir *= movespeed * (*time); dir += self.origin; trace = G_Trace( self.origin, self.mins, self.maxs, dir, &self, self.edict->clipmask, false, "ChooseRandomDirection 1" ); if ( !trace.startsolid && !trace.allsolid ) { newdir = Vector( trace.endpos ); if ( disallowcontentsmask ) { contents = gi.pointcontents( ( newdir + centroid ), 0 ); if ( contents & disallowcontentsmask ) continue; } if ( ( trace.fraction > bestfraction ) && ( newdir != bestdir ) && ( newdir != previousdir ) ) { bestdir = newdir; bestfraction = trace.fraction; if ( bestfraction > .9f ) { *time *= bestfraction; return bestdir; } } } } } else { ang.x = 0.0f; ang.AngleVectors( &dir, NULL, NULL ); endpos = self.origin + ( dir * movespeed * (*time) ) + step; trace = G_Trace( self.origin + step, self.mins, self.maxs, endpos, &self, self.edict->clipmask, false, "ChooseRandomDirection 2" ); if ( !trace.startsolid && !trace.allsolid ) { newdir = Vector( trace.endpos ); if ( disallowcontentsmask ) { contents = gi.pointcontents( ( newdir + centroid ), 0 ); if ( contents & disallowcontentsmask ) continue; } if ( ( trace.fraction > bestfraction ) && ( newdir != bestdir ) && ( newdir != previousdir ) ) { groundend = endpos - ( step * 2.0f ); groundtrace = G_Trace( endpos, self.mins, self.maxs, groundend, &self, self.edict->clipmask, false, "Chase::ChooseRandomDirection 3" ); if ( groundtrace.fraction == 1.0f ) trace.fraction /= 2.0f; if ( trace.fraction > bestfraction ) { bestdir = newdir; bestfraction = trace.fraction; if ( bestfraction > .9f ) { *time *= bestfraction; return bestdir; } } } } } } if ( bestfraction > 0.0f ) { *time *= bestfraction; } else { *time = 0.0f; } return bestdir; } // // WayPoint Stuff // /**************************************************************************** GotoWayPoint Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, GotoWayPoint, NULL ) { { &EV_Behavior_Args, &GotoWayPoint::SetArgs }, { NULL, NULL } }; void GotoWayPoint::SetArgs ( Event *ev ) { path_name = ev->GetString( 1 ); anim = ev->GetString( 2 ); current_waypoint_name = path_name; current_waypoint = NULL; } void GotoWayPoint::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void GotoWayPoint::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } if ( anim != self.animname || self.newanim.length() ) { self.SetAnim( anim ); } chase.Begin( self ); next_think_time = 0.0f; } BehaviorReturnCode_t GotoWayPoint::Evaluate ( Actor &self ) { //First Check if we are at a way point; Vector dest; Vector check; if (!current_waypoint) { current_waypoint = GetWayPoint( current_waypoint_name ); if (!current_waypoint) return BEHAVIOR_SUCCESS; } dest = current_waypoint->origin; check = dest - self.origin; // Check if we're close enough if ( check.length() < 10.0f ) { // Run Our Thread str waypointThread; waypointThread = current_waypoint->GetThread(); if (waypointThread.length() ) self.RunThread( waypointThread ); // See if we have another point to go to if ( current_waypoint->target.length() == 0 ) { return BEHAVIOR_SUCCESS; } // Check for new anim anim = ""; anim = current_waypoint->GetActorAnim(); if ( anim.length() ) self.SetAnim( anim ); // Go To the Next Point current_waypoint_name = current_waypoint->target; current_waypoint = GetWayPoint( current_waypoint_name ); if (!current_waypoint) return BEHAVIOR_SUCCESS; dest = current_waypoint->origin; //Clear out anim strings anim = ""; } float radius=96.0f; chase.SetGoal( current_waypoint->origin, radius, self ); chase.Evaluate(self); return BEHAVIOR_EVALUATING; } void GotoWayPoint::End ( Actor &self ) { chase.End( self ); } WayPointNode* GotoWayPoint::GetWayPoint ( const str &waypoint_name ) { Entity* ent_in_range; gentity_t *ed; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { if (!Q_stricmp(ent_in_range->targetname.c_str() , waypoint_name.c_str() )) { return (WayPointNode*)ent_in_range; } } } return NULL; } /**************************************************************************** FlyCircleAroundWaypoint Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, FlyCircleAroundWaypoint, NULL ) { { &EV_Behavior_Args, &FlyCircleAroundWaypoint::SetArgs }, { NULL, NULL } }; FlyCircleAroundWaypoint::FlyCircleAroundWaypoint() { anim = "fly"; nearestPlayer = false; fly_clockwise = true; } void FlyCircleAroundWaypoint::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); fly_clockwise = ev->GetBoolean( 2 ); waypointname = ev->GetString( 3 ); if (ev->NumArgs() > 3 ) fly.SetSpeed( ev->GetFloat( 4 ) ); if (ev->NumArgs() > 4 ) fly.setAdjustYawAndRoll( ev->GetBoolean( 5 ) ); if ( !Q_stricmp(waypointname.c_str() , "player" ) ) nearestPlayer = true; } void FlyCircleAroundWaypoint::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void FlyCircleAroundWaypoint::Begin ( Actor &self ) { //original_z = self.origin.z; if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( 5.0f ); } BehaviorReturnCode_t FlyCircleAroundWaypoint::Evaluate ( Actor &self ) { Vector goal; trace_t trace; Vector dir; Vector angle; Vector left; qboolean too_far = false; Vector new_dir; Vector fly_dir; WayPointNode *goalNode; goalNode = NULL; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) ) { return BEHAVIOR_SUCCESS; } //if ( self.lastmove == STEPMOVE_OK ) if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) { fly.SetTurnSpeed( 5.0f ); if ( nearestPlayer ) goalNode = GetWayPointNearestPlayer( self ); else goalNode = GetWayPoint( self ); if ( !goalNode ) return BEHAVIOR_SUCCESS; dir = goalNode->origin - self.origin; dir.z = 0; if ( dir.length() > 200.0f ) //radius { too_far = true; } angle = dir.toAngles(); angle.AngleVectors( NULL, &left, NULL ); if ( fly_clockwise ) fly_dir = left; else fly_dir = left * -1.0f; dir.normalize(); if ( too_far ) { new_dir = ( fly_dir * 0.5f ) + ( dir * 0.5f ); new_dir.normalize(); } else { new_dir = fly_dir; } goal = self.origin + ( new_dir * 200.0f ); trace = G_Trace( self.origin, self.mins, self.maxs, goal, &self, self.edict->clipmask, false, "FlyCircle" ); if ( trace.fraction < 1.0f ) { if ( too_far ) trace.fraction /= 2.0f; new_dir = ( fly_dir * trace.fraction ) + ( dir * ( 1.0f - trace.fraction ) ); new_dir.normalize(); goal = self.origin + ( new_dir * 200.0f ); } else { goal = trace.endpos; } fly.SetGoalPoint( goal ); } else { fly.SetTurnSpeed( 20.0f ); } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } WayPointNode *FlyCircleAroundWaypoint::GetWayPoint( Actor &self ) { Entity* ent_in_range; gentity_t *ed; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { if (!Q_stricmp(ent_in_range->targetname.c_str() , waypointname.c_str() )) { return (WayPointNode*)ent_in_range; } } } return NULL; } WayPointNode *FlyCircleAroundWaypoint::GetWayPointNearestPlayer( Actor &self) { Vector DistanceFromPlayer; float distance; Entity* ent_in_range; Entity* best_ent; gentity_t *ed; ent_in_range = NULL; best_ent = NULL; distance = 100000000; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return NULL; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { DistanceFromPlayer = ent_in_range->origin - currentEnemy->origin; if (DistanceFromPlayer.length() < distance ) { best_ent = ent_in_range; distance = DistanceFromPlayer.length(); } } } return (WayPointNode*)best_ent; } void FlyCircleAroundWaypoint::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** HelicopterFlyToPoint Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HelicopterFlyToPoint, NULL ) { { NULL, NULL } }; HelicopterFlyToPoint::HelicopterFlyToPoint() { turn_speed = 10.0; old_turn_speed = turn_speed; speed = 480.0; random_allowed = true; force_goal = false; adjustYawAndRoll = true; offsetOrigin = false; avoidtime = 0; old_forward_speed = 0; stuck = 0; use_temp_goal = false; } void HelicopterFlyToPoint::SetTurnSpeed( float new_turn_speed ) { turn_speed = new_turn_speed; } void HelicopterFlyToPoint::SetGoalPoint( const Vector &goal_point ) { if ( goal_point != goal ) avoidtime = 0; goal = goal_point; } void HelicopterFlyToPoint::SetRandomAllowed( qboolean allowed ) { random_allowed = allowed; } void HelicopterFlyToPoint::SetSpeed( float speed_value ) { speed = speed_value; } void HelicopterFlyToPoint::ForceGoal( void ) { force_goal = true; } void HelicopterFlyToPoint::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void HelicopterFlyToPoint::Begin ( Actor &self ) { avoidtime = 0; //old_forward_speed = self.forwardspeed; old_forward_speed = self.movementSubsystem->getForwardSpeed(); stuck = 0; use_temp_goal = false; } BehaviorReturnCode_t HelicopterFlyToPoint::Evaluate ( Actor &self ) { trace_t trace; Vector dir; Vector ang; float time; float length; float old_yaw; float old_pitch; float x_offset = 0.0f; float y_offset = 0.0f; //float z_offset = 0.0f; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if (offsetOrigin) { x_offset = self.centroid.x - self.origin.x; y_offset = self.centroid.y - self.origin.y; //z_offset = self.centroid.z - self.origin.z; self.origin.x+=x_offset; self.origin.y+=y_offset; //self.origin.z+=z_offset; } //if ( self.lastmove != STEPMOVE_OK ) if ( self.movementSubsystem->getLastMove() != STEPMOVE_OK ) stuck++; else stuck = 0; if ( ( stuck > 1 ) || ( avoidtime <= level.time ) ) { time = G_Random( .3f ) + .3f; use_temp_goal = false; if ( !force_goal ) { trace = G_Trace( self.origin, self.mins, self.maxs, goal, &self, self.edict->clipmask, false, "FlyToPoint" ); if ( ( trace.fraction < 0.5f ) || ( stuck > 2 ) ) { old_turn_speed = self.movementSubsystem->getTurnSpeed(); self.movementSubsystem->setTurnSpeed( 60.0f ); //self.turnspeed = 60; temp_goal = ChooseRandomDirection( self, goal, &time, MASK_WATER, false ); self.movementSubsystem->setTurnSpeed( old_turn_speed ); //self.turnspeed = old_turn_speed; use_temp_goal = true; avoidtime = level.time + time; stuck = 0; } else { goal = trace.endpos; avoidtime = level.time + time; } } else { avoidtime = level.time + time; } if ( use_temp_goal ) dir = temp_goal - self.origin; else dir = goal - self.origin; length = dir.length(); dir.normalize(); //new stuff //self.FlightDir = dir; ang = dir.toAngles(); if ( ( length > 150.0f ) && random_allowed && !use_temp_goal ) { //ang[YAW] += G_Random( 20 ) - 5.0; //ang[PITCH] += G_Random( 20 ) - 5.0; } target_angle = ang; target_angle[YAW] = AngleNormalize180( target_angle[YAW] ); target_angle[PITCH] = AngleNormalize180( target_angle[PITCH] ); if ( currentEnemy ) { Vector enemyDir; enemyDir = currentEnemy->origin - self.origin; enemyDir.normalize(); target_angle[PITCH] = AngleNormalize180(-enemyDir.toPitch()); if ( target_angle[PITCH] < -40.0f ) target_angle[PITCH] = -40.0f; if ( target_angle[PITCH] > 40.0f ) target_angle[PITCH] = 40.0f; target_angle[YAW] = enemyDir.toYaw(); target_angle[ROLL] = 0.0f; } } target_angle[YAW] = AngleNormalize360( target_angle[YAW] ); target_angle[PITCH] = AngleNormalize360( target_angle[PITCH] ); if ( (self.angles[YAW] != target_angle[YAW]) || (self.angles[PITCH] != target_angle[PITCH]) ) { self.movementSubsystem->setForwardSpeed( speed * 0.8f ); //self.forwardspeed = speed * 0.8f; } else { self.movementSubsystem->setForwardSpeed( speed ); //self.forwardspeed = speed; } old_yaw = self.angles[YAW]; old_pitch = self.angles[PITCH]; ang[YAW] = LerpAngle( self.angles[YAW], target_angle[YAW], turn_speed ); ang[PITCH] = LerpAngle( self.angles[PITCH], target_angle[PITCH], turn_speed ); ang[ROLL] = self.angles[ROLL]; /* if( adjustYawAndRoll ) { if ( (AngleDelta( ang[YAW], old_yaw ) > 0) && (ang[ROLL] > 315 || ang[ROLL] <= 45) ) { ang[ROLL] += 5; } else if ( (AngleDelta( ang[YAW], old_yaw ) < 0) && (ang[ROLL] < 45 || ang[ROLL] >= 315) ) { ang[ROLL] -= 5; } else if ( (AngleDelta( ang[YAW], old_yaw ) == 0 ) ) { if ( ang[ROLL] != 0 ) { if ( ang[ROLL] < 5 || ang[ROLL] > 355 ) { ang[ROLL] = 0; } else { if ( ang[ROLL] < 180 ) ang[ROLL] += 5; else ang[ROLL] -= 5; } } } } */ if ( adjustYawAndRoll ) { //Vector travelAngleDelta = dir.toAngles(); float yawDelta = dir[YAW] - self.angles[YAW]; yawDelta = AngleNormalize360(yawDelta); if ( ( yawDelta > 0.0f ) && ( yawDelta <= 180.0f ) ) { ang[ROLL] = self.angles[ROLL] + 2.0f; if ( AngleNormalize180(ang[ROLL]) > 30.0f ) ang[ROLL] = 30.0f; } if ( ( yawDelta >= 180.0f ) && ( yawDelta <= 359.0f ) ) { ang[ROLL] = self.angles[ROLL] - 2.0f; if ( AngleNormalize180(ang[ROLL]) < -30.0f ) ang[ROLL] = -30.0f; } } ang[YAW] = AngleNormalize360( ang[YAW] ); ang[PITCH] = AngleNormalize360( ang[PITCH] ); ang[ROLL] = AngleNormalize360( ang[ROLL] ); // Don't get stuck if still turning if ( ( AngleDelta( ang[YAW], old_yaw ) > .5f ) || ( AngleDelta( ang[YAW], old_yaw ) < -.5f ) || ( AngleDelta( ang[PITCH], old_pitch ) > .5f ) || ( AngleDelta( ang[PITCH], old_pitch ) < -.5f ) ) { stuck = 0; } self.setAngles( ang ); if (offsetOrigin) { self.origin.x-=x_offset; self.origin.y-=y_offset; //self.origin.z-=z_offset; } return BEHAVIOR_EVALUATING; } float HelicopterFlyToPoint::LerpAngle( float old_angle, float new_angle, float lerp_amount ) { float diff; float abs_diff; float lerp_angle; new_angle = AngleNormalize360( new_angle ); old_angle = AngleNormalize360( old_angle ); diff = new_angle - old_angle; if ( diff > 180.0f ) { diff -= 360.0f; } if ( diff < -180.0f ) { diff += 360.0f; } lerp_angle = old_angle; abs_diff = diff; if ( abs_diff < 0.0f ) { abs_diff = -abs_diff; } if ( abs_diff < lerp_amount ) { lerp_amount = abs_diff; } if ( diff < 0.0f ) { lerp_angle -= lerp_amount; } else if ( diff > 0.0f ) { lerp_angle += lerp_amount; } lerp_angle = AngleNormalize360( lerp_angle ); return lerp_angle; } void HelicopterFlyToPoint::End ( Actor &self ) { self.movementSubsystem->setForwardSpeed( old_forward_speed ); //self.FlightDir = vec_zero; } /**************************************************************************** FlyCircle Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HelicopterFlyCircle, NULL ) { { &EV_Behavior_Args, &HelicopterFlyCircle::SetArgs }, { NULL, NULL } }; HelicopterFlyCircle::HelicopterFlyCircle() { anim = "fly"; fly_clockwise = true; circle_player = false; } void HelicopterFlyCircle::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); fly_clockwise = ev->GetBoolean( 2 ); if (ev->NumArgs() > 2 ) fly.setAdjustYawAndRoll( false ); //fly.setAdjustYawAndRoll( ev->GetBoolean( 3 ) ); if (ev->NumArgs() > 3 ) circle_player = ev->GetBoolean ( 4 ); else circle_player = false; if (ev->NumArgs() > 4 ) fly.SetSpeed( ev->GetFloat( 5 ) ); } void HelicopterFlyCircle::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void HelicopterFlyCircle::Begin ( Actor &self ) { //original_z = self.origin.z; if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( 5.0f ); } BehaviorReturnCode_t HelicopterFlyCircle::Evaluate ( Actor &self ) { Vector goal; trace_t trace; Vector dir; Vector angle; Vector left; qboolean too_far = false; Vector new_dir; Vector fly_dir; //Vector or; //or = self.currentEnemy->centroid - self.origin; //or = or.toAngles(); //self.angles = or; //self.setAngles( or ); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) && !circle_player ) { return BEHAVIOR_SUCCESS; } //if ( self.lastmove == STEPMOVE_OK ) if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) { fly.SetTurnSpeed( 5.0f ); if (circle_player) { Player *player = NULL; Player *temp_player = NULL; // Make sure the player is alive and well for(int i = 0; i < game.maxclients; i++) { temp_player = GetPlayer(i); // don't target while player is not in the game or he's in notarget if ( temp_player && !( temp_player->flags & FL_NOTARGET ) ) { player = temp_player; break; } } if ( !player ) return BEHAVIOR_SUCCESS; dir = player->centroid - self.origin; dir.z = 0; //if ( dir.length() > (self.origin.z - player->centroid.z) / .5 ) if ( dir.length() > 600.0f ) { too_far = true; } } else { dir = currentEnemy->centroid - self.origin; dir.z = 0; if ( dir.length() > ( ( self.origin.z - currentEnemy->centroid.z) / 2.0f ) ) { too_far = true; } } angle = dir.toAngles(); angle.AngleVectors( NULL, &left, NULL ); Vector newAngles = self.angles; if ( fly_clockwise ) { fly_dir = left; newAngles[ROLL] -= 5.0f; if ( AngleNormalize180(newAngles[ROLL]) < -30.0f ) newAngles[ROLL] = -30.0f; } else { fly_dir = left * -1.0f; newAngles[ROLL] += 5.0f; if ( AngleNormalize180(newAngles[ROLL]) > 30.0f ) newAngles[ROLL] = 30.0f; } self.setAngles(newAngles); dir.normalize(); if ( too_far ) { new_dir = ( fly_dir * 0.5f ) + ( dir * 0.5f ); new_dir.normalize(); } else { new_dir = fly_dir; } goal = self.origin + ( new_dir * 200.0f ); trace = G_Trace( self.origin, self.mins, self.maxs, goal, &self, self.edict->clipmask, false, "FlyCircle" ); if ( trace.fraction < 1.0f ) { if ( too_far ) trace.fraction /= 2.0f; new_dir = ( fly_dir * trace.fraction ) + ( dir * ( 1.0f - trace.fraction ) ); new_dir.normalize(); goal = self.origin + ( new_dir * 200.0f ); } else { goal = trace.endpos; } fly.SetGoalPoint( goal ); } else { fly.SetTurnSpeed( 20.0f ); } fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } void HelicopterFlyCircle::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** HelicopterStrafeAttack Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HelicopterStrafeAttack, NULL ) { { &EV_Behavior_Args, &HelicopterStrafeAttack::SetArgs }, { NULL, NULL } }; HelicopterStrafeAttack::HelicopterStrafeAttack() { anim = "fly"; turnTime = 5; setUpLerp = false; completedLerp = false; lerpStart = 0.0f; lerpEnd = 0.0f; startYaw = 0.0f; endYaw = 0.0f; } void HelicopterStrafeAttack::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); } void HelicopterStrafeAttack::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void HelicopterStrafeAttack::Begin ( Actor &self ) { self.SetAnim(anim); Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return; //First we find the waypoint closest to the player //These waypoints should be pointed along the axis //that we want the helicopter to strafe WayPointNode *wp = GetWayPointNearestPlayer(self); str tName = wp->Target(); WayPointNode *target = GetWayPoint(self , tName ); //Set up goal location //goal = target->origin; goal = wp->origin; goal.z = self.origin.z; //We need the cross product Vector wpForward; Vector wpUp; Vector cross; wpForward = target->origin - wp->origin; wpForward.normalize(); wpUp.x = 0; wpUp.y = 0; wpUp.z = 1; CrossProduct(wpForward, wpUp, cross ); //wpOrigin.AngleVectors(NULL,&left,NULL); //Get the vector from the player to the actor Vector playerToActor; playerToActor = self.origin - currentEnemy->origin; //Normalize the vectors playerToActor.normalize(); cross.normalize(); //Now we get the Dot product to see if towardPlayer float dot; dot = DotProduct(cross,playerToActor); //Now we get our actual direction Vector; if ( dot > 0.0f ) dir = cross * -1.0f; else dir = cross; //Now set our angles appropriately targetAngles = self.angles; targetAngles[YAW] = dir.toYaw(); startYaw = AngleNormalize360(self.angles[YAW]); endYaw = AngleNormalize360(targetAngles[YAW]); if ( ( endYaw == 0.0f ) && ( startYaw > 180.0f ) ) endYaw = 360.0f; } BehaviorReturnCode_t HelicopterStrafeAttack::Evaluate ( Actor &self ) { Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !setUpLerp ) { lerpStart = level.time; lerpEnd = lerpStart + turnTime; setUpLerp = true; } if ( !completedLerp ) LerpToNewAngle( self ); //Get our travel destination Vector travelDir; travelDir = goal - self.origin; //Check if we are at our destination float length = travelDir.length(); if ( length < 25.0f ) //We're close enough return BEHAVIOR_SUCCESS; travelDir.normalize(); //self.FlightDir = travelDir; self.movementSubsystem->setForwardSpeed( 300.0f ); return BEHAVIOR_EVALUATING; } WayPointNode *HelicopterStrafeAttack::GetWayPointNearestPlayer( Actor &self) { Vector DistanceFromPlayer; float distance; Entity* ent_in_range; Entity* best_ent; gentity_t *ed; ent_in_range = NULL; best_ent = NULL; distance = 100000000; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return NULL; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { DistanceFromPlayer = ent_in_range->origin - currentEnemy->origin; if (DistanceFromPlayer.length() < distance ) { best_ent = ent_in_range; distance = DistanceFromPlayer.length(); } } } return (WayPointNode*)best_ent; } WayPointNode *HelicopterStrafeAttack::GetWayPoint( Actor &self , const str &name ) { Entity* ent_in_range; gentity_t *ed; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { if (!Q_stricmp(ent_in_range->targetname.c_str() , name.c_str() )) { return (WayPointNode*)ent_in_range; } } } return NULL; } void HelicopterStrafeAttack::LerpToNewAngle( Actor &self ) { Vector ang; float newYaw; ang = self.angles; newYaw = ( endYaw - startYaw ) / ( lerpEnd - lerpStart ); newYaw = newYaw * (level.time - lerpStart ); newYaw = newYaw + startYaw; ang[YAW] = newYaw; // Clamp it down in the end if ( level.time >= lerpEnd ) { ang[YAW] = endYaw; completedLerp = true; } self.setAngles(ang); } void HelicopterStrafeAttack::End ( Actor &self ) { //self.FlightDir = vec_zero; } /**************************************************************************** HelicopterFlyToWaypoint Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, HelicopterFlyToWaypoint, NULL ) { { &EV_Behavior_Args, &HelicopterFlyToWaypoint::SetArgs }, { NULL, NULL } }; HelicopterFlyToWaypoint::HelicopterFlyToWaypoint() { anim = "fly"; nearestPlayer = false; nearestPlayerTarget = false; } void HelicopterFlyToWaypoint::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); waypointname = ev->GetString( 2 ); if (ev->NumArgs() > 2 ) fly.SetSpeed( ev->GetFloat( 3 ) ); if (ev->NumArgs() > 3 ) fly.setAdjustYawAndRoll( ev->GetBoolean( 4 ) ); if ( !Q_stricmp(waypointname.c_str() , "player" ) ) nearestPlayer = true; if ( !Q_stricmp(waypointname.c_str() , "target" ) ) nearestPlayerTarget = true; } void HelicopterFlyToWaypoint::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); } void HelicopterFlyToWaypoint::Begin ( Actor &self ) { if ( anim.length() ) { self.SetAnim( anim ); } fly.Begin( self ); fly.SetTurnSpeed( 5.0f ); } BehaviorReturnCode_t HelicopterFlyToWaypoint::Evaluate ( Actor &self ) { Vector goal; WayPointNode *goalNode; goalNode = NULL; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return BEHAVIOR_SUCCESS; if ( !self.IsEntityAlive( currentEnemy ) ) { return BEHAVIOR_SUCCESS; } //if ( self.lastmove == STEPMOVE_OK ) if ( self.movementSubsystem->getLastMove() == STEPMOVE_OK ) { fly.SetTurnSpeed( 5.0f ); if ( nearestPlayer ) goalNode = GetWayPointNearestPlayer( self ); else if ( nearestPlayerTarget ) { goalNode = GetWayPointNearestPlayer( self ); waypointname = goalNode->TargetName(); } if ( !nearestPlayer ) goalNode = GetWayPoint( self ); } if ( !goalNode ) return BEHAVIOR_SUCCESS; goal = goalNode->origin; goal.z = self.origin.z; //Check if we have arrived Vector dir; dir = self.origin - goal; if ( dir.length() <= 25.0f ) return BEHAVIOR_SUCCESS; fly.SetGoalPoint( goal ); fly.Evaluate( self ); return BEHAVIOR_EVALUATING; } WayPointNode *HelicopterFlyToWaypoint::GetWayPoint( Actor &self ) { Entity* ent_in_range; gentity_t *ed; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { if (!Q_stricmp(ent_in_range->targetname.c_str() , waypointname.c_str() )) { return (WayPointNode*)ent_in_range; } } } return NULL; } WayPointNode *HelicopterFlyToWaypoint::GetWayPointNearestPlayer( Actor &self) { Vector DistanceFromPlayer; float distance; Entity* ent_in_range; Entity* best_ent; gentity_t *ed; ent_in_range = NULL; best_ent = NULL; distance = 100000000; Entity *currentEnemy; currentEnemy = self.enemyManager->GetCurrentEnemy(); if ( !currentEnemy ) return NULL; for ( int i = 0; i < MAX_GENTITIES; i++ ) { ed = &g_entities[i]; if ( !ed->inuse || !ed->entity ) { continue; } ent_in_range = g_entities[i].entity; if( ent_in_range->isSubclassOf( WayPointNode ) ) { DistanceFromPlayer = ent_in_range->origin - currentEnemy->origin; if (DistanceFromPlayer.length() < distance ) { best_ent = ent_in_range; distance = DistanceFromPlayer.length(); } } } return (WayPointNode*)best_ent; } void HelicopterFlyToWaypoint::End ( Actor &self ) { fly.End( self ); } /**************************************************************************** GetWithinRangeOfPlayer Class Definition ****************************************************************************/ CLASS_DECLARATION( Behavior, GetWithinRangeOfPlayer, NULL ) { { &EV_Behavior_Args, &GetWithinRangeOfPlayer::SetArgs }, { NULL, NULL } }; void GetWithinRangeOfPlayer::SetArgs ( Event *ev ) { anim = ev->GetString( 1 ); if ( ev->NumArgs() > 1 ) forever = ev->GetBoolean( 2 ); else forever = true; if ( ev->NumArgs() > 2 ) speed = ev->GetFloat( 3 ); else speed = 100; if ( ev->NumArgs() > 3 ) chase.SetRadius( ev->GetFloat( 4 ) ); } void GetWithinRangeOfPlayer::ShowInfo ( Actor &self ) { Behavior::ShowInfo( self ); gi.Printf( "\nchase:\n" ); chase.ShowInfo( self ); } void GetWithinRangeOfPlayer::Begin ( Actor &self ) { if ( !anim.length() ) { anim = "run"; } if ( anim != self.animname || self.newanim.length() ) { self.SetAnim( anim, EV_Actor_NotifyBehavior ); } chase.Begin( self ); wander.Begin( self ); self.movementSubsystem->setForwardSpeed( speed ); next_think_time = 0; startRangeMin = self.preferredMax; startRangeMax = startRangeMin * 1.25; } BehaviorReturnCode_t GetWithinRangeOfPlayer::Evaluate ( Actor &self ) { Steering::ReturnValue result=Steering::EVALUATING; Player *player = NULL; Player *temp_player = NULL; // Make sure the player is alive and well for(int i = 0; i < game.maxclients; i++) { temp_player = GetPlayer(i); // don't target while player is not in the game or he's in notarget if( temp_player && !( temp_player->flags & FL_NOTARGET ) ) { player = temp_player; break; } } if ( !player ) return BEHAVIOR_SUCCESS; if ( next_think_time <= level.time ) { if ( self.groundentity && ( self.groundentity->s.number == player->entnum ) ) { wander.Evaluate( self ); result = Steering::SUCCESS; } else { float radius=96.0f; chase.SetGoal( player, radius, self ); result = chase.Evaluate( self ); } if ( self.GetActorFlag( ACTOR_FLAG_SIMPLE_PATHFINDING ) ) next_think_time = level.time + ( 2.0f * FRAMETIME ); else next_think_time = 0.0f; } else result = Steering::SUCCESS; if ( !forever && ( result == Steering::SUCCESS) ) return BEHAVIOR_SUCCESS; if ( !forever) { switch (result) { case Steering::EVALUATING: return BEHAVIOR_EVALUATING; break; case Steering::SUCCESS: return BEHAVIOR_SUCCESS; break; case Steering::FAILED_BLOCKED_BY_ENEMY: // lint -fallthrough case Steering::FAILED_BLOCKED_BY_CIVILIAN: // lint -fallthrough case Steering::FAILED_BLOCKED_BY_FRIEND: // lint -fallthrough case Steering::FAILED_BLOCKED_BY_TEAMMATE: // lint -fallthrough case Steering::FAILED_BLOCKED_BY_WORLD: // lint -fallthrough case Steering::FAILED_BLOCKED_BY_DOOR: // lint -fallthrough case Steering::FAILED_NO_PATH: // lint -fallthrough case Steering::FAILED: // lint -fallthrough return BEHAVIOR_FAILED; break; } } return BEHAVIOR_EVALUATING; } void GetWithinRangeOfPlayer::End ( Actor &self ) { chase.End( self ); wander.End( self ); self.movementSubsystem->setForwardSpeed( 0 ); }