ef2gamesource/dlls/game/behavior.cpp

12929 lines
232 KiB
C++

//-----------------------------------------------------------------------------
//
// $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 = &current_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 );
}