2011-11-22 21:28:15 +00:00
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 GPL Source Code
2011-12-06 18:20:15 +00:00
Copyright ( C ) 1999 - 2011 id Software LLC , a ZeniMax Media company .
2011-11-22 21:28:15 +00:00
2011-12-06 16:14:59 +00:00
This file is part of the Doom 3 GPL Source Code ( " Doom 3 Source Code " ) .
2011-11-22 21:28:15 +00:00
Doom 3 Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 Source Code is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 Source Code is also subject to certain additional terms . You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code . If not , please request a copy in writing from id Software at the address below .
If you have questions concerning this license or the applicable additional terms , you may contact in writing id Software LLC , c / o ZeniMax Media Inc . , Suite 120 , Rockville , Maryland 20850 USA .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
2011-12-16 22:28:29 +00:00
# include "sys/platform.h"
# include "idlib/math/Quat.h"
2011-11-22 21:28:15 +00:00
2011-12-16 22:28:29 +00:00
# include "gamesys/SysCvar.h"
# include "Moveable.h"
# include "SmokeParticles.h"
# include "ai/AI.h"
2011-11-22 21:28:15 +00:00
static const char * moveCommandString [ NUM_MOVE_COMMANDS ] = {
" MOVE_NONE " ,
" MOVE_FACE_ENEMY " ,
" MOVE_FACE_ENTITY " ,
" MOVE_TO_ENEMY " ,
" MOVE_TO_ENEMYHEIGHT " ,
" MOVE_TO_ENTITY " ,
" MOVE_OUT_OF_RANGE " ,
" MOVE_TO_ATTACK_POSITION " ,
" MOVE_TO_COVER " ,
" MOVE_TO_POSITION " ,
" MOVE_TO_POSITION_DIRECT " ,
" MOVE_SLIDE_TO_POSITION " ,
" MOVE_WANDER "
} ;
/*
= = = = = = = = = = = = = = = = = = = = =
idMoveState : : idMoveState
= = = = = = = = = = = = = = = = = = = = =
*/
idMoveState : : idMoveState ( ) {
moveType = MOVETYPE_ANIM ;
moveCommand = MOVE_NONE ;
moveStatus = MOVE_STATUS_DONE ;
moveDest . Zero ( ) ;
moveDir . Set ( 1.0f , 0.0f , 0.0f ) ;
goalEntity = NULL ;
goalEntityOrigin . Zero ( ) ;
toAreaNum = 0 ;
startTime = 0 ;
duration = 0 ;
speed = 0.0f ;
range = 0.0f ;
wanderYaw = 0 ;
nextWanderTime = 0 ;
blockTime = 0 ;
obstacle = NULL ;
lastMoveOrigin = vec3_origin ;
lastMoveTime = 0 ;
anim = 0 ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idMoveState : : Save
= = = = = = = = = = = = = = = = = = = = =
*/
void idMoveState : : Save ( idSaveGame * savefile ) const {
savefile - > WriteInt ( ( int ) moveType ) ;
savefile - > WriteInt ( ( int ) moveCommand ) ;
savefile - > WriteInt ( ( int ) moveStatus ) ;
savefile - > WriteVec3 ( moveDest ) ;
savefile - > WriteVec3 ( moveDir ) ;
goalEntity . Save ( savefile ) ;
savefile - > WriteVec3 ( goalEntityOrigin ) ;
savefile - > WriteInt ( toAreaNum ) ;
savefile - > WriteInt ( startTime ) ;
savefile - > WriteInt ( duration ) ;
savefile - > WriteFloat ( speed ) ;
savefile - > WriteFloat ( range ) ;
savefile - > WriteFloat ( wanderYaw ) ;
savefile - > WriteInt ( nextWanderTime ) ;
savefile - > WriteInt ( blockTime ) ;
obstacle . Save ( savefile ) ;
savefile - > WriteVec3 ( lastMoveOrigin ) ;
savefile - > WriteInt ( lastMoveTime ) ;
savefile - > WriteInt ( anim ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idMoveState : : Restore
= = = = = = = = = = = = = = = = = = = = =
*/
void idMoveState : : Restore ( idRestoreGame * savefile ) {
savefile - > ReadInt ( ( int & ) moveType ) ;
savefile - > ReadInt ( ( int & ) moveCommand ) ;
savefile - > ReadInt ( ( int & ) moveStatus ) ;
savefile - > ReadVec3 ( moveDest ) ;
savefile - > ReadVec3 ( moveDir ) ;
goalEntity . Restore ( savefile ) ;
savefile - > ReadVec3 ( goalEntityOrigin ) ;
savefile - > ReadInt ( toAreaNum ) ;
savefile - > ReadInt ( startTime ) ;
savefile - > ReadInt ( duration ) ;
savefile - > ReadFloat ( speed ) ;
savefile - > ReadFloat ( range ) ;
savefile - > ReadFloat ( wanderYaw ) ;
savefile - > ReadInt ( nextWanderTime ) ;
savefile - > ReadInt ( blockTime ) ;
obstacle . Restore ( savefile ) ;
savefile - > ReadVec3 ( lastMoveOrigin ) ;
savefile - > ReadInt ( lastMoveTime ) ;
savefile - > ReadInt ( anim ) ;
}
/*
= = = = = = = = = = = =
idAASFindCover : : idAASFindCover
= = = = = = = = = = = =
*/
idAASFindCover : : idAASFindCover ( const idVec3 & hideFromPos ) {
int numPVSAreas ;
idBounds bounds ( hideFromPos - idVec3 ( 16 , 16 , 0 ) , hideFromPos + idVec3 ( 16 , 16 , 64 ) ) ;
// setup PVS
numPVSAreas = gameLocal . pvs . GetPVSAreas ( bounds , PVSAreas , idEntity : : MAX_PVS_AREAS ) ;
hidePVS = gameLocal . pvs . SetupCurrentPVS ( PVSAreas , numPVSAreas ) ;
}
/*
= = = = = = = = = = = =
idAASFindCover : : ~ idAASFindCover
= = = = = = = = = = = =
*/
idAASFindCover : : ~ idAASFindCover ( ) {
gameLocal . pvs . FreeCurrentPVS ( hidePVS ) ;
}
/*
= = = = = = = = = = = =
idAASFindCover : : TestArea
= = = = = = = = = = = =
*/
bool idAASFindCover : : TestArea ( const idAAS * aas , int areaNum ) {
idVec3 areaCenter ;
int numPVSAreas ;
int PVSAreas [ idEntity : : MAX_PVS_AREAS ] ;
areaCenter = aas - > AreaCenter ( areaNum ) ;
areaCenter [ 2 ] + = 1.0f ;
numPVSAreas = gameLocal . pvs . GetPVSAreas ( idBounds ( areaCenter ) . Expand ( 16.0f ) , PVSAreas , idEntity : : MAX_PVS_AREAS ) ;
if ( ! gameLocal . pvs . InCurrentPVS ( hidePVS , PVSAreas , numPVSAreas ) ) {
return true ;
}
return false ;
}
/*
= = = = = = = = = = = =
idAASFindAreaOutOfRange : : idAASFindAreaOutOfRange
= = = = = = = = = = = =
*/
idAASFindAreaOutOfRange : : idAASFindAreaOutOfRange ( const idVec3 & targetPos , float maxDist ) {
this - > targetPos = targetPos ;
this - > maxDistSqr = maxDist * maxDist ;
}
/*
= = = = = = = = = = = =
idAASFindAreaOutOfRange : : TestArea
= = = = = = = = = = = =
*/
bool idAASFindAreaOutOfRange : : TestArea ( const idAAS * aas , int areaNum ) {
const idVec3 & areaCenter = aas - > AreaCenter ( areaNum ) ;
trace_t trace ;
float dist ;
dist = ( targetPos . ToVec2 ( ) - areaCenter . ToVec2 ( ) ) . LengthSqr ( ) ;
if ( ( maxDistSqr > 0.0f ) & & ( dist < maxDistSqr ) ) {
return false ;
}
gameLocal . clip . TracePoint ( trace , targetPos , areaCenter + idVec3 ( 0.0f , 0.0f , 1.0f ) , MASK_OPAQUE , NULL ) ;
if ( trace . fraction < 1.0f ) {
return false ;
}
return true ;
}
/*
= = = = = = = = = = = =
idAASFindAttackPosition : : idAASFindAttackPosition
= = = = = = = = = = = =
*/
idAASFindAttackPosition : : idAASFindAttackPosition ( const idAI * self , const idMat3 & gravityAxis , idEntity * target , const idVec3 & targetPos , const idVec3 & fireOffset ) {
int numPVSAreas ;
this - > target = target ;
this - > targetPos = targetPos ;
this - > fireOffset = fireOffset ;
this - > self = self ;
this - > gravityAxis = gravityAxis ;
excludeBounds = idBounds ( idVec3 ( - 64.0 , - 64.0f , - 8.0f ) , idVec3 ( 64.0 , 64.0f , 64.0f ) ) ;
2011-12-06 18:20:15 +00:00
excludeBounds . TranslateSelf ( self - > GetPhysics ( ) - > GetOrigin ( ) ) ;
2011-11-22 21:28:15 +00:00
// setup PVS
idBounds bounds ( targetPos - idVec3 ( 16 , 16 , 0 ) , targetPos + idVec3 ( 16 , 16 , 64 ) ) ;
numPVSAreas = gameLocal . pvs . GetPVSAreas ( bounds , PVSAreas , idEntity : : MAX_PVS_AREAS ) ;
targetPVS = gameLocal . pvs . SetupCurrentPVS ( PVSAreas , numPVSAreas ) ;
}
/*
= = = = = = = = = = = =
idAASFindAttackPosition : : ~ idAASFindAttackPosition
= = = = = = = = = = = =
*/
idAASFindAttackPosition : : ~ idAASFindAttackPosition ( ) {
gameLocal . pvs . FreeCurrentPVS ( targetPVS ) ;
}
/*
= = = = = = = = = = = =
idAASFindAttackPosition : : TestArea
= = = = = = = = = = = =
*/
bool idAASFindAttackPosition : : TestArea ( const idAAS * aas , int areaNum ) {
idVec3 dir ;
idVec3 local_dir ;
idVec3 fromPos ;
idMat3 axis ;
idVec3 areaCenter ;
int numPVSAreas ;
int PVSAreas [ idEntity : : MAX_PVS_AREAS ] ;
areaCenter = aas - > AreaCenter ( areaNum ) ;
areaCenter [ 2 ] + = 1.0f ;
if ( excludeBounds . ContainsPoint ( areaCenter ) ) {
// too close to where we already are
return false ;
}
numPVSAreas = gameLocal . pvs . GetPVSAreas ( idBounds ( areaCenter ) . Expand ( 16.0f ) , PVSAreas , idEntity : : MAX_PVS_AREAS ) ;
if ( ! gameLocal . pvs . InCurrentPVS ( targetPVS , PVSAreas , numPVSAreas ) ) {
return false ;
}
// calculate the world transform of the launch position
dir = targetPos - areaCenter ;
gravityAxis . ProjectVector ( dir , local_dir ) ;
local_dir . z = 0.0f ;
local_dir . ToVec2 ( ) . Normalize ( ) ;
axis = local_dir . ToMat3 ( ) ;
fromPos = areaCenter + fireOffset * axis ;
return self - > GetAimDir ( fromPos , target , self , dir ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : idAI
= = = = = = = = = = = = = = = = = = = = =
*/
idAI : : idAI ( ) {
aas = NULL ;
travelFlags = TFL_WALK | TFL_AIR ;
kickForce = 2048.0f ;
ignore_obstacles = false ;
blockedRadius = 0.0f ;
blockedMoveTime = 750 ;
blockedAttackTime = 750 ;
turnRate = 360.0f ;
turnVel = 0.0f ;
anim_turn_yaw = 0.0f ;
anim_turn_amount = 0.0f ;
anim_turn_angles = 0.0f ;
fly_offset = 0 ;
fly_seek_scale = 1.0f ;
fly_roll_scale = 0.0f ;
fly_roll_max = 0.0f ;
fly_roll = 0.0f ;
fly_pitch_scale = 0.0f ;
fly_pitch_max = 0.0f ;
fly_pitch = 0.0f ;
allowMove = false ;
allowHiddenMovement = false ;
fly_speed = 0.0f ;
fly_bob_strength = 0.0f ;
fly_bob_vert = 0.0f ;
fly_bob_horz = 0.0f ;
lastHitCheckResult = false ;
lastHitCheckTime = 0 ;
lastAttackTime = 0 ;
melee_range = 0.0f ;
projectile_height_to_distance_ratio = 1.0f ;
projectileDef = NULL ;
projectile = NULL ;
projectileClipModel = NULL ;
projectileRadius = 0.0f ;
projectileVelocity = vec3_origin ;
projectileGravity = vec3_origin ;
projectileSpeed = 0.0f ;
chat_snd = NULL ;
chat_min = 0 ;
chat_max = 0 ;
chat_time = 0 ;
talk_state = TALK_NEVER ;
talkTarget = NULL ;
particles . Clear ( ) ;
restartParticles = true ;
useBoneAxis = false ;
wakeOnFlashlight = false ;
memset ( & worldMuzzleFlash , 0 , sizeof ( worldMuzzleFlash ) ) ;
worldMuzzleFlashHandle = - 1 ;
enemy = NULL ;
lastVisibleEnemyPos . Zero ( ) ;
lastVisibleEnemyEyeOffset . Zero ( ) ;
lastVisibleReachableEnemyPos . Zero ( ) ;
lastReachableEnemyPos . Zero ( ) ;
shrivel_rate = 0.0f ;
shrivel_start = 0 ;
fl . neverDormant = false ; // AI's can go dormant
current_yaw = 0.0f ;
ideal_yaw = 0.0f ;
num_cinematics = 0 ;
current_cinematic = 0 ;
allowEyeFocus = true ;
allowPain = true ;
allowJointMod = true ;
focusEntity = NULL ;
focusTime = 0 ;
alignHeadTime = 0 ;
forceAlignHeadTime = 0 ;
currentFocusPos . Zero ( ) ;
eyeAng . Zero ( ) ;
lookAng . Zero ( ) ;
destLookAng . Zero ( ) ;
lookMin . Zero ( ) ;
lookMax . Zero ( ) ;
eyeMin . Zero ( ) ;
eyeMax . Zero ( ) ;
muzzleFlashEnd = 0 ;
flashTime = 0 ;
flashJointWorld = INVALID_JOINT ;
focusJoint = INVALID_JOINT ;
orientationJoint = INVALID_JOINT ;
flyTiltJoint = INVALID_JOINT ;
eyeVerticalOffset = 0.0f ;
eyeHorizontalOffset = 0.0f ;
eyeFocusRate = 0.0f ;
headFocusRate = 0.0f ;
focusAlignTime = 0 ;
2018-08-26 21:52:13 +00:00
//ivan start
isXlocked = false ;
fastXpos = 0.0f ;
updXlock = false ;
deltaXfromEnemy = 0.0f ;
//deltaYfromEnemy = 0.0f;
//deltaZfromEnemy = 0.0f;
fireMode = 0 ;
noPush = false ;
2021-12-19 04:51:02 +00:00
disableMoving = false ; //rev 2020 disable the actor from moving via a key in spawn arguments
2018-08-26 21:52:13 +00:00
/*
modelCallBackDone = false ;
renderEntity . callback = idAI : : ModelCallback ;
*/
//ivan end
2011-11-22 21:28:15 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : ~ idAI
= = = = = = = = = = = = = = = = = = = = =
*/
idAI : : ~ idAI ( ) {
delete projectileClipModel ;
DeconstructScriptObject ( ) ;
scriptObject . Free ( ) ;
if ( worldMuzzleFlashHandle ! = - 1 ) {
gameRenderWorld - > FreeLightDef ( worldMuzzleFlashHandle ) ;
worldMuzzleFlashHandle = - 1 ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Save
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Save ( idSaveGame * savefile ) const {
int i ;
savefile - > WriteInt ( travelFlags ) ;
move . Save ( savefile ) ;
savedMove . Save ( savefile ) ;
savefile - > WriteFloat ( kickForce ) ;
savefile - > WriteBool ( ignore_obstacles ) ;
savefile - > WriteFloat ( blockedRadius ) ;
savefile - > WriteInt ( blockedMoveTime ) ;
savefile - > WriteInt ( blockedAttackTime ) ;
savefile - > WriteFloat ( ideal_yaw ) ;
savefile - > WriteFloat ( current_yaw ) ;
savefile - > WriteFloat ( turnRate ) ;
savefile - > WriteFloat ( turnVel ) ;
savefile - > WriteFloat ( anim_turn_yaw ) ;
savefile - > WriteFloat ( anim_turn_amount ) ;
savefile - > WriteFloat ( anim_turn_angles ) ;
savefile - > WriteStaticObject ( physicsObj ) ;
savefile - > WriteFloat ( fly_speed ) ;
savefile - > WriteFloat ( fly_bob_strength ) ;
savefile - > WriteFloat ( fly_bob_vert ) ;
savefile - > WriteFloat ( fly_bob_horz ) ;
savefile - > WriteInt ( fly_offset ) ;
savefile - > WriteFloat ( fly_seek_scale ) ;
savefile - > WriteFloat ( fly_roll_scale ) ;
savefile - > WriteFloat ( fly_roll_max ) ;
savefile - > WriteFloat ( fly_roll ) ;
savefile - > WriteFloat ( fly_pitch_scale ) ;
savefile - > WriteFloat ( fly_pitch_max ) ;
savefile - > WriteFloat ( fly_pitch ) ;
savefile - > WriteBool ( allowMove ) ;
savefile - > WriteBool ( allowHiddenMovement ) ;
savefile - > WriteBool ( disableGravity ) ;
savefile - > WriteBool ( af_push_moveables ) ;
savefile - > WriteBool ( lastHitCheckResult ) ;
savefile - > WriteInt ( lastHitCheckTime ) ;
savefile - > WriteInt ( lastAttackTime ) ;
savefile - > WriteFloat ( melee_range ) ;
savefile - > WriteFloat ( projectile_height_to_distance_ratio ) ;
savefile - > WriteInt ( missileLaunchOffset . Num ( ) ) ;
for ( i = 0 ; i < missileLaunchOffset . Num ( ) ; i + + ) {
savefile - > WriteVec3 ( missileLaunchOffset [ i ] ) ;
}
idStr projectileName ;
spawnArgs . GetString ( " def_projectile " , " " , projectileName ) ;
savefile - > WriteString ( projectileName ) ;
savefile - > WriteFloat ( projectileRadius ) ;
savefile - > WriteFloat ( projectileSpeed ) ;
savefile - > WriteVec3 ( projectileVelocity ) ;
savefile - > WriteVec3 ( projectileGravity ) ;
projectile . Save ( savefile ) ;
savefile - > WriteString ( attack ) ;
savefile - > WriteSoundShader ( chat_snd ) ;
savefile - > WriteInt ( chat_min ) ;
savefile - > WriteInt ( chat_max ) ;
savefile - > WriteInt ( chat_time ) ;
savefile - > WriteInt ( talk_state ) ;
talkTarget . Save ( savefile ) ;
savefile - > WriteInt ( num_cinematics ) ;
savefile - > WriteInt ( current_cinematic ) ;
savefile - > WriteBool ( allowJointMod ) ;
focusEntity . Save ( savefile ) ;
savefile - > WriteVec3 ( currentFocusPos ) ;
savefile - > WriteInt ( focusTime ) ;
savefile - > WriteInt ( alignHeadTime ) ;
savefile - > WriteInt ( forceAlignHeadTime ) ;
savefile - > WriteAngles ( eyeAng ) ;
savefile - > WriteAngles ( lookAng ) ;
savefile - > WriteAngles ( destLookAng ) ;
savefile - > WriteAngles ( lookMin ) ;
savefile - > WriteAngles ( lookMax ) ;
savefile - > WriteInt ( lookJoints . Num ( ) ) ;
for ( i = 0 ; i < lookJoints . Num ( ) ; i + + ) {
savefile - > WriteJoint ( lookJoints [ i ] ) ;
savefile - > WriteAngles ( lookJointAngles [ i ] ) ;
}
savefile - > WriteFloat ( shrivel_rate ) ;
savefile - > WriteInt ( shrivel_start ) ;
savefile - > WriteInt ( particles . Num ( ) ) ;
for ( i = 0 ; i < particles . Num ( ) ; i + + ) {
savefile - > WriteParticle ( particles [ i ] . particle ) ;
savefile - > WriteInt ( particles [ i ] . time ) ;
savefile - > WriteJoint ( particles [ i ] . joint ) ;
}
savefile - > WriteBool ( restartParticles ) ;
savefile - > WriteBool ( useBoneAxis ) ;
enemy . Save ( savefile ) ;
savefile - > WriteVec3 ( lastVisibleEnemyPos ) ;
savefile - > WriteVec3 ( lastVisibleEnemyEyeOffset ) ;
savefile - > WriteVec3 ( lastVisibleReachableEnemyPos ) ;
savefile - > WriteVec3 ( lastReachableEnemyPos ) ;
savefile - > WriteBool ( wakeOnFlashlight ) ;
savefile - > WriteAngles ( eyeMin ) ;
savefile - > WriteAngles ( eyeMax ) ;
savefile - > WriteFloat ( eyeVerticalOffset ) ;
savefile - > WriteFloat ( eyeHorizontalOffset ) ;
savefile - > WriteFloat ( eyeFocusRate ) ;
savefile - > WriteFloat ( headFocusRate ) ;
savefile - > WriteInt ( focusAlignTime ) ;
savefile - > WriteJoint ( flashJointWorld ) ;
savefile - > WriteInt ( muzzleFlashEnd ) ;
savefile - > WriteJoint ( focusJoint ) ;
savefile - > WriteJoint ( orientationJoint ) ;
savefile - > WriteJoint ( flyTiltJoint ) ;
savefile - > WriteBool ( GetPhysics ( ) = = static_cast < const idPhysics * > ( & physicsObj ) ) ;
2018-08-26 21:52:13 +00:00
//ivan start
/*
//moved to idActor
savefile - > WriteBool ( isXlocked ) ;
savefile - > WriteBool ( updXlock ) ;
savefile - > WriteFloat ( lockedXpos ) ;
*/
savefile - > WriteFloat ( deltaXfromEnemy ) ;
//savefile->WriteFloat( deltaYfromEnemy );
//savefile->WriteFloat( deltaZfromEnemy );
savefile - > WriteInt ( fireMode ) ;
savefile - > WriteBool ( noPush ) ;
//ivan end
2011-11-22 21:28:15 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Restore
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Restore ( idRestoreGame * savefile ) {
bool restorePhysics ;
int i ;
int num ;
idBounds bounds ;
savefile - > ReadInt ( travelFlags ) ;
move . Restore ( savefile ) ;
savedMove . Restore ( savefile ) ;
savefile - > ReadFloat ( kickForce ) ;
savefile - > ReadBool ( ignore_obstacles ) ;
savefile - > ReadFloat ( blockedRadius ) ;
savefile - > ReadInt ( blockedMoveTime ) ;
savefile - > ReadInt ( blockedAttackTime ) ;
savefile - > ReadFloat ( ideal_yaw ) ;
savefile - > ReadFloat ( current_yaw ) ;
savefile - > ReadFloat ( turnRate ) ;
savefile - > ReadFloat ( turnVel ) ;
savefile - > ReadFloat ( anim_turn_yaw ) ;
savefile - > ReadFloat ( anim_turn_amount ) ;
savefile - > ReadFloat ( anim_turn_angles ) ;
savefile - > ReadStaticObject ( physicsObj ) ;
savefile - > ReadFloat ( fly_speed ) ;
savefile - > ReadFloat ( fly_bob_strength ) ;
savefile - > ReadFloat ( fly_bob_vert ) ;
savefile - > ReadFloat ( fly_bob_horz ) ;
savefile - > ReadInt ( fly_offset ) ;
savefile - > ReadFloat ( fly_seek_scale ) ;
savefile - > ReadFloat ( fly_roll_scale ) ;
savefile - > ReadFloat ( fly_roll_max ) ;
savefile - > ReadFloat ( fly_roll ) ;
savefile - > ReadFloat ( fly_pitch_scale ) ;
savefile - > ReadFloat ( fly_pitch_max ) ;
savefile - > ReadFloat ( fly_pitch ) ;
savefile - > ReadBool ( allowMove ) ;
savefile - > ReadBool ( allowHiddenMovement ) ;
savefile - > ReadBool ( disableGravity ) ;
savefile - > ReadBool ( af_push_moveables ) ;
savefile - > ReadBool ( lastHitCheckResult ) ;
savefile - > ReadInt ( lastHitCheckTime ) ;
savefile - > ReadInt ( lastAttackTime ) ;
savefile - > ReadFloat ( melee_range ) ;
savefile - > ReadFloat ( projectile_height_to_distance_ratio ) ;
savefile - > ReadInt ( num ) ;
missileLaunchOffset . SetGranularity ( 1 ) ;
missileLaunchOffset . SetNum ( num ) ;
for ( i = 0 ; i < num ; i + + ) {
savefile - > ReadVec3 ( missileLaunchOffset [ i ] ) ;
}
idStr projectileName ;
savefile - > ReadString ( projectileName ) ;
if ( projectileName . Length ( ) ) {
projectileDef = gameLocal . FindEntityDefDict ( projectileName ) ;
} else {
projectileDef = NULL ;
}
savefile - > ReadFloat ( projectileRadius ) ;
savefile - > ReadFloat ( projectileSpeed ) ;
savefile - > ReadVec3 ( projectileVelocity ) ;
savefile - > ReadVec3 ( projectileGravity ) ;
projectile . Restore ( savefile ) ;
savefile - > ReadString ( attack ) ;
savefile - > ReadSoundShader ( chat_snd ) ;
savefile - > ReadInt ( chat_min ) ;
savefile - > ReadInt ( chat_max ) ;
savefile - > ReadInt ( chat_time ) ;
savefile - > ReadInt ( i ) ;
talk_state = static_cast < talkState_t > ( i ) ;
talkTarget . Restore ( savefile ) ;
savefile - > ReadInt ( num_cinematics ) ;
savefile - > ReadInt ( current_cinematic ) ;
savefile - > ReadBool ( allowJointMod ) ;
focusEntity . Restore ( savefile ) ;
savefile - > ReadVec3 ( currentFocusPos ) ;
savefile - > ReadInt ( focusTime ) ;
savefile - > ReadInt ( alignHeadTime ) ;
savefile - > ReadInt ( forceAlignHeadTime ) ;
savefile - > ReadAngles ( eyeAng ) ;
savefile - > ReadAngles ( lookAng ) ;
savefile - > ReadAngles ( destLookAng ) ;
savefile - > ReadAngles ( lookMin ) ;
savefile - > ReadAngles ( lookMax ) ;
savefile - > ReadInt ( num ) ;
lookJoints . SetGranularity ( 1 ) ;
lookJoints . SetNum ( num ) ;
lookJointAngles . SetGranularity ( 1 ) ;
lookJointAngles . SetNum ( num ) ;
for ( i = 0 ; i < num ; i + + ) {
savefile - > ReadJoint ( lookJoints [ i ] ) ;
savefile - > ReadAngles ( lookJointAngles [ i ] ) ;
}
savefile - > ReadFloat ( shrivel_rate ) ;
savefile - > ReadInt ( shrivel_start ) ;
savefile - > ReadInt ( num ) ;
particles . SetNum ( num ) ;
for ( i = 0 ; i < particles . Num ( ) ; i + + ) {
savefile - > ReadParticle ( particles [ i ] . particle ) ;
savefile - > ReadInt ( particles [ i ] . time ) ;
savefile - > ReadJoint ( particles [ i ] . joint ) ;
}
savefile - > ReadBool ( restartParticles ) ;
savefile - > ReadBool ( useBoneAxis ) ;
enemy . Restore ( savefile ) ;
savefile - > ReadVec3 ( lastVisibleEnemyPos ) ;
savefile - > ReadVec3 ( lastVisibleEnemyEyeOffset ) ;
savefile - > ReadVec3 ( lastVisibleReachableEnemyPos ) ;
savefile - > ReadVec3 ( lastReachableEnemyPos ) ;
savefile - > ReadBool ( wakeOnFlashlight ) ;
savefile - > ReadAngles ( eyeMin ) ;
savefile - > ReadAngles ( eyeMax ) ;
savefile - > ReadFloat ( eyeVerticalOffset ) ;
savefile - > ReadFloat ( eyeHorizontalOffset ) ;
savefile - > ReadFloat ( eyeFocusRate ) ;
savefile - > ReadFloat ( headFocusRate ) ;
savefile - > ReadInt ( focusAlignTime ) ;
savefile - > ReadJoint ( flashJointWorld ) ;
savefile - > ReadInt ( muzzleFlashEnd ) ;
savefile - > ReadJoint ( focusJoint ) ;
savefile - > ReadJoint ( orientationJoint ) ;
savefile - > ReadJoint ( flyTiltJoint ) ;
savefile - > ReadBool ( restorePhysics ) ;
2018-08-26 21:52:13 +00:00
//ivan start
/*
//moved to idActor
savefile - > ReadBool ( isXlocked ) ;
savefile - > ReadBool ( updXlock ) ;
savefile - > ReadFloat ( lockedXpos ) ;
*/
savefile - > ReadFloat ( deltaXfromEnemy ) ;
//savefile->ReadFloat( deltaYfromEnemy );
//savefile->ReadFloat( deltaZfromEnemy );
savefile - > ReadInt ( fireMode ) ;
savefile - > ReadBool ( noPush ) ;
//ivan end
2011-11-22 21:28:15 +00:00
// Set the AAS if the character has the correct gravity vector
idVec3 gravity = spawnArgs . GetVector ( " gravityDir " , " 0 0 -1 " ) ;
gravity * = g_gravity . GetFloat ( ) ;
if ( gravity = = gameLocal . GetGravity ( ) ) {
SetAAS ( ) ;
}
SetCombatModel ( ) ;
LinkCombat ( ) ;
InitMuzzleFlash ( ) ;
// Link the script variables back to the scriptobject
LinkScriptVariables ( ) ;
if ( restorePhysics ) {
RestorePhysics ( & physicsObj ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Spawn
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Spawn ( void ) {
const char * jointname ;
const idKeyValue * kv ;
idStr jointName ;
idAngles jointScale ;
jointHandle_t joint ;
idVec3 local_dir ;
bool talks ;
2018-08-26 21:52:13 +00:00
int noMove ; //ivan
2011-11-22 21:28:15 +00:00
if ( ! g_monsters . GetBool ( ) ) {
PostEventMS ( & EV_Remove , 0 ) ;
return ;
}
spawnArgs . GetInt ( " team " , " 1 " , team ) ;
spawnArgs . GetInt ( " rank " , " 0 " , rank ) ;
spawnArgs . GetInt ( " fly_offset " , " 0 " , fly_offset ) ;
spawnArgs . GetFloat ( " fly_speed " , " 100 " , fly_speed ) ;
spawnArgs . GetFloat ( " fly_bob_strength " , " 50 " , fly_bob_strength ) ;
spawnArgs . GetFloat ( " fly_bob_vert " , " 2 " , fly_bob_horz ) ;
spawnArgs . GetFloat ( " fly_bob_horz " , " 2.7 " , fly_bob_vert ) ;
spawnArgs . GetFloat ( " fly_seek_scale " , " 4 " , fly_seek_scale ) ;
spawnArgs . GetFloat ( " fly_roll_scale " , " 90 " , fly_roll_scale ) ;
spawnArgs . GetFloat ( " fly_roll_max " , " 60 " , fly_roll_max ) ;
spawnArgs . GetFloat ( " fly_pitch_scale " , " 45 " , fly_pitch_scale ) ;
spawnArgs . GetFloat ( " fly_pitch_max " , " 30 " , fly_pitch_max ) ;
spawnArgs . GetFloat ( " melee_range " , " 64 " , melee_range ) ;
spawnArgs . GetFloat ( " projectile_height_to_distance_ratio " , " 1 " , projectile_height_to_distance_ratio ) ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
spawnArgs . GetFloat ( " turn_rate " , " 360 " , turnRate ) ;
spawnArgs . GetBool ( " talks " , " 0 " , talks ) ;
if ( spawnArgs . GetString ( " npc_name " , NULL ) ! = NULL ) {
if ( talks ) {
talk_state = TALK_OK ;
} else {
talk_state = TALK_BUSY ;
}
} else {
talk_state = TALK_NEVER ;
}
spawnArgs . GetBool ( " animate_z " , " 0 " , disableGravity ) ;
spawnArgs . GetBool ( " af_push_moveables " , " 0 " , af_push_moveables ) ;
spawnArgs . GetFloat ( " kick_force " , " 4096 " , kickForce ) ;
2018-08-26 21:52:13 +00:00
spawnArgs . GetBool ( " ignore_obstacles " , " 1 " , ignore_obstacles ) ; //ivan: set default to 1
2011-11-22 21:28:15 +00:00
spawnArgs . GetFloat ( " blockedRadius " , " -1 " , blockedRadius ) ;
spawnArgs . GetInt ( " blockedMoveTime " , " 750 " , blockedMoveTime ) ;
2021-12-19 04:51:02 +00:00
spawnArgs . GetInt ( " blockedAttackTime " , " 450 " , blockedAttackTime ) ;
2011-11-22 21:28:15 +00:00
spawnArgs . GetInt ( " num_cinematics " , " 0 " , num_cinematics ) ;
current_cinematic = 0 ;
LinkScriptVariables ( ) ;
fl . takedamage = ! spawnArgs . GetBool ( " noDamage " ) ;
enemy = NULL ;
allowMove = true ;
allowHiddenMovement = false ;
animator . RemoveOriginOffset ( true ) ;
2021-12-19 04:51:02 +00:00
disableMoving = spawnArgs . GetBool ( " disable_moving " ) ; //rev 2020 used to disable the enemy from moving
2011-11-22 21:28:15 +00:00
// create combat collision hull for exact collision detection
SetCombatModel ( ) ;
lookMin = spawnArgs . GetAngles ( " look_min " , " -80 -75 0 " ) ;
lookMax = spawnArgs . GetAngles ( " look_max " , " 80 75 0 " ) ;
lookJoints . SetGranularity ( 1 ) ;
lookJointAngles . SetGranularity ( 1 ) ;
kv = spawnArgs . MatchPrefix ( " look_joint " , NULL ) ;
while ( kv ) {
jointName = kv - > GetKey ( ) ;
jointName . StripLeadingOnce ( " look_joint " ) ;
joint = animator . GetJointHandle ( jointName ) ;
if ( joint = = INVALID_JOINT ) {
gameLocal . Warning ( " Unknown look_joint '%s' on entity %s " , jointName . c_str ( ) , name . c_str ( ) ) ;
} else {
jointScale = spawnArgs . GetAngles ( kv - > GetKey ( ) , " 0 0 0 " ) ;
jointScale . roll = 0.0f ;
// if no scale on any component, then don't bother adding it. this may be done to
// zero out rotation from an inherited entitydef.
if ( jointScale ! = ang_zero ) {
lookJoints . Append ( joint ) ;
lookJointAngles . Append ( jointScale ) ;
}
}
kv = spawnArgs . MatchPrefix ( " look_joint " , kv ) ;
}
// calculate joint positions on attack frames so we can do proper "can hit" tests
CalculateAttackOffsets ( ) ;
eyeMin = spawnArgs . GetAngles ( " eye_turn_min " , " -10 -30 0 " ) ;
eyeMax = spawnArgs . GetAngles ( " eye_turn_max " , " 10 30 0 " ) ;
eyeVerticalOffset = spawnArgs . GetFloat ( " eye_verticle_offset " , " 5 " ) ;
eyeHorizontalOffset = spawnArgs . GetFloat ( " eye_horizontal_offset " , " -8 " ) ;
eyeFocusRate = spawnArgs . GetFloat ( " eye_focus_rate " , " 0.5 " ) ;
headFocusRate = spawnArgs . GetFloat ( " head_focus_rate " , " 0.1 " ) ;
focusAlignTime = SEC2MS ( spawnArgs . GetFloat ( " focus_align_time " , " 1 " ) ) ;
flashJointWorld = animator . GetJointHandle ( " flash " ) ;
if ( head . GetEntity ( ) ) {
idAnimator * headAnimator = head . GetEntity ( ) - > GetAnimator ( ) ;
jointname = spawnArgs . GetString ( " bone_focus " ) ;
if ( * jointname ) {
focusJoint = headAnimator - > GetJointHandle ( jointname ) ;
if ( focusJoint = = INVALID_JOINT ) {
gameLocal . Warning ( " Joint '%s' not found on head on '%s' " , jointname , name . c_str ( ) ) ;
}
}
} else {
jointname = spawnArgs . GetString ( " bone_focus " ) ;
if ( * jointname ) {
focusJoint = animator . GetJointHandle ( jointname ) ;
if ( focusJoint = = INVALID_JOINT ) {
gameLocal . Warning ( " Joint '%s' not found on '%s' " , jointname , name . c_str ( ) ) ;
}
}
}
jointname = spawnArgs . GetString ( " bone_orientation " ) ;
if ( * jointname ) {
orientationJoint = animator . GetJointHandle ( jointname ) ;
if ( orientationJoint = = INVALID_JOINT ) {
gameLocal . Warning ( " Joint '%s' not found on '%s' " , jointname , name . c_str ( ) ) ;
}
}
jointname = spawnArgs . GetString ( " bone_flytilt " ) ;
if ( * jointname ) {
flyTiltJoint = animator . GetJointHandle ( jointname ) ;
if ( flyTiltJoint = = INVALID_JOINT ) {
gameLocal . Warning ( " Joint '%s' not found on '%s' " , jointname , name . c_str ( ) ) ;
}
}
InitMuzzleFlash ( ) ;
physicsObj . SetSelf ( this ) ;
physicsObj . SetClipModel ( new idClipModel ( GetPhysics ( ) - > GetClipModel ( ) ) , 1.0f ) ;
physicsObj . SetMass ( spawnArgs . GetFloat ( " mass " , " 100 " ) ) ;
2021-12-19 04:51:02 +00:00
if ( spawnArgs . GetBool ( " big_monster " ) ) {
2011-11-22 21:28:15 +00:00
physicsObj . SetContents ( 0 ) ;
physicsObj . SetClipMask ( MASK_MONSTERSOLID & ~ CONTENTS_BODY ) ;
} else {
if ( use_combat_bbox ) {
physicsObj . SetContents ( CONTENTS_BODY | CONTENTS_SOLID ) ;
} else {
2021-12-19 04:51:02 +00:00
//Rev 2020 allow enemies to pass through each other if team_non_solid is true
//physicsObj.SetContents( CONTENTS_BODY );
if ( ( spawnArgs . GetInt ( " team " , " 1 " ) ) & & spawnArgs . GetBool ( " team_non_solid " , " 1 " ) ) {
physicsObj . SetContents ( CONTENTS_CORPSE ) ; //the monster can pass through other monsters but player still detects touch of death
//gameLocal.Printf( "corpse" );
} else {
physicsObj . SetContents ( CONTENTS_BODY ) ;
//gameLocal.Printf( "body" );
}
//Rev 2020 End
2011-11-22 21:28:15 +00:00
}
physicsObj . SetClipMask ( MASK_MONSTERSOLID ) ;
}
// move up to make sure the monster is at least an epsilon above the floor
physicsObj . SetOrigin ( GetPhysics ( ) - > GetOrigin ( ) + idVec3 ( 0 , 0 , CM_CLIP_EPSILON ) ) ;
2011-12-06 18:20:15 +00:00
2018-08-26 21:52:13 +00:00
//lockedXpos = GetPhysics()->GetOrigin().x; //ivan - remember initial pos.
2011-11-22 21:28:15 +00:00
if ( num_cinematics ) {
physicsObj . SetGravity ( vec3_origin ) ;
} else {
idVec3 gravity = spawnArgs . GetVector ( " gravityDir " , " 0 0 -1 " ) ;
gravity * = g_gravity . GetFloat ( ) ;
physicsObj . SetGravity ( gravity ) ;
}
SetPhysics ( & physicsObj ) ;
physicsObj . GetGravityAxis ( ) . ProjectVector ( viewAxis [ 0 ] , local_dir ) ;
current_yaw = local_dir . ToYaw ( ) ;
ideal_yaw = idMath : : AngleNormalize180 ( current_yaw ) ;
move . blockTime = 0 ;
SetAAS ( ) ;
projectile = NULL ;
projectileDef = NULL ;
projectileClipModel = NULL ;
idStr projectileName ;
if ( spawnArgs . GetString ( " def_projectile " , " " , projectileName ) & & projectileName . Length ( ) ) {
projectileDef = gameLocal . FindEntityDefDict ( projectileName ) ;
CreateProjectile ( vec3_origin , viewAxis [ 0 ] ) ;
projectileRadius = projectile . GetEntity ( ) - > GetPhysics ( ) - > GetClipModel ( ) - > GetBounds ( ) . GetRadius ( ) ;
projectileVelocity = idProjectile : : GetVelocity ( projectileDef ) ;
projectileGravity = idProjectile : : GetGravity ( projectileDef ) ;
projectileSpeed = projectileVelocity . Length ( ) ;
delete projectile . GetEntity ( ) ;
projectile = NULL ;
}
particles . Clear ( ) ;
restartParticles = true ;
useBoneAxis = spawnArgs . GetBool ( " useBoneAxis " ) ;
SpawnParticles ( " smokeParticleSystem " ) ;
if ( num_cinematics | | spawnArgs . GetBool ( " hide " ) | | spawnArgs . GetBool ( " teleport " ) | | spawnArgs . GetBool ( " trigger_anim " ) ) {
fl . takedamage = false ;
physicsObj . SetContents ( 0 ) ;
physicsObj . GetClipModel ( ) - > Unlink ( ) ;
Hide ( ) ;
} else {
// play a looping ambient sound if we have one
StartSound ( " snd_ambient " , SND_CHANNEL_AMBIENT , 0 , false , NULL ) ;
}
if ( health < = 0 ) {
gameLocal . Warning ( " entity '%s' doesn't have health set " , name . c_str ( ) ) ;
health = 1 ;
}
// set up monster chatter
SetChatSound ( ) ;
BecomeActive ( TH_THINK ) ;
if ( af_push_moveables ) {
af . SetupPose ( this , gameLocal . time ) ;
af . GetPhysics ( ) - > EnableClip ( ) ;
}
// init the move variables
StopMove ( MOVE_STATUS_DONE ) ;
2018-08-26 21:52:13 +00:00
//ivan start
//if ( gameLocal.GameState() == GAMESTATE_STARTUP ) { //count them only at map start
if ( ! spawnArgs . GetBool ( " noStats " , " 0 " ) & & ( spawnArgs . GetInt ( " team " , " 1 " ) ! = 0 ) & & ! spawnArgs . GetBool ( " spawner " , " 0 " ) ) {
gameLocal . enemies_spawned_counter + + ;
//gameLocal.Printf( "Enemies counter increased: %d ( %s )\n", gameLocal.enemies_spawned_counter, name.c_str() );
}
//isXlocked = spawnArgs.GetBool( "isXlocked", "1" ); //ivan - moved to idActor
//updXlock = spawnArgs.GetBool( "updXlock", "1" ); //ivan - moved to idActor
fireMode = spawnArgs . GetInt ( " fireMode " , " 0 " ) ;
spawnArgs . GetInt ( " noMove " , " 0 " , noMove ) ;
if ( noMove > 0 ) {
AI_INHIBIT_MOVE = true ;
if ( noMove > 1 ) {
noPush = true ;
}
}
//ivan end
2011-11-22 21:28:15 +00:00
}
/*
= = = = = = = = = = = = = = = = = = =
idAI : : InitMuzzleFlash
= = = = = = = = = = = = = = = = = = =
*/
void idAI : : InitMuzzleFlash ( void ) {
const char * shader ;
idVec3 flashColor ;
spawnArgs . GetString ( " mtr_flashShader " , " muzzleflash " , & shader ) ;
spawnArgs . GetVector ( " flashColor " , " 0 0 0 " , flashColor ) ;
2011-12-06 18:20:15 +00:00
float flashRadius = spawnArgs . GetFloat ( " flashRadius " ) ;
2011-11-22 21:28:15 +00:00
flashTime = SEC2MS ( spawnArgs . GetFloat ( " flashTime " , " 0.25 " ) ) ;
memset ( & worldMuzzleFlash , 0 , sizeof ( worldMuzzleFlash ) ) ;
worldMuzzleFlash . pointLight = true ;
worldMuzzleFlash . shader = declManager - > FindMaterial ( shader , false ) ;
worldMuzzleFlash . shaderParms [ SHADERPARM_RED ] = flashColor [ 0 ] ;
worldMuzzleFlash . shaderParms [ SHADERPARM_GREEN ] = flashColor [ 1 ] ;
worldMuzzleFlash . shaderParms [ SHADERPARM_BLUE ] = flashColor [ 2 ] ;
worldMuzzleFlash . shaderParms [ SHADERPARM_ALPHA ] = 1.0f ;
worldMuzzleFlash . shaderParms [ SHADERPARM_TIMESCALE ] = 1.0f ;
worldMuzzleFlash . lightRadius [ 0 ] = flashRadius ;
worldMuzzleFlash . lightRadius [ 1 ] = flashRadius ;
worldMuzzleFlash . lightRadius [ 2 ] = flashRadius ;
worldMuzzleFlashHandle = - 1 ;
}
/*
= = = = = = = = = = = = = = = = = = =
idAI : : List_f
= = = = = = = = = = = = = = = = = = =
*/
void idAI : : List_f ( const idCmdArgs & args ) {
int e ;
idAI * check ;
int count ;
const char * statename ;
count = 0 ;
gameLocal . Printf ( " %-4s %-20s %s \n " , " Num " , " EntityDef " , " Name " ) ;
gameLocal . Printf ( " ------------------------------------------------ \n " ) ;
for ( e = 0 ; e < MAX_GENTITIES ; e + + ) {
check = static_cast < idAI * > ( gameLocal . entities [ e ] ) ;
if ( ! check | | ! check - > IsType ( idAI : : Type ) ) {
continue ;
}
if ( check - > state ) {
statename = check - > state - > Name ( ) ;
} else {
statename = " NULL state " ;
}
gameLocal . Printf ( " %4i: %-20s %-20s %s move: %d \n " , e , check - > GetEntityDefName ( ) , check - > name . c_str ( ) , statename , check - > allowMove ) ;
count + + ;
}
gameLocal . Printf ( " ...%d monsters \n " , count ) ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : DormantBegin
called when entity becomes dormant
= = = = = = = = = = = = = = = =
*/
void idAI : : DormantBegin ( void ) {
// since dormant happens on a timer, we wont get to update particles to
// hidden through the think loop, but we need to hide them though.
if ( particles . Num ( ) ) {
for ( int i = 0 ; i < particles . Num ( ) ; i + + ) {
particles [ i ] . time = 0 ;
}
}
if ( enemyNode . InList ( ) ) {
// remove ourselves from the enemy's enemylist
enemyNode . Remove ( ) ;
}
idActor : : DormantBegin ( ) ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : DormantEnd
called when entity wakes from being dormant
= = = = = = = = = = = = = = = =
*/
void idAI : : DormantEnd ( void ) {
if ( enemy . GetEntity ( ) & & ! enemyNode . InList ( ) ) {
// let our enemy know we're back on the trail
enemyNode . AddToEnd ( enemy . GetEntity ( ) - > enemyList ) ;
}
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( particles . Num ( ) ) {
for ( int i = 0 ; i < particles . Num ( ) ; i + + ) {
particles [ i ] . time = gameLocal . time ;
}
}
idActor : : DormantEnd ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Think
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Think ( void ) {
// if we are completely closed off from the player, don't do anything at all
if ( CheckDormant ( ) ) {
2018-08-26 21:52:13 +00:00
//isOnScreen = false; //ivan - fix needed?
2011-11-22 21:28:15 +00:00
return ;
}
if ( thinkFlags & TH_THINK ) {
// clear out the enemy when he dies or is hidden
idActor * enemyEnt = enemy . GetEntity ( ) ;
2018-08-26 21:52:13 +00:00
if ( enemyEnt ) {
2011-11-22 21:28:15 +00:00
if ( enemyEnt - > health < = 0 ) {
EnemyDead ( ) ;
}
}
current_yaw + = deltaViewAngles . yaw ;
ideal_yaw = idMath : : AngleNormalize180 ( ideal_yaw + deltaViewAngles . yaw ) ;
deltaViewAngles . Zero ( ) ;
viewAxis = idAngles ( 0 , current_yaw , 0 ) . ToMat3 ( ) ;
if ( num_cinematics ) {
if ( ! IsHidden ( ) & & torsoAnim . AnimDone ( 0 ) ) {
PlayCinematic ( ) ;
}
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
} else if ( ! allowHiddenMovement & & IsHidden ( ) ) {
// hidden monsters
UpdateAIScript ( ) ;
} else {
// clear the ik before we do anything else so the skeleton doesn't get updated twice
walkIK . ClearJointMods ( ) ;
switch ( move . moveType ) {
case MOVETYPE_DEAD :
// dead monsters
UpdateAIScript ( ) ;
DeadMove ( ) ;
break ;
case MOVETYPE_FLY :
// flying monsters
UpdateEnemyPosition ( ) ;
UpdateAIScript ( ) ;
FlyMove ( ) ;
PlayChatter ( ) ;
CheckBlink ( ) ;
break ;
case MOVETYPE_STATIC :
// static monsters
UpdateEnemyPosition ( ) ;
UpdateAIScript ( ) ;
StaticMove ( ) ;
PlayChatter ( ) ;
CheckBlink ( ) ;
break ;
case MOVETYPE_ANIM :
// animation based movement
UpdateEnemyPosition ( ) ;
UpdateAIScript ( ) ;
AnimMove ( ) ;
PlayChatter ( ) ;
CheckBlink ( ) ;
break ;
case MOVETYPE_SLIDE :
// velocity based movement
UpdateEnemyPosition ( ) ;
UpdateAIScript ( ) ;
SlideMove ( ) ;
PlayChatter ( ) ;
CheckBlink ( ) ;
break ;
}
}
// clear pain flag so that we recieve any damage between now and the next time we run the script
AI_PAIN = false ;
AI_SPECIAL_DAMAGE = 0 ;
AI_PUSHED = false ;
} else if ( thinkFlags & TH_PHYSICS ) {
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
}
if ( af_push_moveables ) {
PushWithAF ( ) ;
}
if ( fl . hidden & & allowHiddenMovement ) {
// UpdateAnimation won't call frame commands when hidden, so call them here when we allow hidden movement
animator . ServiceAnims ( gameLocal . previousTime , gameLocal . time ) ;
}
/* this still draws in retail builds.. not sure why.. don't care at this point.
if ( ! aas & & developer . GetBool ( ) & & ! fl . hidden & & ! num_cinematics ) {
gameRenderWorld - > DrawText ( " No AAS " , physicsObj . GetAbsBounds ( ) . GetCenter ( ) , 0.1f , colorWhite , gameLocal . GetLocalPlayer ( ) - > viewAngles . ToMat3 ( ) , 1 , gameLocal . msec ) ;
}
*/
2018-08-26 21:52:13 +00:00
//ivan start
//upd is on screen
UpdateIsOnScreen ( ) ; //NOTE: my "isOnScreen" value is useful also to other entities.
//ivan end
2021-12-19 04:51:02 +00:00
//rev 2020 hurt the player when overlapping enemies. Updated: make sure the enemy isn't hidden or else the box will still spawn.
if ( ! IsHidden ( ) & & spawnArgs . GetInt ( " touchofdeath " , " 1 " ) ) {
gameLocal . HurtBox ( this ) ;
}
2011-11-22 21:28:15 +00:00
UpdateMuzzleFlash ( ) ;
UpdateAnimation ( ) ;
UpdateParticles ( ) ;
Present ( ) ;
UpdateDamageEffects ( ) ;
LinkCombat ( ) ;
2018-08-26 21:52:13 +00:00
//ivan start
//modelCallBackNotDone = true; //reset to true every frame. Next time ::think will be called it will be already updated.
//ivan end
2011-11-22 21:28:15 +00:00
}
/***********************************************************************
AI script state management
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : LinkScriptVariables
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : LinkScriptVariables ( void ) {
AI_TALK . LinkTo ( scriptObject , " AI_TALK " ) ;
AI_DAMAGE . LinkTo ( scriptObject , " AI_DAMAGE " ) ;
AI_PAIN . LinkTo ( scriptObject , " AI_PAIN " ) ;
AI_SPECIAL_DAMAGE . LinkTo ( scriptObject , " AI_SPECIAL_DAMAGE " ) ;
AI_DEAD . LinkTo ( scriptObject , " AI_DEAD " ) ;
AI_ENEMY_VISIBLE . LinkTo ( scriptObject , " AI_ENEMY_VISIBLE " ) ;
AI_ENEMY_IN_FOV . LinkTo ( scriptObject , " AI_ENEMY_IN_FOV " ) ;
AI_ENEMY_DEAD . LinkTo ( scriptObject , " AI_ENEMY_DEAD " ) ;
AI_MOVE_DONE . LinkTo ( scriptObject , " AI_MOVE_DONE " ) ;
AI_ONGROUND . LinkTo ( scriptObject , " AI_ONGROUND " ) ;
AI_ACTIVATED . LinkTo ( scriptObject , " AI_ACTIVATED " ) ;
AI_FORWARD . LinkTo ( scriptObject , " AI_FORWARD " ) ;
AI_JUMP . LinkTo ( scriptObject , " AI_JUMP " ) ;
AI_BLOCKED . LinkTo ( scriptObject , " AI_BLOCKED " ) ;
AI_DEST_UNREACHABLE . LinkTo ( scriptObject , " AI_DEST_UNREACHABLE " ) ;
AI_HIT_ENEMY . LinkTo ( scriptObject , " AI_HIT_ENEMY " ) ;
AI_OBSTACLE_IN_PATH . LinkTo ( scriptObject , " AI_OBSTACLE_IN_PATH " ) ;
AI_PUSHED . LinkTo ( scriptObject , " AI_PUSHED " ) ;
2018-08-26 21:52:13 +00:00
//ivan start
AI_INHIBIT_ATTACK . LinkTo ( scriptObject , " AI_INHIBIT_ATTACK " ) ;
AI_INHIBIT_MOVE . LinkTo ( scriptObject , " AI_INHIBIT_MOVE " ) ;
//ivan end
2011-11-22 21:28:15 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : UpdateAIScript
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : UpdateAIScript ( void ) {
UpdateScript ( ) ;
// clear the hit enemy flag so we catch the next time we hit someone
AI_HIT_ENEMY = false ;
if ( allowHiddenMovement | | ! IsHidden ( ) ) {
// update the animstate if we're not hidden
UpdateAnimState ( ) ;
}
}
/***********************************************************************
navigation
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = =
idAI : : KickObstacles
= = = = = = = = = = = =
*/
void idAI : : KickObstacles ( const idVec3 & dir , float force , idEntity * alwaysKick ) {
int i , numListedClipModels ;
idBounds clipBounds ;
idEntity * obEnt ;
idClipModel * clipModel ;
idClipModel * clipModelList [ MAX_GENTITIES ] ;
int clipmask ;
idVec3 org ;
idVec3 forceVec ;
idVec3 delta ;
idVec2 perpendicular ;
org = physicsObj . GetOrigin ( ) ;
// find all possible obstacles
clipBounds = physicsObj . GetAbsBounds ( ) ;
clipBounds . TranslateSelf ( dir * 32.0f ) ;
clipBounds . ExpandSelf ( 8.0f ) ;
clipBounds . AddPoint ( org ) ;
clipmask = physicsObj . GetClipMask ( ) ;
numListedClipModels = gameLocal . clip . ClipModelsTouchingBounds ( clipBounds , clipmask , clipModelList , MAX_GENTITIES ) ;
for ( i = 0 ; i < numListedClipModels ; i + + ) {
clipModel = clipModelList [ i ] ;
obEnt = clipModel - > GetEntity ( ) ;
if ( obEnt = = alwaysKick ) {
// we'll kick this one outside the loop
continue ;
}
if ( ! clipModel - > IsTraceModel ( ) ) {
continue ;
}
if ( obEnt - > IsType ( idMoveable : : Type ) & & obEnt - > GetPhysics ( ) - > IsPushable ( ) ) {
delta = obEnt - > GetPhysics ( ) - > GetOrigin ( ) - org ;
delta . NormalizeFast ( ) ;
perpendicular . x = - delta . y ;
perpendicular . y = delta . x ;
delta . z + = 0.5f ;
delta . ToVec2 ( ) + = perpendicular * gameLocal . random . CRandomFloat ( ) * 0.5f ;
forceVec = delta * force * obEnt - > GetPhysics ( ) - > GetMass ( ) ;
obEnt - > ApplyImpulse ( this , 0 , obEnt - > GetPhysics ( ) - > GetOrigin ( ) , forceVec ) ;
}
}
if ( alwaysKick ) {
delta = alwaysKick - > GetPhysics ( ) - > GetOrigin ( ) - org ;
delta . NormalizeFast ( ) ;
perpendicular . x = - delta . y ;
perpendicular . y = delta . x ;
delta . z + = 0.5f ;
delta . ToVec2 ( ) + = perpendicular * gameLocal . random . CRandomFloat ( ) * 0.5f ;
forceVec = delta * force * alwaysKick - > GetPhysics ( ) - > GetMass ( ) ;
alwaysKick - > ApplyImpulse ( this , 0 , alwaysKick - > GetPhysics ( ) - > GetOrigin ( ) , forceVec ) ;
}
}
/*
= = = = = = = = = = = =
ValidForBounds
= = = = = = = = = = = =
*/
bool ValidForBounds ( const idAASSettings * settings , const idBounds & bounds ) {
int i ;
for ( i = 0 ; i < 3 ; i + + ) {
if ( bounds [ 0 ] [ i ] < settings - > boundingBoxes [ 0 ] [ 0 ] [ i ] ) {
return false ;
}
if ( bounds [ 1 ] [ i ] > settings - > boundingBoxes [ 0 ] [ 1 ] [ i ] ) {
return false ;
}
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SetAAS
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : SetAAS ( void ) {
idStr use_aas ;
spawnArgs . GetString ( " use_aas " , NULL , use_aas ) ;
aas = gameLocal . GetAAS ( use_aas ) ;
if ( aas ) {
const idAASSettings * settings = aas - > GetSettings ( ) ;
if ( settings ) {
if ( ! ValidForBounds ( settings , physicsObj . GetBounds ( ) ) ) {
gameLocal . Error ( " %s cannot use use_aas %s \n " , name . c_str ( ) , use_aas . c_str ( ) ) ;
}
float height = settings - > maxStepHeight ;
physicsObj . SetMaxStepHeight ( height ) ;
return ;
} else {
aas = NULL ;
}
}
gameLocal . Printf ( " WARNING: %s has no AAS file \n " , name . c_str ( ) ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : DrawRoute
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : DrawRoute ( void ) const {
if ( aas & & move . toAreaNum & & move . moveCommand ! = MOVE_NONE & & move . moveCommand ! = MOVE_WANDER & & move . moveCommand ! = MOVE_FACE_ENEMY & & move . moveCommand ! = MOVE_FACE_ENTITY & & move . moveCommand ! = MOVE_TO_POSITION_DIRECT ) {
if ( move . moveType = = MOVETYPE_FLY ) {
aas - > ShowFlyPath ( physicsObj . GetOrigin ( ) , move . toAreaNum , move . moveDest ) ;
} else {
aas - > ShowWalkPath ( physicsObj . GetOrigin ( ) , move . toAreaNum , move . moveDest ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : ReachedPos
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : ReachedPos ( const idVec3 & pos , const moveCommand_t moveCommand ) const {
if ( move . moveType = = MOVETYPE_SLIDE ) {
idBounds bnds ( idVec3 ( - 4 , - 4.0f , - 8.0f ) , idVec3 ( 4.0f , 4.0f , 64.0f ) ) ;
2011-12-06 18:20:15 +00:00
bnds . TranslateSelf ( physicsObj . GetOrigin ( ) ) ;
2011-11-22 21:28:15 +00:00
if ( bnds . ContainsPoint ( pos ) ) {
return true ;
}
} else {
if ( ( moveCommand = = MOVE_TO_ENEMY ) | | ( moveCommand = = MOVE_TO_ENTITY ) ) {
if ( physicsObj . GetAbsBounds ( ) . IntersectsBounds ( idBounds ( pos ) . Expand ( 8.0f ) ) ) {
return true ;
}
} else {
idBounds bnds ( idVec3 ( - 16.0 , - 16.0f , - 8.0f ) , idVec3 ( 16.0 , 16.0f , 64.0f ) ) ;
2011-12-06 18:20:15 +00:00
bnds . TranslateSelf ( physicsObj . GetOrigin ( ) ) ;
2011-11-22 21:28:15 +00:00
if ( bnds . ContainsPoint ( pos ) ) {
return true ;
}
}
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : PointReachableAreaNum
= = = = = = = = = = = = = = = = = = = = =
*/
int idAI : : PointReachableAreaNum ( const idVec3 & pos , const float boundsScale ) const {
int areaNum ;
idVec3 size ;
idBounds bounds ;
if ( ! aas ) {
return 0 ;
}
size = aas - > GetSettings ( ) - > boundingBoxes [ 0 ] [ 1 ] * boundsScale ;
bounds [ 0 ] = - size ;
size . z = 32.0f ;
bounds [ 1 ] = size ;
if ( move . moveType = = MOVETYPE_FLY ) {
areaNum = aas - > PointReachableAreaNum ( pos , bounds , AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) ;
} else {
areaNum = aas - > PointReachableAreaNum ( pos , bounds , AREA_REACHABLE_WALK ) ;
}
return areaNum ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : PathToGoal
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : PathToGoal ( aasPath_t & path , int areaNum , const idVec3 & origin , int goalAreaNum , const idVec3 & goalOrigin ) const {
idVec3 org ;
idVec3 goal ;
if ( ! aas ) {
return false ;
}
org = origin ;
aas - > PushPointIntoAreaNum ( areaNum , org ) ;
if ( ! areaNum ) {
return false ;
}
goal = goalOrigin ;
aas - > PushPointIntoAreaNum ( goalAreaNum , goal ) ;
if ( ! goalAreaNum ) {
return false ;
}
2018-08-26 21:52:13 +00:00
//ivan start
/*
se io sono / vado su ALTRO piano E c ' <EFBFBD> strada che collega :
- isXlocked 0 updXlock 0 - - > mai lockato , riesce a fare strada ,
- isXlocked 0 updXlock 1 - - > si slocca , riesce a fare strada ,
- isXlocked 1 updXlock 1 - - > si slocca , riesce a fare strada ,
- isXlocked 1 updXlock 0 - - > <EFBFBD> lockato , impazzisce cercando di fare strada ,
Soluz : se lockato e no updXlock , non deve provare a raggiungermi se X diversa .
*/
if ( isXlocked & & ! updXlock ) {
if ( goal . x ! = fastXpos ) {
//gameLocal.Printf("fix 1\n");
return false ;
}
}
/*se io sono/vado su STESSO piano E c'<27> strada alternativa su altro piano:
- isXlocked 0 updXlock 0 - - > mai lockato , riesce a fare strada alternativa ,
- isXlocked 0 updXlock 1 - - > se gi <EFBFBD> lockato , impazzisce cercando di fare strada alternativa , ( quindi ora var isXlocked = 1 )
- isXlocked 1 updXlock 1 - - > <EFBFBD> lockato , impazzisce cercando di fare strada alternativa ,
- isXlocked 1 updXlock 0 - - > <EFBFBD> lockato , impazzisce cercando di fare strada alternativa ,
Soluz : se lockato , non deve vedere la strada .
Miglioramento : se updXlock 1 pu <EFBFBD> cmq vederla , ma se deve anche sloccare . . .
*/
/*was:
2011-11-22 21:28:15 +00:00
if ( move . moveType = = MOVETYPE_FLY ) {
return aas - > FlyPathToGoal ( path , areaNum , org , goalAreaNum , goal , travelFlags ) ;
} else {
return aas - > WalkPathToGoal ( path , areaNum , org , goalAreaNum , goal , travelFlags ) ;
}
2018-08-26 21:52:13 +00:00
*/
bool result ;
if ( move . moveType = = MOVETYPE_FLY ) {
result = aas - > FlyPathToGoal ( path , areaNum , org , goalAreaNum , goal , travelFlags ) ;
} else {
result = aas - > WalkPathToGoal ( path , areaNum , org , goalAreaNum , goal , travelFlags ) ;
}
if ( isXlocked & & result ) {
if ( path . moveGoal . x ! = fastXpos ) {
//gameLocal.Printf("fix 2\n");
result = false ;
}
}
return result ;
//ivan end
2011-11-22 21:28:15 +00:00
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TravelDistance
Returns the approximate travel distance from one position to the goal , or if no AAS , the straight line distance .
This is feakin ' slow , so it ' s not good to do it too many times per frame . It also is slower the further you
are from the goal , so try to break the goals up into shorter distances .
= = = = = = = = = = = = = = = = = = = = =
*/
float idAI : : TravelDistance ( const idVec3 & start , const idVec3 & end ) const {
int fromArea ;
int toArea ;
float dist ;
idVec2 delta ;
if ( ! aas ) {
// no aas, so just take the straight line distance
delta = end . ToVec2 ( ) - start . ToVec2 ( ) ;
dist = delta . LengthFast ( ) ;
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorBlue , start , end , gameLocal . msec , false ) ;
gameRenderWorld - > DrawText ( va ( " %d " , ( int ) dist ) , ( start + end ) * 0.5f , 0.1f , colorWhite , gameLocal . GetLocalPlayer ( ) - > viewAngles . ToMat3 ( ) ) ;
}
return dist ;
}
fromArea = PointReachableAreaNum ( start ) ;
toArea = PointReachableAreaNum ( end ) ;
if ( ! fromArea | | ! toArea ) {
// can't seem to get there
return - 1 ;
}
if ( fromArea = = toArea ) {
// same area, so just take the straight line distance
delta = end . ToVec2 ( ) - start . ToVec2 ( ) ;
dist = delta . LengthFast ( ) ;
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorBlue , start , end , gameLocal . msec , false ) ;
gameRenderWorld - > DrawText ( va ( " %d " , ( int ) dist ) , ( start + end ) * 0.5f , 0.1f , colorWhite , gameLocal . GetLocalPlayer ( ) - > viewAngles . ToMat3 ( ) ) ;
}
return dist ;
}
idReachability * reach ;
int travelTime ;
if ( ! aas - > RouteToGoalArea ( fromArea , start , toArea , travelFlags , travelTime , & reach ) ) {
return - 1 ;
}
if ( ai_debugMove . GetBool ( ) ) {
if ( move . moveType = = MOVETYPE_FLY ) {
aas - > ShowFlyPath ( start , toArea , end ) ;
} else {
aas - > ShowWalkPath ( start , toArea , end ) ;
}
}
return travelTime ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : StopMove
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : StopMove ( moveStatus_t status ) {
AI_MOVE_DONE = true ;
AI_FORWARD = false ;
move . moveCommand = MOVE_NONE ;
move . moveStatus = status ;
move . toAreaNum = 0 ;
move . goalEntity = NULL ;
move . moveDest = physicsObj . GetOrigin ( ) ;
AI_DEST_UNREACHABLE = false ;
AI_OBSTACLE_IN_PATH = false ;
AI_BLOCKED = false ;
move . startTime = gameLocal . time ;
move . duration = 0 ;
move . range = 0.0f ;
move . speed = 0.0f ;
move . anim = 0 ;
move . moveDir . Zero ( ) ;
move . lastMoveOrigin . Zero ( ) ;
move . lastMoveTime = gameLocal . time ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : FaceEnemy
Continually face the enemy ' s last known position . MoveDone is always true in this case .
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : FaceEnemy ( void ) {
idActor * enemyEnt = enemy . GetEntity ( ) ;
if ( ! enemyEnt ) {
StopMove ( MOVE_STATUS_DEST_NOT_FOUND ) ;
return false ;
}
TurnToward ( lastVisibleEnemyPos ) ;
move . goalEntity = enemyEnt ;
move . moveDest = physicsObj . GetOrigin ( ) ;
move . moveCommand = MOVE_FACE_ENEMY ;
move . moveStatus = MOVE_STATUS_WAITING ;
move . startTime = gameLocal . time ;
move . speed = 0.0f ;
AI_MOVE_DONE = true ;
AI_FORWARD = false ;
AI_DEST_UNREACHABLE = false ;
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : FaceEntity
Continually face the entity position . MoveDone is always true in this case .
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : FaceEntity ( idEntity * ent ) {
if ( ! ent ) {
StopMove ( MOVE_STATUS_DEST_NOT_FOUND ) ;
return false ;
}
idVec3 entityOrg = ent - > GetPhysics ( ) - > GetOrigin ( ) ;
TurnToward ( entityOrg ) ;
move . goalEntity = ent ;
move . moveDest = physicsObj . GetOrigin ( ) ;
move . moveCommand = MOVE_FACE_ENTITY ;
move . moveStatus = MOVE_STATUS_WAITING ;
move . startTime = gameLocal . time ;
move . speed = 0.0f ;
AI_MOVE_DONE = true ;
AI_FORWARD = false ;
AI_DEST_UNREACHABLE = false ;
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : DirectMoveToPosition
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : DirectMoveToPosition ( const idVec3 & pos ) {
if ( ReachedPos ( pos , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
return true ;
}
move . moveDest = pos ;
move . goalEntity = NULL ;
move . moveCommand = MOVE_TO_POSITION_DIRECT ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . startTime = gameLocal . time ;
move . speed = fly_speed ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
if ( move . moveType = = MOVETYPE_FLY ) {
idVec3 dir = pos - physicsObj . GetOrigin ( ) ;
dir . Normalize ( ) ;
dir * = fly_speed ;
physicsObj . SetLinearVelocity ( dir ) ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveToEnemyHeight
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveToEnemyHeight ( void ) {
idActor * enemyEnt = enemy . GetEntity ( ) ;
if ( ! enemyEnt | | ( move . moveType ! = MOVETYPE_FLY ) ) {
StopMove ( MOVE_STATUS_DEST_NOT_FOUND ) ;
return false ;
}
move . moveDest . z = lastVisibleEnemyPos . z + enemyEnt - > EyeOffset ( ) . z + fly_offset ;
move . goalEntity = enemyEnt ;
move . moveCommand = MOVE_TO_ENEMYHEIGHT ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . startTime = gameLocal . time ;
move . speed = 0.0f ;
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveToEnemy
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveToEnemy ( void ) {
int areaNum ;
aasPath_t path ;
idActor * enemyEnt = enemy . GetEntity ( ) ;
if ( ! enemyEnt ) {
StopMove ( MOVE_STATUS_DEST_NOT_FOUND ) ;
return false ;
}
if ( ReachedPos ( lastVisibleReachableEnemyPos , MOVE_TO_ENEMY ) ) {
if ( ! ReachedPos ( lastVisibleEnemyPos , MOVE_TO_ENEMY ) | | ! AI_ENEMY_VISIBLE ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
StopMove ( MOVE_STATUS_DONE ) ;
return true ;
}
idVec3 pos = lastVisibleReachableEnemyPos ;
move . toAreaNum = 0 ;
if ( aas ) {
move . toAreaNum = PointReachableAreaNum ( pos ) ;
aas - > PushPointIntoAreaNum ( move . toAreaNum , pos ) ;
areaNum = PointReachableAreaNum ( physicsObj . GetOrigin ( ) ) ;
if ( ! PathToGoal ( path , areaNum , physicsObj . GetOrigin ( ) , move . toAreaNum , pos ) ) {
AI_DEST_UNREACHABLE = true ;
return false ;
}
}
if ( ! move . toAreaNum ) {
// if only trying to update the enemy position
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
if ( ! aas ) {
// keep the move destination up to date for wandering
move . moveDest = pos ;
}
return false ;
}
if ( ! NewWanderDir ( pos ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
}
if ( move . moveCommand ! = MOVE_TO_ENEMY ) {
move . moveCommand = MOVE_TO_ENEMY ;
move . startTime = gameLocal . time ;
}
move . moveDest = pos ;
move . goalEntity = enemyEnt ;
move . speed = fly_speed ;
move . moveStatus = MOVE_STATUS_MOVING ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveToEntity
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveToEntity ( idEntity * ent ) {
int areaNum ;
aasPath_t path ;
idVec3 pos ;
if ( ! ent ) {
StopMove ( MOVE_STATUS_DEST_NOT_FOUND ) ;
return false ;
}
pos = ent - > GetPhysics ( ) - > GetOrigin ( ) ;
if ( ( move . moveType ! = MOVETYPE_FLY ) & & ( ( move . moveCommand ! = MOVE_TO_ENTITY ) | | ( move . goalEntityOrigin ! = pos ) ) ) {
ent - > GetFloorPos ( 64.0f , pos ) ;
}
if ( ReachedPos ( pos , MOVE_TO_ENTITY ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
return true ;
}
move . toAreaNum = 0 ;
if ( aas ) {
move . toAreaNum = PointReachableAreaNum ( pos ) ;
aas - > PushPointIntoAreaNum ( move . toAreaNum , pos ) ;
areaNum = PointReachableAreaNum ( physicsObj . GetOrigin ( ) ) ;
if ( ! PathToGoal ( path , areaNum , physicsObj . GetOrigin ( ) , move . toAreaNum , pos ) ) {
AI_DEST_UNREACHABLE = true ;
return false ;
}
}
if ( ! move . toAreaNum ) {
// if only trying to update the entity position
if ( move . moveCommand = = MOVE_TO_ENTITY ) {
if ( ! aas ) {
// keep the move destination up to date for wandering
move . moveDest = pos ;
}
return false ;
}
if ( ! NewWanderDir ( pos ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
}
if ( ( move . moveCommand ! = MOVE_TO_ENTITY ) | | ( move . goalEntity . GetEntity ( ) ! = ent ) ) {
move . startTime = gameLocal . time ;
move . goalEntity = ent ;
move . moveCommand = MOVE_TO_ENTITY ;
}
move . moveDest = pos ;
move . goalEntityOrigin = ent - > GetPhysics ( ) - > GetOrigin ( ) ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . speed = fly_speed ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveOutOfRange
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveOutOfRange ( idEntity * ent , float range ) {
int areaNum ;
aasObstacle_t obstacle ;
aasGoal_t goal ;
idBounds bounds ;
idVec3 pos ;
if ( ! aas | | ! ent ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
const idVec3 & org = physicsObj . GetOrigin ( ) ;
areaNum = PointReachableAreaNum ( org ) ;
// consider the entity the monster is getting close to as an obstacle
obstacle . absBounds = ent - > GetPhysics ( ) - > GetAbsBounds ( ) ;
if ( ent = = enemy . GetEntity ( ) ) {
pos = lastVisibleEnemyPos ;
} else {
pos = ent - > GetPhysics ( ) - > GetOrigin ( ) ;
}
idAASFindAreaOutOfRange findGoal ( pos , range ) ;
if ( ! aas - > FindNearestGoal ( goal , areaNum , org , pos , travelFlags , & obstacle , 1 , findGoal ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
if ( ReachedPos ( goal . origin , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
return true ;
}
move . moveDest = goal . origin ;
move . toAreaNum = goal . areaNum ;
move . goalEntity = ent ;
move . moveCommand = MOVE_OUT_OF_RANGE ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . range = range ;
move . speed = fly_speed ;
move . startTime = gameLocal . time ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveToAttackPosition
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveToAttackPosition ( idEntity * ent , int attack_anim ) {
int areaNum ;
aasObstacle_t obstacle ;
aasGoal_t goal ;
idBounds bounds ;
idVec3 pos ;
if ( ! aas | | ! ent ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
const idVec3 & org = physicsObj . GetOrigin ( ) ;
areaNum = PointReachableAreaNum ( org ) ;
// consider the entity the monster is getting close to as an obstacle
obstacle . absBounds = ent - > GetPhysics ( ) - > GetAbsBounds ( ) ;
if ( ent = = enemy . GetEntity ( ) ) {
pos = lastVisibleEnemyPos ;
} else {
pos = ent - > GetPhysics ( ) - > GetOrigin ( ) ;
}
idAASFindAttackPosition findGoal ( this , physicsObj . GetGravityAxis ( ) , ent , pos , missileLaunchOffset [ attack_anim ] ) ;
if ( ! aas - > FindNearestGoal ( goal , areaNum , org , pos , travelFlags , & obstacle , 1 , findGoal ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
move . moveDest = goal . origin ;
move . toAreaNum = goal . areaNum ;
move . goalEntity = ent ;
move . moveCommand = MOVE_TO_ATTACK_POSITION ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . speed = fly_speed ;
move . startTime = gameLocal . time ;
move . anim = attack_anim ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveToPosition
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveToPosition ( const idVec3 & pos ) {
idVec3 org ;
int areaNum ;
aasPath_t path ;
if ( ReachedPos ( pos , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
return true ;
}
org = pos ;
move . toAreaNum = 0 ;
if ( aas ) {
move . toAreaNum = PointReachableAreaNum ( org ) ;
aas - > PushPointIntoAreaNum ( move . toAreaNum , org ) ;
areaNum = PointReachableAreaNum ( physicsObj . GetOrigin ( ) ) ;
if ( ! PathToGoal ( path , areaNum , physicsObj . GetOrigin ( ) , move . toAreaNum , org ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
}
if ( ! move . toAreaNum & & ! NewWanderDir ( org ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
move . moveDest = org ;
move . goalEntity = NULL ;
move . moveCommand = MOVE_TO_POSITION ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . startTime = gameLocal . time ;
move . speed = fly_speed ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveToCover
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveToCover ( idEntity * entity , const idVec3 & hideFromPos ) {
int areaNum ;
aasObstacle_t obstacle ;
aasGoal_t hideGoal ;
idBounds bounds ;
if ( ! aas | | ! entity ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
const idVec3 & org = physicsObj . GetOrigin ( ) ;
areaNum = PointReachableAreaNum ( org ) ;
// consider the entity the monster tries to hide from as an obstacle
obstacle . absBounds = entity - > GetPhysics ( ) - > GetAbsBounds ( ) ;
idAASFindCover findCover ( hideFromPos ) ;
if ( ! aas - > FindNearestGoal ( hideGoal , areaNum , org , hideFromPos , travelFlags , & obstacle , 1 , findCover ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
if ( ReachedPos ( hideGoal . origin , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
return true ;
}
move . moveDest = hideGoal . origin ;
move . toAreaNum = hideGoal . areaNum ;
move . goalEntity = entity ;
move . moveCommand = MOVE_TO_COVER ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . startTime = gameLocal . time ;
move . speed = fly_speed ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_DEST_UNREACHABLE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SlideToPosition
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : SlideToPosition ( const idVec3 & pos , float time ) {
StopMove ( MOVE_STATUS_DONE ) ;
move . moveDest = pos ;
move . goalEntity = NULL ;
move . moveCommand = MOVE_SLIDE_TO_POSITION ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . startTime = gameLocal . time ;
move . duration = idPhysics : : SnapTimeToPhysicsFrame ( SEC2MS ( time ) ) ;
AI_MOVE_DONE = false ;
AI_DEST_UNREACHABLE = false ;
AI_FORWARD = false ;
if ( move . duration > 0 ) {
move . moveDir = ( pos - physicsObj . GetOrigin ( ) ) / MS2SEC ( move . duration ) ;
if ( move . moveType ! = MOVETYPE_FLY ) {
move . moveDir . z = 0.0f ;
}
move . speed = move . moveDir . LengthFast ( ) ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : WanderAround
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : WanderAround ( void ) {
StopMove ( MOVE_STATUS_DONE ) ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
move . moveDest = physicsObj . GetOrigin ( ) + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 256.0f ;
2018-08-26 21:52:13 +00:00
//ivan start - lock the X position - don't try to wander around
if ( isXlocked ) {
move . moveDest . x = fastXpos ;
}
//ivan end
2011-11-22 21:28:15 +00:00
if ( ! NewWanderDir ( move . moveDest ) ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
return false ;
}
move . moveCommand = MOVE_WANDER ;
move . moveStatus = MOVE_STATUS_MOVING ;
move . startTime = gameLocal . time ;
move . speed = fly_speed ;
2021-12-19 04:51:02 +00:00
//rev 2020 disable moving
//AI_MOVE_DONE = false;
//AI_FORWARD = true;
if ( disableMoving ) {
AI_MOVE_DONE = true ;
AI_FORWARD = false ;
} else {
2011-11-22 21:28:15 +00:00
AI_MOVE_DONE = false ;
2021-12-19 04:51:02 +00:00
AI_FORWARD = true ;
}
//rev 2020 end
2011-11-22 21:28:15 +00:00
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : MoveDone
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : MoveDone ( void ) const {
return ( move . moveCommand = = MOVE_NONE ) ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : StepDirection
= = = = = = = = = = = = = = = =
*/
bool idAI : : StepDirection ( float dir ) {
predictedPath_t path ;
idVec3 org ;
move . wanderYaw = dir ;
move . moveDir = idAngles ( 0 , move . wanderYaw , 0 ) . ToForward ( ) ;
org = physicsObj . GetOrigin ( ) ;
idAI : : PredictPath ( this , aas , org , move . moveDir * 48.0f , 1000 , 1000 , ( move . moveType = = MOVETYPE_FLY ) ? SE_BLOCKED : ( SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA ) , path ) ;
if ( path . blockingEntity & & ( ( move . moveCommand = = MOVE_TO_ENEMY ) | | ( move . moveCommand = = MOVE_TO_ENTITY ) ) & & ( path . blockingEntity = = move . goalEntity . GetEntity ( ) ) ) {
// don't report being blocked if we ran into our goal entity
return true ;
}
if ( ( move . moveType = = MOVETYPE_FLY ) & & ( path . endEvent = = SE_BLOCKED ) ) {
float z ;
move . moveDir = path . endVelocity * 1.0f / 48.0f ;
// trace down to the floor and see if we can go forward
idAI : : PredictPath ( this , aas , org , idVec3 ( 0.0f , 0.0f , - 1024.0f ) , 1000 , 1000 , SE_BLOCKED , path ) ;
idVec3 floorPos = path . endPos ;
idAI : : PredictPath ( this , aas , floorPos , move . moveDir * 48.0f , 1000 , 1000 , SE_BLOCKED , path ) ;
if ( ! path . endEvent ) {
move . moveDir . z = - 1.0f ;
return true ;
}
// trace up to see if we can go over something and go forward
idAI : : PredictPath ( this , aas , org , idVec3 ( 0.0f , 0.0f , 256.0f ) , 1000 , 1000 , SE_BLOCKED , path ) ;
idVec3 ceilingPos = path . endPos ;
for ( z = org . z ; z < = ceilingPos . z + 64.0f ; z + = 64.0f ) {
idVec3 start ;
if ( z < = ceilingPos . z ) {
start . x = org . x ;
start . y = org . y ;
2011-12-06 18:20:15 +00:00
start . z = z ;
2011-11-22 21:28:15 +00:00
} else {
start = ceilingPos ;
}
idAI : : PredictPath ( this , aas , start , move . moveDir * 48.0f , 1000 , 1000 , SE_BLOCKED , path ) ;
if ( ! path . endEvent ) {
move . moveDir . z = 1.0f ;
return true ;
}
}
return false ;
}
return ( path . endEvent = = 0 ) ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : NewWanderDir
= = = = = = = = = = = = = = = =
*/
bool idAI : : NewWanderDir ( const idVec3 & dest ) {
2018-08-26 21:52:13 +00:00
2011-11-22 21:28:15 +00:00
float deltax , deltay ;
float d [ 3 ] ;
float tdir , olddir , turnaround ;
2018-08-26 21:52:13 +00:00
//ivan start
if ( isXlocked ) {
//gameLocal.Printf("idAI::NewWanderDir : current_yaw %f \n", current_yaw);
if ( current_yaw > 0 & & current_yaw < 180 ) { //case about 90 --> turn to -90
turnaround = idMath : : AngleNormalize360 ( - 90 ) ;
} else { //case about -90 --> turn to 90
turnaround = idMath : : AngleNormalize360 ( 90 ) ;
}
} else {
//ivan end
move . nextWanderTime = gameLocal . time + ( gameLocal . random . RandomFloat ( ) * 500 + 500 ) ;
olddir = idMath : : AngleNormalize360 ( ( int ) ( current_yaw / 45 ) * 45 ) ;
turnaround = idMath : : AngleNormalize360 ( olddir - 180 ) ;
idVec3 org = physicsObj . GetOrigin ( ) ;
deltax = dest . x - org . x ;
deltay = dest . y - org . y ;
if ( deltax > 10 ) {
d [ 1 ] = 0 ;
} else if ( deltax < - 10 ) {
d [ 1 ] = 180 ;
} else {
d [ 1 ] = DI_NODIR ;
}
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
if ( deltay < - 10 ) {
d [ 2 ] = 270 ;
} else if ( deltay > 10 ) {
d [ 2 ] = 90 ;
} else {
d [ 2 ] = DI_NODIR ;
}
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
// try direct route
if ( d [ 1 ] ! = DI_NODIR & & d [ 2 ] ! = DI_NODIR ) {
if ( d [ 1 ] = = 0 ) {
tdir = d [ 2 ] = = 90 ? 45 : 315 ;
} else {
tdir = d [ 2 ] = = 90 ? 135 : 215 ;
}
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
if ( tdir ! = turnaround & & StepDirection ( tdir ) ) {
return true ;
}
}
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
// try other directions
if ( ( gameLocal . random . RandomInt ( ) & 1 ) | | idMath : : Fabs ( deltay ) > idMath : : Fabs ( deltax ) ) {
tdir = d [ 1 ] ;
d [ 1 ] = d [ 2 ] ;
d [ 2 ] = tdir ;
2011-11-22 21:28:15 +00:00
}
2018-08-26 21:52:13 +00:00
if ( d [ 1 ] ! = DI_NODIR & & d [ 1 ] ! = turnaround & & StepDirection ( d [ 1 ] ) ) {
2011-11-22 21:28:15 +00:00
return true ;
}
2018-08-26 21:52:13 +00:00
if ( d [ 2 ] ! = DI_NODIR & & d [ 2 ] ! = turnaround & & StepDirection ( d [ 2 ] ) ) {
return true ;
}
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
// there is no direct path to the player, so pick another direction
if ( olddir ! = DI_NODIR & & StepDirection ( olddir ) ) {
return true ;
}
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
// randomly determine direction of search
if ( gameLocal . random . RandomInt ( ) & 1 ) {
for ( tdir = 0 ; tdir < = 315 ; tdir + = 45 ) {
if ( tdir ! = turnaround & & StepDirection ( tdir ) ) {
return true ;
}
2011-11-22 21:28:15 +00:00
}
2018-08-26 21:52:13 +00:00
} else {
for ( tdir = 315 ; tdir > = 0 ; tdir - = 45 ) {
if ( tdir ! = turnaround & & StepDirection ( tdir ) ) {
return true ;
}
2011-11-22 21:28:15 +00:00
}
}
2018-08-26 21:52:13 +00:00
} //ivan
2011-11-22 21:28:15 +00:00
2018-08-26 21:52:13 +00:00
if ( turnaround ! = DI_NODIR & & StepDirection ( turnaround ) ) { //just try turnaround
2011-11-22 21:28:15 +00:00
return true ;
}
// can't move
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : GetMovePos
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : GetMovePos ( idVec3 & seekPos ) {
int areaNum ;
aasPath_t path ;
bool result ;
idVec3 org ;
org = physicsObj . GetOrigin ( ) ;
seekPos = org ;
switch ( move . moveCommand ) {
case MOVE_NONE :
seekPos = move . moveDest ;
return false ;
break ;
case MOVE_FACE_ENEMY :
case MOVE_FACE_ENTITY :
seekPos = move . moveDest ;
return false ;
break ;
case MOVE_TO_POSITION_DIRECT :
seekPos = move . moveDest ;
if ( ReachedPos ( move . moveDest , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
}
return false ;
break ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
case MOVE_SLIDE_TO_POSITION :
seekPos = org ;
return false ;
break ;
}
if ( move . moveCommand = = MOVE_TO_ENTITY ) {
MoveToEntity ( move . goalEntity . GetEntity ( ) ) ;
}
move . moveStatus = MOVE_STATUS_MOVING ;
result = false ;
if ( gameLocal . time > move . blockTime ) {
if ( move . moveCommand = = MOVE_WANDER ) {
move . moveDest = org + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 256.0f ;
} else {
if ( ReachedPos ( move . moveDest , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
seekPos = org ;
return false ;
}
}
if ( aas & & move . toAreaNum ) {
areaNum = PointReachableAreaNum ( org ) ;
if ( PathToGoal ( path , areaNum , org , move . toAreaNum , move . moveDest ) ) {
seekPos = path . moveGoal ;
result = true ;
move . nextWanderTime = 0 ;
} else {
AI_DEST_UNREACHABLE = true ;
}
}
}
if ( ! result ) {
// wander around
if ( ( gameLocal . time > move . nextWanderTime ) | | ! StepDirection ( move . wanderYaw ) ) {
result = NewWanderDir ( move . moveDest ) ;
if ( ! result ) {
StopMove ( MOVE_STATUS_DEST_UNREACHABLE ) ;
AI_DEST_UNREACHABLE = true ;
seekPos = org ;
return false ;
}
} else {
result = true ;
}
seekPos = org + move . moveDir * 2048.0f ;
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorYellow , org , seekPos , gameLocal . msec , true ) ;
}
} else {
AI_DEST_UNREACHABLE = false ;
}
if ( result & & ( ai_debugMove . GetBool ( ) ) ) {
gameRenderWorld - > DebugLine ( colorCyan , physicsObj . GetOrigin ( ) , seekPos ) ;
}
2018-08-26 21:52:13 +00:00
if ( isXlocked ) {
seekPos . x = fastXpos ; //ivan - make sure goalPos.x = fastXpos just in case result == true.
}
2011-11-22 21:28:15 +00:00
return result ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : EntityCanSeePos
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : EntityCanSeePos ( idActor * actor , const idVec3 & actorOrigin , const idVec3 & pos ) {
idVec3 eye , point ;
trace_t results ;
pvsHandle_t handle ;
handle = gameLocal . pvs . SetupCurrentPVS ( actor - > GetPVSAreas ( ) , actor - > GetNumPVSAreas ( ) ) ;
if ( ! gameLocal . pvs . InCurrentPVS ( handle , GetPVSAreas ( ) , GetNumPVSAreas ( ) ) ) {
gameLocal . pvs . FreeCurrentPVS ( handle ) ;
return false ;
}
gameLocal . pvs . FreeCurrentPVS ( handle ) ;
eye = actorOrigin + actor - > EyeOffset ( ) ;
point = pos ;
point [ 2 ] + = 1.0f ;
physicsObj . DisableClip ( ) ;
gameLocal . clip . TracePoint ( results , eye , point , MASK_SOLID , actor ) ;
if ( results . fraction > = 1.0f | | ( gameLocal . GetTraceEntity ( results ) = = this ) ) {
physicsObj . EnableClip ( ) ;
return true ;
}
const idBounds & bounds = physicsObj . GetBounds ( ) ;
point [ 2 ] + = bounds [ 1 ] [ 2 ] - bounds [ 0 ] [ 2 ] ;
gameLocal . clip . TracePoint ( results , eye , point , MASK_SOLID , actor ) ;
physicsObj . EnableClip ( ) ;
if ( results . fraction > = 1.0f | | ( gameLocal . GetTraceEntity ( results ) = = this ) ) {
return true ;
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : BlockedFailSafe
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : BlockedFailSafe ( void ) {
if ( ! ai_blockedFailSafe . GetBool ( ) | | blockedRadius < 0.0f ) {
return ;
}
if ( ! physicsObj . OnGround ( ) | | enemy . GetEntity ( ) = = NULL | |
( physicsObj . GetOrigin ( ) - move . lastMoveOrigin ) . LengthSqr ( ) > Square ( blockedRadius ) ) {
move . lastMoveOrigin = physicsObj . GetOrigin ( ) ;
move . lastMoveTime = gameLocal . time ;
}
if ( move . lastMoveTime < gameLocal . time - blockedMoveTime ) {
if ( lastAttackTime < gameLocal . time - blockedAttackTime ) {
AI_BLOCKED = true ;
move . lastMoveTime = gameLocal . time ;
}
}
}
/***********************************************************************
turning
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Turn
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Turn ( void ) {
float diff ;
float diff2 ;
float turnAmount ;
animFlags_t animflags ;
if ( ! turnRate ) {
return ;
}
// check if the animator has marker this anim as non-turning
if ( ! legsAnim . Disabled ( ) & & ! legsAnim . AnimDone ( 0 ) ) {
animflags = legsAnim . GetAnimFlags ( ) ;
} else {
animflags = torsoAnim . GetAnimFlags ( ) ;
}
if ( animflags . ai_no_turn ) {
return ;
}
if ( anim_turn_angles & & animflags . anim_turn ) {
idMat3 rotateAxis ;
// set the blend between no turn and full turn
float frac = anim_turn_amount / anim_turn_angles ;
animator . CurrentAnim ( ANIMCHANNEL_LEGS ) - > SetSyncedAnimWeight ( 0 , 1.0f - frac ) ;
animator . CurrentAnim ( ANIMCHANNEL_LEGS ) - > SetSyncedAnimWeight ( 1 , frac ) ;
animator . CurrentAnim ( ANIMCHANNEL_TORSO ) - > SetSyncedAnimWeight ( 0 , 1.0f - frac ) ;
animator . CurrentAnim ( ANIMCHANNEL_TORSO ) - > SetSyncedAnimWeight ( 1 , frac ) ;
// get the total rotation from the start of the anim
animator . GetDeltaRotation ( 0 , gameLocal . time , rotateAxis ) ;
current_yaw = idMath : : AngleNormalize180 ( anim_turn_yaw + rotateAxis [ 0 ] . ToYaw ( ) ) ;
} else {
diff = idMath : : AngleNormalize180 ( ideal_yaw - current_yaw ) ;
turnVel + = AI_TURN_SCALE * diff * MS2SEC ( gameLocal . msec ) ;
if ( turnVel > turnRate ) {
turnVel = turnRate ;
} else if ( turnVel < - turnRate ) {
turnVel = - turnRate ;
}
turnAmount = turnVel * MS2SEC ( gameLocal . msec ) ;
if ( ( diff > = 0.0f ) & & ( turnAmount > = diff ) ) {
turnVel = diff / MS2SEC ( gameLocal . msec ) ;
turnAmount = diff ;
} else if ( ( diff < = 0.0f ) & & ( turnAmount < = diff ) ) {
turnVel = diff / MS2SEC ( gameLocal . msec ) ;
turnAmount = diff ;
}
current_yaw + = turnAmount ;
current_yaw = idMath : : AngleNormalize180 ( current_yaw ) ;
diff2 = idMath : : AngleNormalize180 ( ideal_yaw - current_yaw ) ;
if ( idMath : : Fabs ( diff2 ) < 0.1f ) {
current_yaw = ideal_yaw ;
}
}
viewAxis = idAngles ( 0 , current_yaw , 0 ) . ToMat3 ( ) ;
if ( ai_debugMove . GetBool ( ) ) {
const idVec3 & org = physicsObj . GetOrigin ( ) ;
gameRenderWorld - > DebugLine ( colorRed , org , org + idAngles ( 0 , ideal_yaw , 0 ) . ToForward ( ) * 64 , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorGreen , org , org + idAngles ( 0 , current_yaw , 0 ) . ToForward ( ) * 48 , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorYellow , org , org + idAngles ( 0 , current_yaw + turnVel , 0 ) . ToForward ( ) * 32 , gameLocal . msec ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : FacingIdeal
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : FacingIdeal ( void ) {
float diff ;
if ( ! turnRate ) {
return true ;
}
diff = idMath : : AngleNormalize180 ( current_yaw - ideal_yaw ) ;
if ( idMath : : Fabs ( diff ) < 0.01f ) {
// force it to be exact
current_yaw = ideal_yaw ;
return true ;
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TurnToward
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : TurnToward ( float yaw ) {
ideal_yaw = idMath : : AngleNormalize180 ( yaw ) ;
bool result = FacingIdeal ( ) ;
return result ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TurnToward
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : TurnToward ( const idVec3 & pos ) {
idVec3 dir ;
idVec3 local_dir ;
float lengthSqr ;
dir = pos - physicsObj . GetOrigin ( ) ;
physicsObj . GetGravityAxis ( ) . ProjectVector ( dir , local_dir ) ;
local_dir . z = 0.0f ;
lengthSqr = local_dir . LengthSqr ( ) ;
if ( lengthSqr > Square ( 2.0f ) | | ( lengthSqr > Square ( 0.1f ) & & enemy . GetEntity ( ) = = NULL ) ) {
ideal_yaw = idMath : : AngleNormalize180 ( local_dir . ToYaw ( ) ) ;
}
bool result = FacingIdeal ( ) ;
return result ;
}
/***********************************************************************
Movement
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = =
idAI : : ApplyImpulse
= = = = = = = = = = = = = = = =
*/
void idAI : : ApplyImpulse ( idEntity * ent , int id , const idVec3 & point , const idVec3 & impulse ) {
// FIXME: Jim take a look at this and see if this is a reasonable thing to do
// instead of a spawnArg flag.. Sabaoth is the only slide monster ( and should be the only one for D3 )
// and we don't want him taking physics impulses as it can knock him off the path
2018-08-26 21:52:13 +00:00
//ivan start
if ( noPush ) return ;
//ivan end
2011-11-22 21:28:15 +00:00
if ( move . moveType ! = MOVETYPE_STATIC & & move . moveType ! = MOVETYPE_SLIDE ) {
idActor : : ApplyImpulse ( ent , id , point , impulse ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : GetMoveDelta
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : GetMoveDelta ( const idMat3 & oldaxis , const idMat3 & axis , idVec3 & delta ) {
idVec3 oldModelOrigin ;
idVec3 modelOrigin ;
animator . GetDelta ( gameLocal . time - gameLocal . msec , gameLocal . time , delta ) ;
delta = axis * delta ;
if ( modelOffset ! = vec3_zero ) {
// the pivot of the monster's model is around its origin, and not around the bounding
// box's origin, so we have to compensate for this when the model is offset so that
// the monster still appears to rotate around it's origin.
oldModelOrigin = modelOffset * oldaxis ;
modelOrigin = modelOffset * axis ;
delta + = oldModelOrigin - modelOrigin ;
}
delta * = physicsObj . GetGravityAxis ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : CheckObstacleAvoidance
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : CheckObstacleAvoidance ( const idVec3 & goalPos , idVec3 & newPos ) {
idEntity * obstacle ;
obstaclePath_t path ;
idVec3 dir ;
float dist ;
bool foundPath ;
if ( ignore_obstacles ) {
newPos = goalPos ;
move . obstacle = NULL ;
return ;
}
const idVec3 & origin = physicsObj . GetOrigin ( ) ;
obstacle = NULL ;
AI_OBSTACLE_IN_PATH = false ;
foundPath = FindPathAroundObstacles ( & physicsObj , aas , enemy . GetEntity ( ) , origin , goalPos , path ) ;
if ( ai_showObstacleAvoidance . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorBlue , goalPos + idVec3 ( 1.0f , 1.0f , 0.0f ) , goalPos + idVec3 ( 1.0f , 1.0f , 64.0f ) , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( foundPath ? colorYellow : colorRed , path . seekPos , path . seekPos + idVec3 ( 0.0f , 0.0f , 64.0f ) , gameLocal . msec ) ;
}
if ( ! foundPath ) {
// couldn't get around obstacles
if ( path . firstObstacle ) {
AI_OBSTACLE_IN_PATH = true ;
if ( physicsObj . GetAbsBounds ( ) . Expand ( 2.0f ) . IntersectsBounds ( path . firstObstacle - > GetPhysics ( ) - > GetAbsBounds ( ) ) ) {
obstacle = path . firstObstacle ;
}
} else if ( path . startPosObstacle ) {
AI_OBSTACLE_IN_PATH = true ;
if ( physicsObj . GetAbsBounds ( ) . Expand ( 2.0f ) . IntersectsBounds ( path . startPosObstacle - > GetPhysics ( ) - > GetAbsBounds ( ) ) ) {
obstacle = path . startPosObstacle ;
}
} else {
// Blocked by wall
move . moveStatus = MOVE_STATUS_BLOCKED_BY_WALL ;
}
#if 0
} else if ( path . startPosObstacle ) {
// check if we're past where the our origin was pushed out of the obstacle
dir = goalPos - origin ;
dir . Normalize ( ) ;
dist = ( path . seekPos - origin ) * dir ;
if ( dist < 1.0f ) {
AI_OBSTACLE_IN_PATH = true ;
obstacle = path . startPosObstacle ;
}
# endif
} else if ( path . seekPosObstacle ) {
// if the AI is very close to the path.seekPos already and path.seekPosObstacle != NULL
// then we want to push the path.seekPosObstacle entity out of the way
AI_OBSTACLE_IN_PATH = true ;
// check if we're past where the goalPos was pushed out of the obstacle
dir = goalPos - origin ;
dir . Normalize ( ) ;
dist = ( path . seekPos - origin ) * dir ;
if ( dist < 1.0f ) {
obstacle = path . seekPosObstacle ;
}
}
// if we had an obstacle, set our move status based on the type, and kick it out of the way if it's a moveable
if ( obstacle ) {
if ( obstacle - > IsType ( idActor : : Type ) ) {
// monsters aren't kickable
if ( obstacle = = enemy . GetEntity ( ) ) {
move . moveStatus = MOVE_STATUS_BLOCKED_BY_ENEMY ;
} else {
move . moveStatus = MOVE_STATUS_BLOCKED_BY_MONSTER ;
}
} else {
// try kicking the object out of the way
move . moveStatus = MOVE_STATUS_BLOCKED_BY_OBJECT ;
}
newPos = obstacle - > GetPhysics ( ) - > GetOrigin ( ) ;
//newPos = path.seekPos;
move . obstacle = obstacle ;
} else {
newPos = path . seekPos ;
move . obstacle = NULL ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : DeadMove
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : DeadMove ( void ) {
idVec3 delta ;
GetMoveDelta ( viewAxis , viewAxis , delta ) ;
physicsObj . SetDelta ( delta ) ;
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
AI_ONGROUND = physicsObj . OnGround ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : AnimMove
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : AnimMove ( void ) {
idVec3 goalPos ;
idVec3 delta ;
idVec3 goalDelta ;
float goalDist ;
idVec3 newDest ;
idVec3 oldorigin = physicsObj . GetOrigin ( ) ;
idMat3 oldaxis = viewAxis ;
AI_BLOCKED = false ;
2011-12-06 18:20:15 +00:00
if ( move . moveCommand < NUM_NONMOVING_COMMANDS ) {
2011-11-22 21:28:15 +00:00
move . lastMoveOrigin . Zero ( ) ;
move . lastMoveTime = gameLocal . time ;
}
move . obstacle = NULL ;
if ( ( move . moveCommand = = MOVE_FACE_ENEMY ) & & enemy . GetEntity ( ) ) {
TurnToward ( lastVisibleEnemyPos ) ;
goalPos = oldorigin ;
} else if ( ( move . moveCommand = = MOVE_FACE_ENTITY ) & & move . goalEntity . GetEntity ( ) ) {
TurnToward ( move . goalEntity . GetEntity ( ) - > GetPhysics ( ) - > GetOrigin ( ) ) ;
goalPos = oldorigin ;
} else if ( GetMovePos ( goalPos ) ) {
if ( move . moveCommand ! = MOVE_WANDER ) {
CheckObstacleAvoidance ( goalPos , newDest ) ;
TurnToward ( newDest ) ;
} else {
TurnToward ( goalPos ) ;
}
}
Turn ( ) ;
2018-08-26 21:52:13 +00:00
//ivan start
if ( AI_INHIBIT_MOVE ) {
delta . Zero ( ) ;
} else
//ivan end
2011-11-22 21:28:15 +00:00
if ( move . moveCommand = = MOVE_SLIDE_TO_POSITION ) {
if ( gameLocal . time < move . startTime + move . duration ) {
goalPos = move . moveDest - move . moveDir * MS2SEC ( move . startTime + move . duration - gameLocal . time ) ;
delta = goalPos - oldorigin ;
delta . z = 0.0f ;
} else {
delta = move . moveDest - oldorigin ;
delta . z = 0.0f ;
StopMove ( MOVE_STATUS_DONE ) ;
}
} else if ( allowMove ) {
GetMoveDelta ( oldaxis , viewAxis , delta ) ;
} else {
delta . Zero ( ) ;
}
if ( move . moveCommand = = MOVE_TO_POSITION ) {
goalDelta = move . moveDest - oldorigin ;
goalDist = goalDelta . LengthFast ( ) ;
if ( goalDist < delta . LengthFast ( ) ) {
delta = goalDelta ;
}
}
2018-08-26 21:52:13 +00:00
physicsObj . UseFlyMove ( false ) ; //ivan - from D3XP
2011-11-22 21:28:15 +00:00
physicsObj . SetDelta ( delta ) ;
physicsObj . ForceDeltaMove ( disableGravity ) ;
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorCyan , oldorigin , physicsObj . GetOrigin ( ) , 5000 ) ;
}
if ( ! af_push_moveables & & attack . Length ( ) & & TestMelee ( ) ) {
DirectDamage ( attack , enemy . GetEntity ( ) ) ;
} else {
idEntity * blockEnt = physicsObj . GetSlideMoveEntity ( ) ;
if ( blockEnt & & blockEnt - > IsType ( idMoveable : : Type ) & & blockEnt - > GetPhysics ( ) - > IsPushable ( ) ) {
KickObstacles ( viewAxis [ 0 ] , kickForce , blockEnt ) ;
}
}
BlockedFailSafe ( ) ;
AI_ONGROUND = physicsObj . OnGround ( ) ;
idVec3 org = physicsObj . GetOrigin ( ) ;
if ( oldorigin ! = org ) {
TouchTriggers ( ) ;
}
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugBounds ( colorMagenta , physicsObj . GetBounds ( ) , org , gameLocal . msec ) ;
gameRenderWorld - > DebugBounds ( colorMagenta , physicsObj . GetBounds ( ) , move . moveDest , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorYellow , org + EyeOffset ( ) , org + EyeOffset ( ) + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 16.0f , gameLocal . msec , true ) ;
DrawRoute ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
Seek
= = = = = = = = = = = = = = = = = = = = =
*/
idVec3 Seek ( idVec3 & vel , const idVec3 & org , const idVec3 & goal , float prediction ) {
idVec3 predictedPos ;
idVec3 goalDelta ;
idVec3 seekVel ;
// predict our position
predictedPos = org + vel * prediction ;
goalDelta = goal - predictedPos ;
seekVel = goalDelta * MS2SEC ( gameLocal . msec ) ;
return seekVel ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SlideMove
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : SlideMove ( void ) {
idVec3 goalPos ;
idVec3 delta ;
idVec3 goalDelta ;
float goalDist ;
idVec3 newDest ;
idVec3 oldorigin = physicsObj . GetOrigin ( ) ;
AI_BLOCKED = false ;
2011-12-06 18:20:15 +00:00
if ( move . moveCommand < NUM_NONMOVING_COMMANDS ) {
2011-11-22 21:28:15 +00:00
move . lastMoveOrigin . Zero ( ) ;
move . lastMoveTime = gameLocal . time ;
}
move . obstacle = NULL ;
if ( ( move . moveCommand = = MOVE_FACE_ENEMY ) & & enemy . GetEntity ( ) ) {
TurnToward ( lastVisibleEnemyPos ) ;
goalPos = move . moveDest ;
} else if ( ( move . moveCommand = = MOVE_FACE_ENTITY ) & & move . goalEntity . GetEntity ( ) ) {
TurnToward ( move . goalEntity . GetEntity ( ) - > GetPhysics ( ) - > GetOrigin ( ) ) ;
goalPos = move . moveDest ;
} else if ( GetMovePos ( goalPos ) ) {
CheckObstacleAvoidance ( goalPos , newDest ) ;
TurnToward ( newDest ) ;
goalPos = newDest ;
}
if ( move . moveCommand = = MOVE_SLIDE_TO_POSITION ) {
if ( gameLocal . time < move . startTime + move . duration ) {
goalPos = move . moveDest - move . moveDir * MS2SEC ( move . startTime + move . duration - gameLocal . time ) ;
} else {
goalPos = move . moveDest ;
StopMove ( MOVE_STATUS_DONE ) ;
}
}
if ( move . moveCommand = = MOVE_TO_POSITION ) {
goalDelta = move . moveDest - oldorigin ;
goalDist = goalDelta . LengthFast ( ) ;
if ( goalDist < delta . LengthFast ( ) ) {
delta = goalDelta ;
}
}
idVec3 vel = physicsObj . GetLinearVelocity ( ) ;
float z = vel . z ;
idVec3 predictedPos = oldorigin + vel * AI_SEEK_PREDICTION ;
// seek the goal position
goalDelta = goalPos - predictedPos ;
vel - = vel * AI_FLY_DAMPENING * MS2SEC ( gameLocal . msec ) ;
vel + = goalDelta * MS2SEC ( gameLocal . msec ) ;
// cap our speed
vel . Truncate ( fly_speed ) ;
vel . z = z ;
physicsObj . SetLinearVelocity ( vel ) ;
physicsObj . UseVelocityMove ( true ) ;
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
if ( ( move . moveCommand = = MOVE_FACE_ENEMY ) & & enemy . GetEntity ( ) ) {
TurnToward ( lastVisibleEnemyPos ) ;
} else if ( ( move . moveCommand = = MOVE_FACE_ENTITY ) & & move . goalEntity . GetEntity ( ) ) {
TurnToward ( move . goalEntity . GetEntity ( ) - > GetPhysics ( ) - > GetOrigin ( ) ) ;
} else if ( move . moveCommand ! = MOVE_NONE ) {
if ( vel . ToVec2 ( ) . LengthSqr ( ) > 0.1f ) {
TurnToward ( vel . ToYaw ( ) ) ;
}
}
Turn ( ) ;
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorCyan , oldorigin , physicsObj . GetOrigin ( ) , 5000 ) ;
}
if ( ! af_push_moveables & & attack . Length ( ) & & TestMelee ( ) ) {
DirectDamage ( attack , enemy . GetEntity ( ) ) ;
} else {
idEntity * blockEnt = physicsObj . GetSlideMoveEntity ( ) ;
if ( blockEnt & & blockEnt - > IsType ( idMoveable : : Type ) & & blockEnt - > GetPhysics ( ) - > IsPushable ( ) ) {
KickObstacles ( viewAxis [ 0 ] , kickForce , blockEnt ) ;
}
}
BlockedFailSafe ( ) ;
AI_ONGROUND = physicsObj . OnGround ( ) ;
idVec3 org = physicsObj . GetOrigin ( ) ;
if ( oldorigin ! = org ) {
TouchTriggers ( ) ;
}
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugBounds ( colorMagenta , physicsObj . GetBounds ( ) , org , gameLocal . msec ) ;
gameRenderWorld - > DebugBounds ( colorMagenta , physicsObj . GetBounds ( ) , move . moveDest , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorYellow , org + EyeOffset ( ) , org + EyeOffset ( ) + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 16.0f , gameLocal . msec , true ) ;
DrawRoute ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : AdjustFlyingAngles
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : AdjustFlyingAngles ( void ) {
idVec3 vel ;
2011-12-06 18:20:15 +00:00
float speed ;
float roll ;
float pitch ;
2011-11-22 21:28:15 +00:00
vel = physicsObj . GetLinearVelocity ( ) ;
speed = vel . Length ( ) ;
if ( speed < 5.0f ) {
roll = 0.0f ;
pitch = 0.0f ;
} else {
roll = vel * viewAxis [ 1 ] * - fly_roll_scale / fly_speed ;
if ( roll > fly_roll_max ) {
roll = fly_roll_max ;
} else if ( roll < - fly_roll_max ) {
roll = - fly_roll_max ;
}
pitch = vel * viewAxis [ 2 ] * - fly_pitch_scale / fly_speed ;
if ( pitch > fly_pitch_max ) {
pitch = fly_pitch_max ;
} else if ( pitch < - fly_pitch_max ) {
pitch = - fly_pitch_max ;
}
}
fly_roll = fly_roll * 0.95f + roll * 0.05f ;
fly_pitch = fly_pitch * 0.95f + pitch * 0.05f ;
if ( flyTiltJoint ! = INVALID_JOINT ) {
animator . SetJointAxis ( flyTiltJoint , JOINTMOD_WORLD , idAngles ( fly_pitch , 0.0f , fly_roll ) . ToMat3 ( ) ) ;
} else {
viewAxis = idAngles ( fly_pitch , current_yaw , fly_roll ) . ToMat3 ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : AddFlyBob
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : AddFlyBob ( idVec3 & vel ) {
idVec3 fly_bob_add ;
float t ;
if ( fly_bob_strength ) {
t = MS2SEC ( gameLocal . time + entityNumber * 497 ) ;
fly_bob_add = ( viewAxis [ 1 ] * idMath : : Sin16 ( t * fly_bob_horz ) + viewAxis [ 2 ] * idMath : : Sin16 ( t * fly_bob_vert ) ) * fly_bob_strength ;
vel + = fly_bob_add * MS2SEC ( gameLocal . msec ) ;
if ( ai_debugMove . GetBool ( ) ) {
const idVec3 & origin = physicsObj . GetOrigin ( ) ;
gameRenderWorld - > DebugArrow ( colorOrange , origin , origin + fly_bob_add , 0 ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : AdjustFlyHeight
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : AdjustFlyHeight ( idVec3 & vel , const idVec3 & goalPos ) {
const idVec3 & origin = physicsObj . GetOrigin ( ) ;
predictedPath_t path ;
idVec3 end ;
idVec3 dest ;
trace_t trace ;
idActor * enemyEnt ;
bool goLower ;
// make sure we're not flying too high to get through doors
goLower = false ;
if ( origin . z > goalPos . z ) {
dest = goalPos ;
dest . z = origin . z + 128.0f ;
idAI : : PredictPath ( this , aas , goalPos , dest - origin , 1000 , 1000 , SE_BLOCKED , path ) ;
if ( path . endPos . z < origin . z ) {
idVec3 addVel = Seek ( vel , origin , path . endPos , AI_SEEK_PREDICTION ) ;
vel . z + = addVel . z ;
goLower = true ;
}
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugBounds ( goLower ? colorRed : colorGreen , physicsObj . GetBounds ( ) , path . endPos , gameLocal . msec ) ;
}
}
if ( ! goLower ) {
// make sure we don't fly too low
end = origin ;
enemyEnt = enemy . GetEntity ( ) ;
if ( enemyEnt ) {
end . z = lastVisibleEnemyPos . z + lastVisibleEnemyEyeOffset . z + fly_offset ;
} else {
// just use the default eye height for the player
end . z = goalPos . z + DEFAULT_FLY_OFFSET + fly_offset ;
}
gameLocal . clip . Translation ( trace , origin , end , physicsObj . GetClipModel ( ) , mat3_identity , MASK_MONSTERSOLID , this ) ;
vel + = Seek ( vel , origin , trace . endpos , AI_SEEK_PREDICTION ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : FlySeekGoal
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : FlySeekGoal ( idVec3 & vel , idVec3 & goalPos ) {
idVec3 seekVel ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
// seek the goal position
seekVel = Seek ( vel , physicsObj . GetOrigin ( ) , goalPos , AI_SEEK_PREDICTION ) ;
seekVel * = fly_seek_scale ;
vel + = seekVel ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : AdjustFlySpeed
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : AdjustFlySpeed ( idVec3 & vel ) {
float speed ;
// apply dampening
vel - = vel * AI_FLY_DAMPENING * MS2SEC ( gameLocal . msec ) ;
// gradually speed up/slow down to desired speed
speed = vel . Normalize ( ) ;
speed + = ( move . speed - speed ) * MS2SEC ( gameLocal . msec ) ;
if ( speed < 0.0f ) {
speed = 0.0f ;
} else if ( move . speed & & ( speed > move . speed ) ) {
speed = move . speed ;
}
vel * = speed ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : FlyTurn
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : FlyTurn ( void ) {
if ( move . moveCommand = = MOVE_FACE_ENEMY ) {
TurnToward ( lastVisibleEnemyPos ) ;
} else if ( ( move . moveCommand = = MOVE_FACE_ENTITY ) & & move . goalEntity . GetEntity ( ) ) {
TurnToward ( move . goalEntity . GetEntity ( ) - > GetPhysics ( ) - > GetOrigin ( ) ) ;
} else if ( move . speed > 0.0f ) {
const idVec3 & vel = physicsObj . GetLinearVelocity ( ) ;
if ( vel . ToVec2 ( ) . LengthSqr ( ) > 0.1f ) {
TurnToward ( vel . ToYaw ( ) ) ;
}
}
Turn ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : FlyMove
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : FlyMove ( void ) {
idVec3 goalPos ;
idVec3 oldorigin ;
idVec3 newDest ;
AI_BLOCKED = false ;
if ( ( move . moveCommand ! = MOVE_NONE ) & & ReachedPos ( move . moveDest , move . moveCommand ) ) {
StopMove ( MOVE_STATUS_DONE ) ;
}
if ( ai_debugMove . GetBool ( ) ) {
gameLocal . Printf ( " %d: %s: %s, vel = %.2f, sp = %.2f, maxsp = %.2f \n " , gameLocal . time , name . c_str ( ) , moveCommandString [ move . moveCommand ] , physicsObj . GetLinearVelocity ( ) . Length ( ) , move . speed , fly_speed ) ;
}
if ( move . moveCommand ! = MOVE_TO_POSITION_DIRECT ) {
idVec3 vel = physicsObj . GetLinearVelocity ( ) ;
if ( GetMovePos ( goalPos ) ) {
CheckObstacleAvoidance ( goalPos , newDest ) ;
goalPos = newDest ;
}
if ( move . speed ) {
FlySeekGoal ( vel , goalPos ) ;
}
// add in bobbing
AddFlyBob ( vel ) ;
if ( enemy . GetEntity ( ) & & ( move . moveCommand ! = MOVE_TO_POSITION ) ) {
AdjustFlyHeight ( vel , goalPos ) ;
}
AdjustFlySpeed ( vel ) ;
physicsObj . SetLinearVelocity ( vel ) ;
}
// turn
FlyTurn ( ) ;
// run the physics for this frame
oldorigin = physicsObj . GetOrigin ( ) ;
physicsObj . UseFlyMove ( true ) ;
physicsObj . UseVelocityMove ( false ) ;
physicsObj . SetDelta ( vec3_zero ) ;
physicsObj . ForceDeltaMove ( disableGravity ) ;
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
monsterMoveResult_t moveResult = physicsObj . GetMoveResult ( ) ;
if ( ! af_push_moveables & & attack . Length ( ) & & TestMelee ( ) ) {
DirectDamage ( attack , enemy . GetEntity ( ) ) ;
} else {
idEntity * blockEnt = physicsObj . GetSlideMoveEntity ( ) ;
if ( blockEnt & & blockEnt - > IsType ( idMoveable : : Type ) & & blockEnt - > GetPhysics ( ) - > IsPushable ( ) ) {
KickObstacles ( viewAxis [ 0 ] , kickForce , blockEnt ) ;
} else if ( moveResult = = MM_BLOCKED ) {
move . blockTime = gameLocal . time + 500 ;
AI_BLOCKED = true ;
}
}
idVec3 org = physicsObj . GetOrigin ( ) ;
if ( oldorigin ! = org ) {
TouchTriggers ( ) ;
}
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorCyan , oldorigin , physicsObj . GetOrigin ( ) , 4000 ) ;
gameRenderWorld - > DebugBounds ( colorOrange , physicsObj . GetBounds ( ) , org , gameLocal . msec ) ;
gameRenderWorld - > DebugBounds ( colorMagenta , physicsObj . GetBounds ( ) , move . moveDest , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorRed , org , org + physicsObj . GetLinearVelocity ( ) , gameLocal . msec , true ) ;
gameRenderWorld - > DebugLine ( colorBlue , org , goalPos , gameLocal . msec , true ) ;
gameRenderWorld - > DebugLine ( colorYellow , org + EyeOffset ( ) , org + EyeOffset ( ) + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 16.0f , gameLocal . msec , true ) ;
DrawRoute ( ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : StaticMove
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : StaticMove ( void ) {
idActor * enemyEnt = enemy . GetEntity ( ) ;
if ( AI_DEAD ) {
return ;
}
if ( ( move . moveCommand = = MOVE_FACE_ENEMY ) & & enemyEnt ) {
TurnToward ( lastVisibleEnemyPos ) ;
} else if ( ( move . moveCommand = = MOVE_FACE_ENTITY ) & & move . goalEntity . GetEntity ( ) ) {
TurnToward ( move . goalEntity . GetEntity ( ) - > GetPhysics ( ) - > GetOrigin ( ) ) ;
} else if ( move . moveCommand ! = MOVE_NONE ) {
TurnToward ( move . moveDest ) ;
}
Turn ( ) ;
physicsObj . ForceDeltaMove ( true ) ; // disable gravity
2018-08-26 21:52:13 +00:00
RunPhysicsWrapper ( ) ; //ivan - was: RunPhysics();
2011-11-22 21:28:15 +00:00
AI_ONGROUND = false ;
if ( ! af_push_moveables & & attack . Length ( ) & & TestMelee ( ) ) {
DirectDamage ( attack , enemyEnt ) ;
}
if ( ai_debugMove . GetBool ( ) ) {
const idVec3 & org = physicsObj . GetOrigin ( ) ;
gameRenderWorld - > DebugBounds ( colorMagenta , physicsObj . GetBounds ( ) , org , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorBlue , org , move . moveDest , gameLocal . msec , true ) ;
gameRenderWorld - > DebugLine ( colorYellow , org + EyeOffset ( ) , org + EyeOffset ( ) + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 16.0f , gameLocal . msec , true ) ;
}
}
/***********************************************************************
Damage
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : ReactionTo
= = = = = = = = = = = = = = = = = = = = =
*/
int idAI : : ReactionTo ( const idEntity * ent ) {
if ( ent - > fl . hidden ) {
// ignore hidden entities
return ATTACK_IGNORE ;
}
if ( ! ent - > IsType ( idActor : : Type ) ) {
return ATTACK_IGNORE ;
}
const idActor * actor = static_cast < const idActor * > ( ent ) ;
if ( actor - > IsType ( idPlayer : : Type ) & & static_cast < const idPlayer * > ( actor ) - > noclip ) {
// ignore players in noclip mode
return ATTACK_IGNORE ;
}
// actors on different teams will always fight each other
if ( actor - > team ! = team ) {
if ( actor - > fl . notarget ) {
// don't attack on sight when attacker is notargeted
return ATTACK_ON_DAMAGE | ATTACK_ON_ACTIVATE ;
}
return ATTACK_ON_SIGHT | ATTACK_ON_DAMAGE | ATTACK_ON_ACTIVATE ;
}
// monsters will fight when attacked by lower ranked monsters. rank 0 never fights back.
if ( rank & & ( actor - > rank < rank ) ) {
return ATTACK_ON_DAMAGE ;
}
// don't fight back
return ATTACK_IGNORE ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Pain
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : Pain ( idEntity * inflictor , idEntity * attacker , int damage , const idVec3 & dir , int location ) {
idActor * actor ;
AI_PAIN = idActor : : Pain ( inflictor , attacker , damage , dir , location ) ;
AI_DAMAGE = true ;
// force a blink
blink_time = 0 ;
// ignore damage from self
if ( attacker ! = this ) {
if ( inflictor ) {
AI_SPECIAL_DAMAGE = inflictor - > spawnArgs . GetInt ( " special_damage " ) ;
} else {
AI_SPECIAL_DAMAGE = 0 ;
}
if ( enemy . GetEntity ( ) ! = attacker & & attacker - > IsType ( idActor : : Type ) ) {
actor = ( idActor * ) attacker ;
if ( ReactionTo ( actor ) & ATTACK_ON_DAMAGE ) {
gameLocal . AlertAI ( actor ) ;
SetEnemy ( actor ) ;
}
}
}
return ( AI_PAIN ! = 0 ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SpawnParticles
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : SpawnParticles ( const char * keyName ) {
const idKeyValue * kv = spawnArgs . MatchPrefix ( keyName , NULL ) ;
while ( kv ) {
particleEmitter_t pe ;
idStr particleName = kv - > GetValue ( ) ;
if ( particleName . Length ( ) ) {
idStr jointName = kv - > GetValue ( ) ;
int dash = jointName . Find ( ' - ' ) ;
if ( dash > 0 ) {
particleName = particleName . Left ( dash ) ;
jointName = jointName . Right ( jointName . Length ( ) - dash - 1 ) ;
}
SpawnParticlesOnJoint ( pe , particleName , jointName ) ;
particles . Append ( pe ) ;
}
kv = spawnArgs . MatchPrefix ( keyName , kv ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SpawnParticlesOnJoint
= = = = = = = = = = = = = = = = = = = = =
*/
const idDeclParticle * idAI : : SpawnParticlesOnJoint ( particleEmitter_t & pe , const char * particleName , const char * jointName ) {
idVec3 origin ;
idMat3 axis ;
if ( * particleName = = ' \0 ' ) {
memset ( & pe , 0 , sizeof ( pe ) ) ;
return pe . particle ;
}
pe . joint = animator . GetJointHandle ( jointName ) ;
if ( pe . joint = = INVALID_JOINT ) {
gameLocal . Warning ( " Unknown particleJoint '%s' on '%s' " , jointName , name . c_str ( ) ) ;
pe . time = 0 ;
pe . particle = NULL ;
} else {
animator . GetJointTransform ( pe . joint , gameLocal . time , origin , axis ) ;
origin = renderEntity . origin + origin * renderEntity . axis ;
BecomeActive ( TH_UPDATEPARTICLES ) ;
if ( ! gameLocal . time ) {
// particles with time of 0 don't show, so set the time differently on the first frame
pe . time = 1 ;
} else {
pe . time = gameLocal . time ;
}
pe . particle = static_cast < const idDeclParticle * > ( declManager - > FindType ( DECL_PARTICLE , particleName ) ) ;
gameLocal . smokeParticles - > EmitSmoke ( pe . particle , pe . time , gameLocal . random . CRandomFloat ( ) , origin , axis ) ;
}
return pe . particle ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Killed
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Killed ( idEntity * inflictor , idEntity * attacker , int damage , const idVec3 & dir , int location ) {
idAngles ang ;
const char * modelDeath ;
// make sure the monster is activated
EndAttack ( ) ;
if ( g_debugDamage . GetBool ( ) ) {
2011-12-06 18:20:15 +00:00
gameLocal . Printf ( " Damage: joint: '%s', zone '%s' \n " , animator . GetJointName ( ( jointHandle_t ) location ) ,
2011-11-22 21:28:15 +00:00
GetDamageGroup ( location ) ) ;
}
if ( inflictor ) {
AI_SPECIAL_DAMAGE = inflictor - > spawnArgs . GetInt ( " special_damage " ) ;
} else {
AI_SPECIAL_DAMAGE = 0 ;
}
if ( AI_DEAD ) {
AI_PAIN = true ;
AI_DAMAGE = true ;
return ;
}
// stop all voice sounds
StopSound ( SND_CHANNEL_VOICE , false ) ;
if ( head . GetEntity ( ) ) {
head . GetEntity ( ) - > StopSound ( SND_CHANNEL_VOICE , false ) ;
head . GetEntity ( ) - > GetAnimator ( ) - > ClearAllAnims ( gameLocal . time , 100 ) ;
}
disableGravity = false ;
move . moveType = MOVETYPE_DEAD ;
af_push_moveables = false ;
physicsObj . UseFlyMove ( false ) ;
physicsObj . ForceDeltaMove ( false ) ;
// end our looping ambient sound
StopSound ( SND_CHANNEL_AMBIENT , false ) ;
if ( attacker & & attacker - > IsType ( idActor : : Type ) ) {
gameLocal . AlertAI ( ( idActor * ) attacker ) ;
}
// activate targets
ActivateTargets ( attacker ) ;
RemoveAttachments ( ) ;
RemoveProjectile ( ) ;
StopMove ( MOVE_STATUS_DONE ) ;
ClearEnemy ( ) ;
AI_DEAD = true ;
// make monster nonsolid
physicsObj . SetContents ( 0 ) ;
physicsObj . GetClipModel ( ) - > Unlink ( ) ;
2021-12-19 04:51:02 +00:00
//rev 2021 bullets pass through corpses because it causes a bottleneck in 2d gameplay. dead bodies block bullets!
combatModel - > SetContents ( 0 ) ;
2011-11-22 21:28:15 +00:00
Unbind ( ) ;
if ( StartRagdoll ( ) ) {
StartSound ( " snd_death " , SND_CHANNEL_VOICE , 0 , false , NULL ) ;
}
if ( spawnArgs . GetString ( " model_death " , " " , & modelDeath ) ) {
// lost soul is only case that does not use a ragdoll and has a model_death so get the death sound in here
StartSound ( " snd_death " , SND_CHANNEL_VOICE , 0 , false , NULL ) ;
renderEntity . shaderParms [ SHADERPARM_TIMEOFFSET ] = - MS2SEC ( gameLocal . time ) ;
SetModel ( modelDeath ) ;
physicsObj . SetLinearVelocity ( vec3_zero ) ;
physicsObj . PutToRest ( ) ;
physicsObj . DisableImpact ( ) ;
}
restartParticles = false ;
state = GetScriptFunction ( " state_Killed " ) ;
SetState ( state ) ;
SetWaitState ( " " ) ;
const idKeyValue * kv = spawnArgs . MatchPrefix ( " def_drops " , NULL ) ;
while ( kv ) {
idDict args ;
args . Set ( " classname " , kv - > GetValue ( ) ) ;
args . Set ( " origin " , physicsObj . GetOrigin ( ) . ToString ( ) ) ;
gameLocal . SpawnEntityDef ( args ) ;
kv = spawnArgs . MatchPrefix ( " def_drops " , kv ) ;
}
2018-08-26 21:52:13 +00:00
if ( attacker & & attacker - > IsType ( idPlayer : : Type ) ) {
idPlayer * player = static_cast < idPlayer * > ( attacker ) ;
if ( inflictor & & ! inflictor - > IsType ( idSoulCubeMissile : : Type ) ) {
player - > AddAIKill ( ) ;
}
//ivan start - add score only if player is the killer
player - > AddScore ( spawnArgs . GetInt ( " score " , " 0 " ) ) ;
//ivan end
}
//ivan start - upd the global counter regardless who is the killer
if ( ! spawnArgs . GetBool ( " noStats " , " 0 " ) & & ( spawnArgs . GetInt ( " team " , " 1 " ) ! = 0 ) ) {
gameLocal . enemies_killed_counter + + ;
2011-11-22 21:28:15 +00:00
}
2018-08-26 21:52:13 +00:00
//ivan end
2011-11-22 21:28:15 +00:00
}
/***********************************************************************
Targeting / Combat
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : PlayCinematic
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : PlayCinematic ( void ) {
const char * animname ;
if ( current_cinematic > = num_cinematics ) {
if ( g_debugCinematic . GetBool ( ) ) {
gameLocal . Printf ( " %d: '%s' stop \n " , gameLocal . framenum , GetName ( ) ) ;
}
if ( ! spawnArgs . GetBool ( " cinematic_no_hide " ) ) {
Hide ( ) ;
}
current_cinematic = 0 ;
ActivateTargets ( gameLocal . GetLocalPlayer ( ) ) ;
fl . neverDormant = false ;
return ;
}
Show ( ) ;
current_cinematic + + ;
allowJointMod = false ;
allowEyeFocus = false ;
spawnArgs . GetString ( va ( " anim%d " , current_cinematic ) , NULL , & animname ) ;
if ( ! animname ) {
gameLocal . Warning ( " missing 'anim%d' key on %s " , current_cinematic , name . c_str ( ) ) ;
return ;
}
if ( g_debugCinematic . GetBool ( ) ) {
gameLocal . Printf ( " %d: '%s' start '%s' \n " , gameLocal . framenum , GetName ( ) , animname ) ;
}
headAnim . animBlendFrames = 0 ;
headAnim . lastAnimBlendFrames = 0 ;
headAnim . BecomeIdle ( ) ;
legsAnim . animBlendFrames = 0 ;
legsAnim . lastAnimBlendFrames = 0 ;
legsAnim . BecomeIdle ( ) ;
torsoAnim . animBlendFrames = 0 ;
torsoAnim . lastAnimBlendFrames = 0 ;
ProcessEvent ( & AI_PlayAnim , ANIMCHANNEL_TORSO , animname ) ;
// make sure our model gets updated
animator . ForceUpdate ( ) ;
// update the anim bounds
UpdateAnimation ( ) ;
UpdateVisuals ( ) ;
Present ( ) ;
if ( head . GetEntity ( ) ) {
// since the body anim was updated, we need to run physics to update the position of the head
RunPhysics ( ) ;
// make sure our model gets updated
head . GetEntity ( ) - > GetAnimator ( ) - > ForceUpdate ( ) ;
// update the anim bounds
head . GetEntity ( ) - > UpdateAnimation ( ) ;
head . GetEntity ( ) - > UpdateVisuals ( ) ;
head . GetEntity ( ) - > Present ( ) ;
}
fl . neverDormant = true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : Activate
Notifies the script that a monster has been activated by a trigger or flashlight
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : Activate ( idEntity * activator ) {
idPlayer * player ;
if ( AI_DEAD ) {
// ignore it when they're dead
return ;
}
// make sure he's not dormant
dormantStart = 0 ;
if ( num_cinematics ) {
PlayCinematic ( ) ;
} else {
AI_ACTIVATED = true ;
if ( ! activator | | ! activator - > IsType ( idPlayer : : Type ) ) {
player = gameLocal . GetLocalPlayer ( ) ;
} else {
player = static_cast < idPlayer * > ( activator ) ;
}
if ( ReactionTo ( player ) & ATTACK_ON_ACTIVATE ) {
SetEnemy ( player ) ;
}
// update the script in cinematics so that entities don't start anims or show themselves a frame late.
if ( cinematic ) {
2011-12-06 18:20:15 +00:00
UpdateAIScript ( ) ;
2011-11-22 21:28:15 +00:00
// make sure our model gets updated
animator . ForceUpdate ( ) ;
// update the anim bounds
UpdateAnimation ( ) ;
UpdateVisuals ( ) ;
Present ( ) ;
if ( head . GetEntity ( ) ) {
// since the body anim was updated, we need to run physics to update the position of the head
RunPhysics ( ) ;
// make sure our model gets updated
head . GetEntity ( ) - > GetAnimator ( ) - > ForceUpdate ( ) ;
// update the anim bounds
head . GetEntity ( ) - > UpdateAnimation ( ) ;
head . GetEntity ( ) - > UpdateVisuals ( ) ;
head . GetEntity ( ) - > Present ( ) ;
}
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : EnemyDead
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : EnemyDead ( void ) {
ClearEnemy ( ) ;
AI_ENEMY_DEAD = true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TalkTo
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : TalkTo ( idActor * actor ) {
if ( talk_state ! = TALK_OK ) {
return ;
}
talkTarget = actor ;
if ( actor ) {
AI_TALK = true ;
} else {
AI_TALK = false ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : GetEnemy
= = = = = = = = = = = = = = = = = = = = =
*/
idActor * idAI : : GetEnemy ( void ) const {
return enemy . GetEntity ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : GetTalkState
= = = = = = = = = = = = = = = = = = = = =
*/
talkState_t idAI : : GetTalkState ( void ) const {
if ( ( talk_state ! = TALK_NEVER ) & & AI_DEAD ) {
return TALK_DEAD ;
}
if ( IsHidden ( ) ) {
return TALK_NEVER ;
}
return talk_state ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TouchedByFlashlight
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : TouchedByFlashlight ( idActor * flashlight_owner ) {
if ( wakeOnFlashlight ) {
Activate ( flashlight_owner ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : ClearEnemy
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : ClearEnemy ( void ) {
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
StopMove ( MOVE_STATUS_DEST_NOT_FOUND ) ;
}
enemyNode . Remove ( ) ;
enemy = NULL ;
AI_ENEMY_IN_FOV = false ;
AI_ENEMY_VISIBLE = false ;
AI_ENEMY_DEAD = true ;
SetChatSound ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : EnemyPositionValid
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : EnemyPositionValid ( void ) const {
trace_t tr ;
idVec3 muzzle ;
idMat3 axis ;
if ( ! enemy . GetEntity ( ) ) {
return false ;
}
if ( AI_ENEMY_VISIBLE ) {
return true ;
}
gameLocal . clip . TracePoint ( tr , GetEyePosition ( ) , lastVisibleEnemyPos + lastVisibleEnemyEyeOffset , MASK_OPAQUE , this ) ;
if ( tr . fraction < 1.0f ) {
// can't see the area yet, so don't know if he's there or not
return true ;
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SetEnemyPosition
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : SetEnemyPosition ( void ) {
idActor * enemyEnt = enemy . GetEntity ( ) ;
int enemyAreaNum ;
int areaNum ;
int lastVisibleReachableEnemyAreaNum ;
aasPath_t path ;
idVec3 pos ;
bool onGround ;
if ( ! enemyEnt ) {
return ;
}
lastVisibleReachableEnemyPos = lastReachableEnemyPos ;
lastVisibleEnemyEyeOffset = enemyEnt - > EyeOffset ( ) ;
lastVisibleEnemyPos = enemyEnt - > GetPhysics ( ) - > GetOrigin ( ) ;
2018-08-26 21:52:13 +00:00
2011-11-22 21:28:15 +00:00
if ( move . moveType = = MOVETYPE_FLY ) {
pos = lastVisibleEnemyPos ;
onGround = true ;
} else {
onGround = enemyEnt - > GetFloorPos ( 64.0f , pos ) ;
if ( enemyEnt - > OnLadder ( ) ) {
onGround = false ;
}
}
if ( ! onGround ) {
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
AI_DEST_UNREACHABLE = true ;
}
return ;
}
// when we don't have an AAS, we can't tell if an enemy is reachable or not,
// so just assume that he is.
if ( ! aas ) {
lastVisibleReachableEnemyPos = lastVisibleEnemyPos ;
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
AI_DEST_UNREACHABLE = false ;
}
enemyAreaNum = 0 ;
areaNum = 0 ;
} else {
lastVisibleReachableEnemyAreaNum = move . toAreaNum ;
enemyAreaNum = PointReachableAreaNum ( lastVisibleEnemyPos , 1.0f ) ;
if ( ! enemyAreaNum ) {
enemyAreaNum = PointReachableAreaNum ( lastReachableEnemyPos , 1.0f ) ;
pos = lastReachableEnemyPos ;
}
if ( ! enemyAreaNum ) {
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
AI_DEST_UNREACHABLE = true ;
}
areaNum = 0 ;
} else {
const idVec3 & org = physicsObj . GetOrigin ( ) ;
areaNum = PointReachableAreaNum ( org ) ;
if ( PathToGoal ( path , areaNum , org , enemyAreaNum , pos ) ) {
lastVisibleReachableEnemyPos = pos ;
lastVisibleReachableEnemyAreaNum = enemyAreaNum ;
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
AI_DEST_UNREACHABLE = false ;
}
} else if ( move . moveCommand = = MOVE_TO_ENEMY ) {
AI_DEST_UNREACHABLE = true ;
}
}
}
if ( move . moveCommand = = MOVE_TO_ENEMY ) {
if ( ! aas ) {
// keep the move destination up to date for wandering
move . moveDest = lastVisibleReachableEnemyPos ;
} else if ( enemyAreaNum ) {
move . toAreaNum = lastVisibleReachableEnemyAreaNum ;
move . moveDest = lastVisibleReachableEnemyPos ;
}
if ( move . moveType = = MOVETYPE_FLY ) {
predictedPath_t path ;
idVec3 end = move . moveDest ;
end . z + = enemyEnt - > EyeOffset ( ) . z + fly_offset ;
idAI : : PredictPath ( this , aas , move . moveDest , end - move . moveDest , 1000 , 1000 , SE_BLOCKED , path ) ;
move . moveDest = path . endPos ;
move . toAreaNum = PointReachableAreaNum ( move . moveDest , 1.0f ) ;
}
}
}
2018-08-26 21:52:13 +00:00
//ivan start
/*
= = = = = = = = = = = = = = = =
idEntity : : UpdateRenderEntity
= = = = = = = = = = = = = = = =
bool idAI : : UpdateRenderEntity ( renderEntity_s * renderEntity , const renderView_t * renderView ) {
if ( gameLocal . inCinematic & & gameLocal . skipCinematic ) {
return false ;
}
//ivan start
modelCallBackDone = true ;
//ivan end
idAnimator * animator = GetAnimator ( ) ;
if ( animator ) {
return animator - > CreateFrame ( gameLocal . time , false ) ;
}
return false ;
}
*/
/*
= = = = = = = = = = = = = = = =
idAI : : ModelCallback
NOTE : overrides idEntity : : ModelCallback just to know if we are visible !
NOTE : this is indirectly called by UpdateEntityDef in Present , that is called by Think .
= = = = = = = = = = = = = = = =
bool idAI : : ModelCallback ( renderEntity_s * renderEntity , const renderView_t * renderView ) {
idAI * ent ;
ent = static_cast < idAI * > ( gameLocal . entities [ renderEntity - > entityNum ] ) ;
if ( ! ent ) {
gameLocal . Error ( " idAI::ModelCallback: callback with NULL game entity " ) ;
}
return ent - > UpdateRenderEntity ( renderEntity , renderView ) ;
}
*/
//not used
/*
bool idAI : : AllowSeeEnemy ( idActor * enemyEnt ) { //called after UpdateInhibitAttack
// let it see on Z axis?
//if( deltaZfromEnemy > AI_Z_DELTA_CANSEE_MAX ){
// return false;
//}
//was:
//test
if ( isXlocked & & enemyEnt - > isXlocked & & ( enemyEnt - > lockedXpos ! = lockedXpos ) ) { //both locked on different planes!
return true ;
}
if ( ! isXlocked | | updXlock ) { //if he can (or soon it will able to) follow the enemy --> don't check X delta
return ( deltaYfromEnemy < gameLocal . aiSeeDistanceY ) ;
} else { //isXlocked && !updXlock //cannot follow the enemy --> see enemy only when can attack
return ( ! AI_INHIBIT_ATTACK ) ; //if attack is not inhibited, we can see
}
}
*/
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : RunPhysicsWrapper
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : RunPhysicsWrapper ( void ) {
RunPhysics ( ) ;
//lock the X position
if ( isXlocked ) {
physicsObj . SetOrigin ( idVec3 ( fastXpos , physicsObj . GetOrigin ( ) . y , physicsObj . GetOrigin ( ) . z ) ) ;
} else {
fastXpos = physicsObj . GetOrigin ( ) . x ; //always upd so others can easily know our X pos
}
//upd is on screen
//UpdateIsOnScreen(); //NOTE: my "isOnScreen" value is useful also to other entities.
}
bool idAI : : AllowSeeActor ( idActor * actor ) {
float deltaXfromEntity ;
//one of us is out of screen
if ( ! isOnScreen | | ! actor - > isOnScreen ) {
return false ;
}
deltaXfromEntity = idMath : : Fabs ( fastXpos - actor - > fastXpos ) ;
//too far on X-axis. NOTE: if locked but cannot change, then he can see regardless X pos
if ( ( ! isXlocked | | updXlock ) & & ( deltaXfromEntity > AI_X_DELTA_CANSEE_MAX ) ) {
return false ;
}
return true ;
}
void idAI : : UpdateInhibitAttack ( idActor * enemyEnt ) {
//upd this also for other functions
deltaXfromEnemy = idMath : : Fabs ( fastXpos - enemyEnt - > fastXpos ) ;
//one of us is out of screen
if ( ! isOnScreen | | ! enemyEnt - > isOnScreen ) {
AI_INHIBIT_ATTACK = true ;
//gameLocal.Warning("%s AI_INHIBIT_ATTACK 0", GetName() );
return ;
}
//too far on X-axis. NOTE: if locked but cannot change, then he can fire regardless X pos
if ( ( ! isXlocked | | updXlock ) & & ( deltaXfromEnemy > AI_X_DELTA_CANSEE_MAX ) ) {
AI_INHIBIT_ATTACK = true ;
//gameLocal.Warning("%s AI_INHIBIT_ATTACK 1", GetName() );
return ;
}
AI_INHIBIT_ATTACK = false ;
//gameLocal.Warning("%s AI_INHIBIT_ATTACK NO", GetName() );
}
void idAI : : UpdateXlock ( idActor * enemyEnt ) {
/*
if ( deltaYfromEnemy > gameLocal . aiSeeDistanceY ) {
//we can unlock if too far!
isXlocked = false ;
return ; //don't upd if too far (outside of the screen)
}
*/
if ( updXlock ) { //Xlocked position can be updated
if ( ! enemyEnt - > updXlock ) { //never lock with someone who is not locked
isXlocked = false ;
} else if ( deltaXfromEnemy > AI_X_DELTA_LOCK_MIN ) { //too far --> unlock
isXlocked = false ;
} else if ( ! isXlocked ) {
//gameLocal.Printf("%s is now locked\n", GetName() );
isXlocked = true ;
fastXpos = enemyEnt - > fastXpos ; //we'll be pushed in place.
}
} else if ( isXlocked ) { //locked and never update --> clear enemies if cannot fight
if ( AI_INHIBIT_ATTACK ) { //if attack is inhibited, we cannot fight this enemy
ClearEnemy ( ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : UpdateIsOnScreen
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : UpdateIsOnScreen ( void ) {
if ( IsHidden ( ) ) {
isOnScreen = false ;
return ;
}
idPlayer * player = gameLocal . GetLocalPlayer ( ) ;
2021-12-19 04:51:02 +00:00
//Rev 2020 save game crash fix from DG. check needed to stop crash.
// WAS: if ( !player ) {
if ( player = = NULL | | player - > GetRenderView ( ) = = NULL ) {
2018-08-26 21:52:13 +00:00
isOnScreen = false ; //no player, no camera
return ;
}
2021-12-19 04:51:02 +00:00
//Rev 2020 End
2018-08-26 21:52:13 +00:00
const idVec3 & myPos = physicsObj . GetOrigin ( ) ;
const idVec3 & cameraPos = player - > GetRenderView ( ) - > vieworg ;
idVec3 delta ;
//ivan start - upd deltas stuff
delta = cameraPos - myPos ;
/*
delta . x = cameraPos . x - myPos . x ; //how far we are from the camera.
delta . y = idMath : : Fabs ( cameraPos . y - myPos . y ) ; //horizontal distance
delta . z = idMath : : Fabs ( cameraPos . z - myPos . z ) ; //vertical distance
*/
//X axis - how far we are from the camera
if ( delta . x > 0 ) {
//gameLocal.Printf("left\n");
isOnScreen = false ;
return ;
} else {
delta . x = - delta . x ; //use the positive value next
}
//Y axis - horizontal distance
2021-12-19 04:51:02 +00:00
if ( idMath : : Fabs ( delta . y ) > delta . x + 120 ) { //if horizontal dist is greater than camera dist, than we are out of view (Horizontal FOV 90 --> 90 45 45 triangle)
//Rev 2018 increased range by 120. this allows enemies to attack as soon as they are fully on screen.
2018-08-26 21:52:13 +00:00
isOnScreen = false ;
return ;
}
//Z axis - vertical distance
if ( delta . z > EyeHeight ( ) ) { //positive value --> camera is higher than me --> remove the eye offset
delta . z - = EyeHeight ( ) ; //like being closer
}
if ( idMath : : Fabs ( delta . z ) > delta . x * 0.75f ) { //if vertical dist is greater than camera dist*0.75, than we are out of view (Vertical FOV < 90 --> NOT 90 45 45 triangle! Use 4:3 ratio).
isOnScreen = false ;
} else {
isOnScreen = true ;
}
//Assuming FOV = 90, delta.x is also the horizontal max distance
//isOnScreen = ( (delta.y < delta.x) && (delta.z < delta.x * 0.75f) );
/*
if ( isOnScreen ) {
gameLocal . Warning ( " %s isOnScreen " , GetName ( ) ) ;
} else {
gameLocal . Warning ( " %s not isOnScreen " , GetName ( ) ) ;
}
*/
}
//ivan end
2011-11-22 21:28:15 +00:00
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : UpdateEnemyPosition
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : UpdateEnemyPosition ( void ) {
idActor * enemyEnt = enemy . GetEntity ( ) ;
int enemyAreaNum ;
int areaNum ;
aasPath_t path ;
idVec3 enemyPos ;
bool onGround ;
if ( ! enemyEnt ) {
2018-08-26 21:52:13 +00:00
AI_INHIBIT_ATTACK = true ;
2011-11-22 21:28:15 +00:00
return ;
}
const idVec3 & org = physicsObj . GetOrigin ( ) ;
if ( move . moveType = = MOVETYPE_FLY ) {
enemyPos = enemyEnt - > GetPhysics ( ) - > GetOrigin ( ) ;
onGround = true ;
} else {
onGround = enemyEnt - > GetFloorPos ( 64.0f , enemyPos ) ;
if ( enemyEnt - > OnLadder ( ) ) {
onGround = false ;
}
}
2018-08-26 21:52:13 +00:00
//ivan start - upd deltas stuff
UpdateInhibitAttack ( enemyEnt ) ; //Inhibit Attacks based on deltas
UpdateXlock ( enemyEnt ) ;
if ( ! enemy . GetEntity ( ) ) { //UpdateXlock could have cleared the enemy
AI_ENEMY_IN_FOV = false ;
AI_ENEMY_VISIBLE = false ;
return ;
}
//ivan end
2011-11-22 21:28:15 +00:00
if ( onGround ) {
// when we don't have an AAS, we can't tell if an enemy is reachable or not,
// so just assume that he is.
if ( ! aas ) {
enemyAreaNum = 0 ;
lastReachableEnemyPos = enemyPos ;
} else {
enemyAreaNum = PointReachableAreaNum ( enemyPos , 1.0f ) ;
if ( enemyAreaNum ) {
areaNum = PointReachableAreaNum ( org ) ;
if ( PathToGoal ( path , areaNum , org , enemyAreaNum , enemyPos ) ) {
lastReachableEnemyPos = enemyPos ;
}
}
}
}
AI_ENEMY_IN_FOV = false ;
AI_ENEMY_VISIBLE = false ;
2018-08-26 21:52:13 +00:00
if ( CanSee ( enemyEnt , false ) ) { //ivan - AllowSeeEnemy( enemyEnt ) &&
2011-11-22 21:28:15 +00:00
AI_ENEMY_VISIBLE = true ;
if ( CheckFOV ( enemyEnt - > GetPhysics ( ) - > GetOrigin ( ) ) ) {
AI_ENEMY_IN_FOV = true ;
}
SetEnemyPosition ( ) ;
} else {
// check if we heard any sounds in the last frame
if ( enemyEnt = = gameLocal . GetAlertEntity ( ) ) {
float dist = ( enemyEnt - > GetPhysics ( ) - > GetOrigin ( ) - org ) . LengthSqr ( ) ;
if ( dist < Square ( AI_HEARING_RANGE ) ) {
SetEnemyPosition ( ) ;
}
}
}
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugBounds ( colorLtGrey , enemyEnt - > GetPhysics ( ) - > GetBounds ( ) , lastReachableEnemyPos , gameLocal . msec ) ;
gameRenderWorld - > DebugBounds ( colorWhite , enemyEnt - > GetPhysics ( ) - > GetBounds ( ) , lastVisibleReachableEnemyPos , gameLocal . msec ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SetEnemy
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : SetEnemy ( idActor * newEnemy ) {
2018-08-26 21:52:13 +00:00
int enemyAreaNum ;
2011-11-22 21:28:15 +00:00
if ( AI_DEAD ) {
ClearEnemy ( ) ;
return ;
}
AI_ENEMY_DEAD = false ;
if ( ! newEnemy ) {
ClearEnemy ( ) ;
} else if ( enemy . GetEntity ( ) ! = newEnemy ) {
enemy = newEnemy ;
2018-08-26 21:52:13 +00:00
//ivan start - upd deltas stuff
UpdateInhibitAttack ( newEnemy ) ; //Inhibit Attacks based on deltas
UpdateXlock ( newEnemy ) ;
if ( ! enemy . GetEntity ( ) ) return ; //UpdateXlock could have cleared the enemy
//ivan end
2011-11-22 21:28:15 +00:00
enemyNode . AddToEnd ( newEnemy - > enemyList ) ;
if ( newEnemy - > health < = 0 ) {
EnemyDead ( ) ;
return ;
}
2018-08-26 21:52:13 +00:00
2011-11-22 21:28:15 +00:00
// let the monster know where the enemy is
newEnemy - > GetAASLocation ( aas , lastReachableEnemyPos , enemyAreaNum ) ;
SetEnemyPosition ( ) ;
SetChatSound ( ) ;
lastReachableEnemyPos = lastVisibleEnemyPos ;
lastVisibleReachableEnemyPos = lastReachableEnemyPos ;
enemyAreaNum = PointReachableAreaNum ( lastReachableEnemyPos , 1.0f ) ;
if ( aas & & enemyAreaNum ) {
aas - > PushPointIntoAreaNum ( enemyAreaNum , lastReachableEnemyPos ) ;
lastVisibleReachableEnemyPos = lastReachableEnemyPos ;
}
}
}
/*
= = = = = = = = = = = =
idAI : : FirstVisiblePointOnPath
= = = = = = = = = = = =
*/
idVec3 idAI : : FirstVisiblePointOnPath ( const idVec3 origin , const idVec3 & target , int travelFlags ) const {
int i , areaNum , targetAreaNum , curAreaNum , travelTime ;
idVec3 curOrigin ;
idReachability * reach ;
if ( ! aas ) {
return origin ;
}
areaNum = PointReachableAreaNum ( origin ) ;
targetAreaNum = PointReachableAreaNum ( target ) ;
if ( ! areaNum | | ! targetAreaNum ) {
return origin ;
}
if ( ( areaNum = = targetAreaNum ) | | PointVisible ( origin ) ) {
return origin ;
}
curAreaNum = areaNum ;
curOrigin = origin ;
for ( i = 0 ; i < 10 ; i + + ) {
if ( ! aas - > RouteToGoalArea ( curAreaNum , curOrigin , targetAreaNum , travelFlags , travelTime , & reach ) ) {
break ;
}
if ( ! reach ) {
return target ;
}
curAreaNum = reach - > toAreaNum ;
curOrigin = reach - > end ;
if ( PointVisible ( curOrigin ) ) {
return curOrigin ;
}
}
return origin ;
}
/*
= = = = = = = = = = = = = = = = = = =
idAI : : CalculateAttackOffsets
calculate joint positions on attack frames so we can do proper " can hit " tests
= = = = = = = = = = = = = = = = = = =
*/
void idAI : : CalculateAttackOffsets ( void ) {
const idDeclModelDef * modelDef ;
int num ;
int i ;
int frame ;
const frameCommand_t * command ;
idMat3 axis ;
const idAnim * anim ;
jointHandle_t joint ;
modelDef = animator . ModelDef ( ) ;
if ( ! modelDef ) {
return ;
}
num = modelDef - > NumAnims ( ) ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
// needs to be off while getting the offsets so that we account for the distance the monster moves in the attack anim
animator . RemoveOriginOffset ( false ) ;
// anim number 0 is reserved for non-existant anims. to avoid off by one issues, just allocate an extra spot for
// launch offsets so that anim number can be used without subtracting 1.
missileLaunchOffset . SetGranularity ( 1 ) ;
missileLaunchOffset . SetNum ( num + 1 ) ;
missileLaunchOffset [ 0 ] . Zero ( ) ;
for ( i = 1 ; i < = num ; i + + ) {
missileLaunchOffset [ i ] . Zero ( ) ;
anim = modelDef - > GetAnim ( i ) ;
if ( anim ) {
frame = anim - > FindFrameForFrameCommand ( FC_LAUNCHMISSILE , & command ) ;
if ( frame > = 0 ) {
joint = animator . GetJointHandle ( command - > string - > c_str ( ) ) ;
if ( joint = = INVALID_JOINT ) {
gameLocal . Error ( " Invalid joint '%s' on 'launch_missile' frame command on frame %d of model '%s' " , command - > string - > c_str ( ) , frame , modelDef - > GetName ( ) ) ;
}
GetJointTransformForAnim ( joint , i , FRAME2MS ( frame ) , missileLaunchOffset [ i ] , axis ) ;
}
}
}
animator . RemoveOriginOffset ( true ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : CreateProjectileClipModel
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : CreateProjectileClipModel ( void ) const {
if ( projectileClipModel = = NULL ) {
idBounds projectileBounds ( vec3_origin ) ;
projectileBounds . ExpandSelf ( projectileRadius ) ;
projectileClipModel = new idClipModel ( idTraceModel ( projectileBounds ) ) ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : GetAimDir
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : GetAimDir ( const idVec3 & firePos , idEntity * aimAtEnt , const idEntity * ignore , idVec3 & aimDir ) const {
idVec3 targetPos1 ;
idVec3 targetPos2 ;
idVec3 delta ;
float max_height ;
bool result ;
// if no aimAtEnt or projectile set
if ( ! aimAtEnt | | ! projectileDef ) {
aimDir = viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) ;
return false ;
}
if ( projectileClipModel = = NULL ) {
CreateProjectileClipModel ( ) ;
}
if ( aimAtEnt = = enemy . GetEntity ( ) ) {
static_cast < idActor * > ( aimAtEnt ) - > GetAIAimTargets ( lastVisibleEnemyPos , targetPos1 , targetPos2 ) ;
} else if ( aimAtEnt - > IsType ( idActor : : Type ) ) {
static_cast < idActor * > ( aimAtEnt ) - > GetAIAimTargets ( aimAtEnt - > GetPhysics ( ) - > GetOrigin ( ) , targetPos1 , targetPos2 ) ;
} else {
targetPos1 = aimAtEnt - > GetPhysics ( ) - > GetAbsBounds ( ) . GetCenter ( ) ;
targetPos2 = targetPos1 ;
}
// try aiming for chest
delta = firePos - targetPos1 ;
max_height = delta . LengthFast ( ) * projectile_height_to_distance_ratio ;
result = PredictTrajectory ( firePos , targetPos1 , projectileSpeed , projectileGravity , projectileClipModel , MASK_SHOT_RENDERMODEL , max_height , ignore , aimAtEnt , ai_debugTrajectory . GetBool ( ) ? 1000 : 0 , aimDir ) ;
if ( result | | ! aimAtEnt - > IsType ( idActor : : Type ) ) {
return result ;
}
// try aiming for head
delta = firePos - targetPos2 ;
max_height = delta . LengthFast ( ) * projectile_height_to_distance_ratio ;
result = PredictTrajectory ( firePos , targetPos2 , projectileSpeed , projectileGravity , projectileClipModel , MASK_SHOT_RENDERMODEL , max_height , ignore , aimAtEnt , ai_debugTrajectory . GetBool ( ) ? 1000 : 0 , aimDir ) ;
return result ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : BeginAttack
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : BeginAttack ( const char * name ) {
attack = name ;
lastAttackTime = gameLocal . time ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : EndAttack
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : EndAttack ( void ) {
attack = " " ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : CreateProjectile
= = = = = = = = = = = = = = = = = = = = =
*/
idProjectile * idAI : : CreateProjectile ( const idVec3 & pos , const idVec3 & dir ) {
idEntity * ent ;
const char * clsname ;
if ( ! projectile . GetEntity ( ) ) {
gameLocal . SpawnEntityDef ( * projectileDef , & ent , false ) ;
if ( ! ent ) {
clsname = projectileDef - > GetString ( " classname " ) ;
gameLocal . Error ( " Could not spawn entityDef '%s' " , clsname ) ;
}
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( ! ent - > IsType ( idProjectile : : Type ) ) {
clsname = ent - > GetClassname ( ) ;
gameLocal . Error ( " '%s' is not an idProjectile " , clsname ) ;
}
projectile = ( idProjectile * ) ent ;
}
projectile . GetEntity ( ) - > Create ( this , pos , dir ) ;
return projectile . GetEntity ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : RemoveProjectile
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : RemoveProjectile ( void ) {
if ( projectile . GetEntity ( ) ) {
projectile . GetEntity ( ) - > PostEventMS ( & EV_Remove , 0 ) ;
projectile = NULL ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : LaunchProjectile
= = = = = = = = = = = = = = = = = = = = =
*/
idProjectile * idAI : : LaunchProjectile ( const char * jointname , idEntity * target , bool clampToAttackCone ) {
idVec3 muzzle ;
idVec3 dir ;
idVec3 start ;
trace_t tr ;
idBounds projBounds ;
float distance ;
const idClipModel * projClip ;
float attack_accuracy ;
float attack_cone ;
float projectile_spread ;
float diff ;
2018-08-26 21:55:54 +00:00
float angle = 0.0f ;
2011-11-22 21:28:15 +00:00
float spin ;
idAngles ang ;
int num_projectiles ;
int i ;
idMat3 axis ;
idVec3 tmp ;
idProjectile * lastProjectile ;
2021-12-19 04:51:02 +00:00
int noAim ; //rev 2019
2011-11-22 21:28:15 +00:00
if ( ! projectileDef ) {
gameLocal . Warning ( " %s (%s) doesn't have a projectile specified " , name . c_str ( ) , GetEntityDefName ( ) ) ;
return NULL ;
}
attack_accuracy = spawnArgs . GetFloat ( " attack_accuracy " , " 7 " ) ;
attack_cone = spawnArgs . GetFloat ( " attack_cone " , " 70 " ) ;
projectile_spread = spawnArgs . GetFloat ( " projectile_spread " , " 0 " ) ;
num_projectiles = spawnArgs . GetInt ( " num_projectiles " , " 1 " ) ;
GetMuzzle ( jointname , muzzle , axis ) ;
if ( ! projectile . GetEntity ( ) ) {
CreateProjectile ( muzzle , axis [ 0 ] ) ;
}
lastProjectile = projectile . GetEntity ( ) ;
2021-12-19 04:51:02 +00:00
//rev 2019 start. make the ai shoot only shoot straight ahead when the noaim key is set to 1 in their def file
/*
2011-11-22 21:28:15 +00:00
if ( target ! = NULL ) {
tmp = target - > GetPhysics ( ) - > GetAbsBounds ( ) . GetCenter ( ) - muzzle ;
tmp . Normalize ( ) ;
axis = tmp . ToMat3 ( ) ;
} else {
axis = viewAxis ;
}
2021-12-19 04:51:02 +00:00
*/
noAim = spawnArgs . GetInt ( " noaim " ) ;
if ( noAim > 0 ) {
if ( target = NULL ) {
tmp = target - > GetPhysics ( ) - > GetAbsBounds ( ) . GetCenter ( ) - muzzle ;
tmp . Normalize ( ) ;
axis = tmp . ToMat3 ( ) ;
} else {
axis = viewAxis ;
}
}
if ( noAim < 1 ) {
if ( target ! = NULL ) {
tmp = target - > GetPhysics ( ) - > GetAbsBounds ( ) . GetCenter ( ) - muzzle ;
tmp . Normalize ( ) ;
axis = tmp . ToMat3 ( ) ;
} else {
axis = viewAxis ;
}
}
//rev 2019 noaim end
2011-11-22 21:28:15 +00:00
// rotate it because the cone points up by default
tmp = axis [ 2 ] ;
axis [ 2 ] = axis [ 0 ] ;
axis [ 0 ] = - tmp ;
// make sure the projectile starts inside the monster bounding box
const idBounds & ownerBounds = physicsObj . GetAbsBounds ( ) ;
projClip = lastProjectile - > GetPhysics ( ) - > GetClipModel ( ) ;
projBounds = projClip - > GetBounds ( ) . Rotate ( axis ) ;
// check if the owner bounds is bigger than the projectile bounds
if ( ( ( ownerBounds [ 1 ] [ 0 ] - ownerBounds [ 0 ] [ 0 ] ) > ( projBounds [ 1 ] [ 0 ] - projBounds [ 0 ] [ 0 ] ) ) & &
( ( ownerBounds [ 1 ] [ 1 ] - ownerBounds [ 0 ] [ 1 ] ) > ( projBounds [ 1 ] [ 1 ] - projBounds [ 0 ] [ 1 ] ) ) & &
( ( ownerBounds [ 1 ] [ 2 ] - ownerBounds [ 0 ] [ 2 ] ) > ( projBounds [ 1 ] [ 2 ] - projBounds [ 0 ] [ 2 ] ) ) ) {
if ( ( ownerBounds - projBounds ) . RayIntersection ( muzzle , viewAxis [ 0 ] , distance ) ) {
start = muzzle + distance * viewAxis [ 0 ] ;
} else {
start = ownerBounds . GetCenter ( ) ;
}
} else {
// projectile bounds bigger than the owner bounds, so just start it from the center
start = ownerBounds . GetCenter ( ) ;
}
gameLocal . clip . Translation ( tr , start , muzzle , projClip , axis , MASK_SHOT_RENDERMODEL , this ) ;
muzzle = tr . endpos ;
// set aiming direction
GetAimDir ( muzzle , target , this , dir ) ;
ang = dir . ToAngles ( ) ;
// adjust his aim so it's not perfect. uses sine based movement so the tracers appear less random in their spread.
float t = MS2SEC ( gameLocal . time + entityNumber * 497 ) ;
ang . pitch + = idMath : : Sin16 ( t * 5.1 ) * attack_accuracy ;
ang . yaw + = idMath : : Sin16 ( t * 6.7 ) * attack_accuracy ;
if ( clampToAttackCone ) {
// clamp the attack direction to be within monster's attack cone so he doesn't do
// things like throw the missile backwards if you're behind him
diff = idMath : : AngleDelta ( ang . yaw , current_yaw ) ;
if ( diff > attack_cone ) {
ang . yaw = current_yaw + attack_cone ;
} else if ( diff < - attack_cone ) {
ang . yaw = current_yaw - attack_cone ;
}
}
axis = ang . ToMat3 ( ) ;
2018-08-26 21:52:13 +00:00
//ivan start - fire modes - setup
int firemodeCounter = 0 ;
int firemodeCounterPos = 0 ;
idVec3 updown_offset ;
updown_offset . Zero ( ) ;
if ( fireMode = = AI_FIREMODE_2D_STEP_SPREAD ) {
if ( num_projectiles > 1 ) { //Example: spread = 90, num projs = 5
angle = 2 * projectile_spread / ( num_projectiles - 1 ) ; //Spread step: 2* 90 /(5-1) = 45 degrees
angle = angle / 180.0f ; //normalized from 0 to 1: 45/180 = 0.5 --> % of 180 degrees to use: 0, 0.25, -0.25, 0.5, -0.5
}
}
//ivan end
2011-11-22 21:28:15 +00:00
float spreadRad = DEG2RAD ( projectile_spread ) ;
for ( i = 0 ; i < num_projectiles ; i + + ) {
2018-08-26 21:52:13 +00:00
//ivan start - fire modes - direction
if ( fireMode = = AI_FIREMODE_DEFAULT ) {
// spread the projectiles out
angle = idMath : : Sin ( spreadRad * gameLocal . random . RandomFloat ( ) ) ;
spin = ( float ) DEG2RAD ( 360.0f ) * gameLocal . random . RandomFloat ( ) ;
dir = axis [ 0 ] + axis [ 2 ] * ( angle * idMath : : Sin ( spin ) ) - axis [ 1 ] * ( angle * idMath : : Cos ( spin ) ) ;
} else if ( fireMode = = AI_FIREMODE_2D_STEP_SPREAD ) {
2018-08-26 21:55:54 +00:00
// FIXME: DG: if num_projectiles == 1, angle is not properly initialized
// (I initialized it to 0 above, but not sure that's the appropriate value)
2018-08-26 21:52:13 +00:00
dir = ( axis [ 0 ] * ( 1 - angle * firemodeCounterPos ) ) + ( axis [ 2 ] * angle * firemodeCounter ) ;
//upd counter
if ( firemodeCounter > = 0 ) {
firemodeCounter + + ;
firemodeCounterPos = firemodeCounter ;
}
firemodeCounter = - firemodeCounter ;
} else if ( fireMode = = AI_FIREMODE_2D_PARALLEL_SPREAD ) {
dir = axis [ 0 ] ;
updown_offset = axis [ 2 ] * projectile_spread * firemodeCounter ;
//upd counter
if ( firemodeCounter > = 0 ) {
firemodeCounter + + ;
}
firemodeCounter = - firemodeCounter ;
} else if ( fireMode = = AI_FIREMODE_2D_RANDOM_SPREAD ) {
// spread the projectiles out
angle = idMath : : Sin ( spreadRad * gameLocal . random . RandomFloat ( ) ) ;
spin = ( float ) DEG2RAD ( 360.0f ) * gameLocal . random . RandomFloat ( ) ;
dir = axis [ 0 ] + axis [ 2 ] * ( angle * idMath : : Sin ( spin ) ) ;
} else {
gameLocal . Error ( " Unknown AI fire mode: %d " , fireMode ) ;
}
//ivan end
2011-11-22 21:28:15 +00:00
dir . Normalize ( ) ;
// launch the projectile
if ( ! projectile . GetEntity ( ) ) {
CreateProjectile ( muzzle , dir ) ;
}
lastProjectile = projectile . GetEntity ( ) ;
2018-08-26 21:52:13 +00:00
//ivan start - fire modes - postion offset
//lastProjectile->Launch( muzzle, dir, vec3_origin );
lastProjectile - > Launch ( muzzle + updown_offset , dir , vec3_origin ) ;
//ivan test end
2011-11-22 21:28:15 +00:00
projectile = NULL ;
}
TriggerWeaponEffects ( muzzle ) ;
lastAttackTime = gameLocal . time ;
return lastProjectile ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : DamageFeedback
callback function for when another entity received damage from this entity . damage can be adjusted and returned to the caller .
FIXME : This gets called when we call idPlayer : : CalcDamagePoints from idAI : : AttackMelee , which then checks for a saving throw ,
possibly forcing a miss . This is harmless behavior ATM , but is not intuitive .
= = = = = = = = = = = = = = = =
*/
void idAI : : DamageFeedback ( idEntity * victim , idEntity * inflictor , int & damage ) {
if ( ( victim = = this ) & & inflictor - > IsType ( idProjectile : : Type ) ) {
// monsters only get half damage from their own projectiles
damage = ( damage + 1 ) / 2 ; // round up so we don't do 0 damage
} else if ( victim = = enemy . GetEntity ( ) ) {
AI_HIT_ENEMY = true ;
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : DirectDamage
Causes direct damage to an entity
kickDir is specified in the monster ' s coordinate system , and gives the direction
that the view kick and knockback should go
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : DirectDamage ( const char * meleeDefName , idEntity * ent ) {
const idDict * meleeDef ;
const char * p ;
const idSoundShader * shader ;
meleeDef = gameLocal . FindEntityDefDict ( meleeDefName , false ) ;
if ( ! meleeDef ) {
gameLocal . Error ( " Unknown damage def '%s' on '%s' " , meleeDefName , name . c_str ( ) ) ;
}
if ( ! ent - > fl . takedamage ) {
const idSoundShader * shader = declManager - > FindSound ( meleeDef - > GetString ( " snd_miss " ) ) ;
StartSoundShader ( shader , SND_CHANNEL_DAMAGE , 0 , false , NULL ) ;
return ;
}
//
// do the damage
//
p = meleeDef - > GetString ( " snd_hit " ) ;
if ( p & & * p ) {
shader = declManager - > FindSound ( p ) ;
StartSoundShader ( shader , SND_CHANNEL_DAMAGE , 0 , false , NULL ) ;
}
idVec3 kickDir ;
meleeDef - > GetVector ( " kickDir " , " 0 0 0 " , kickDir ) ;
idVec3 globalKickDir ;
globalKickDir = ( viewAxis * physicsObj . GetGravityAxis ( ) ) * kickDir ;
ent - > Damage ( this , this , globalKickDir , meleeDefName , 1.0f , INVALID_JOINT ) ;
// end the attack if we're a multiframe attack
EndAttack ( ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TestMelee
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : TestMelee ( void ) const {
trace_t trace ;
idActor * enemyEnt = enemy . GetEntity ( ) ;
if ( ! enemyEnt | | ! melee_range ) {
return false ;
}
//FIXME: make work with gravity vector
idVec3 org = physicsObj . GetOrigin ( ) ;
const idBounds & myBounds = physicsObj . GetBounds ( ) ;
idBounds bounds ;
// expand the bounds out by our melee range
bounds [ 0 ] [ 0 ] = - melee_range ;
bounds [ 0 ] [ 1 ] = - melee_range ;
bounds [ 0 ] [ 2 ] = myBounds [ 0 ] [ 2 ] - 4.0f ;
bounds [ 1 ] [ 0 ] = melee_range ;
bounds [ 1 ] [ 1 ] = melee_range ;
bounds [ 1 ] [ 2 ] = myBounds [ 1 ] [ 2 ] + 4.0f ;
bounds . TranslateSelf ( org ) ;
idVec3 enemyOrg = enemyEnt - > GetPhysics ( ) - > GetOrigin ( ) ;
idBounds enemyBounds = enemyEnt - > GetPhysics ( ) - > GetBounds ( ) ;
enemyBounds . TranslateSelf ( enemyOrg ) ;
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugBounds ( colorYellow , bounds , vec3_zero , gameLocal . msec ) ;
}
if ( ! bounds . IntersectsBounds ( enemyBounds ) ) {
return false ;
}
idVec3 start = GetEyePosition ( ) ;
idVec3 end = enemyEnt - > GetEyePosition ( ) ;
gameLocal . clip . TracePoint ( trace , start , end , MASK_SHOT_BOUNDINGBOX , this ) ;
if ( ( trace . fraction = = 1.0f ) | | ( gameLocal . GetTraceEntity ( trace ) = = enemyEnt ) ) {
return true ;
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : AttackMelee
jointname allows the endpoint to be exactly specified in the model ,
as for the commando tentacle . If not specified , it will be set to
the facing direction + melee_range .
kickDir is specified in the monster ' s coordinate system , and gives the direction
that the view kick and knockback should go
= = = = = = = = = = = = = = = = = = = = =
*/
bool idAI : : AttackMelee ( const char * meleeDefName ) {
const idDict * meleeDef ;
idActor * enemyEnt = enemy . GetEntity ( ) ;
const char * p ;
const idSoundShader * shader ;
meleeDef = gameLocal . FindEntityDefDict ( meleeDefName , false ) ;
if ( ! meleeDef ) {
gameLocal . Error ( " Unknown melee '%s' " , meleeDefName ) ;
}
if ( ! enemyEnt ) {
p = meleeDef - > GetString ( " snd_miss " ) ;
if ( p & & * p ) {
shader = declManager - > FindSound ( p ) ;
StartSoundShader ( shader , SND_CHANNEL_DAMAGE , 0 , false , NULL ) ;
}
return false ;
}
// check for the "saving throw" automatic melee miss on lethal blow
// stupid place for this.
bool forceMiss = false ;
if ( enemyEnt - > IsType ( idPlayer : : Type ) & & g_skill . GetInteger ( ) < 2 ) {
int damage , armor ;
idPlayer * player = static_cast < idPlayer * > ( enemyEnt ) ;
player - > CalcDamagePoints ( this , this , meleeDef , 1.0f , INVALID_JOINT , & damage , & armor ) ;
if ( enemyEnt - > health < = damage ) {
int t = gameLocal . time - player - > lastSavingThrowTime ;
if ( t > SAVING_THROW_TIME ) {
player - > lastSavingThrowTime = gameLocal . time ;
t = 0 ;
}
if ( t < 1000 ) {
gameLocal . Printf ( " Saving throw. \n " ) ;
forceMiss = true ;
}
}
}
// make sure the trace can actually hit the enemy
if ( forceMiss | | ! TestMelee ( ) ) {
// missed
p = meleeDef - > GetString ( " snd_miss " ) ;
if ( p & & * p ) {
shader = declManager - > FindSound ( p ) ;
StartSoundShader ( shader , SND_CHANNEL_DAMAGE , 0 , false , NULL ) ;
}
return false ;
}
//
// do the damage
//
p = meleeDef - > GetString ( " snd_hit " ) ;
if ( p & & * p ) {
shader = declManager - > FindSound ( p ) ;
StartSoundShader ( shader , SND_CHANNEL_DAMAGE , 0 , false , NULL ) ;
}
idVec3 kickDir ;
meleeDef - > GetVector ( " kickDir " , " 0 0 0 " , kickDir ) ;
idVec3 globalKickDir ;
globalKickDir = ( viewAxis * physicsObj . GetGravityAxis ( ) ) * kickDir ;
enemyEnt - > Damage ( this , this , globalKickDir , meleeDefName , 1.0f , INVALID_JOINT ) ;
lastAttackTime = gameLocal . time ;
return true ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : PushWithAF
= = = = = = = = = = = = = = = =
*/
void idAI : : PushWithAF ( void ) {
int i , j ;
afTouch_t touchList [ MAX_GENTITIES ] ;
idEntity * pushed_ents [ MAX_GENTITIES ] ;
idEntity * ent ;
idVec3 vel ;
int num_pushed ;
num_pushed = 0 ;
af . ChangePose ( this , gameLocal . time ) ;
int num = af . EntitiesTouchingAF ( touchList ) ;
for ( i = 0 ; i < num ; i + + ) {
if ( touchList [ i ] . touchedEnt - > IsType ( idProjectile : : Type ) ) {
// skip projectiles
continue ;
}
// make sure we havent pushed this entity already. this avoids causing double damage
for ( j = 0 ; j < num_pushed ; j + + ) {
if ( pushed_ents [ j ] = = touchList [ i ] . touchedEnt ) {
break ;
}
}
if ( j > = num_pushed ) {
ent = touchList [ i ] . touchedEnt ;
pushed_ents [ num_pushed + + ] = ent ;
vel = ent - > GetPhysics ( ) - > GetAbsBounds ( ) . GetCenter ( ) - touchList [ i ] . touchedByBody - > GetWorldOrigin ( ) ;
vel . Normalize ( ) ;
if ( attack . Length ( ) & & ent - > IsType ( idActor : : Type ) ) {
ent - > Damage ( this , this , vel , attack , 1.0f , INVALID_JOINT ) ;
} else {
ent - > GetPhysics ( ) - > SetLinearVelocity ( 100.0f * vel , touchList [ i ] . touchedClipModel - > GetId ( ) ) ;
}
}
}
}
/***********************************************************************
Misc
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = =
idAI : : GetMuzzle
= = = = = = = = = = = = = = = =
*/
void idAI : : GetMuzzle ( const char * jointname , idVec3 & muzzle , idMat3 & axis ) {
jointHandle_t joint ;
if ( ! jointname | | ! jointname [ 0 ] ) {
muzzle = physicsObj . GetOrigin ( ) + viewAxis [ 0 ] * physicsObj . GetGravityAxis ( ) * 14 ;
muzzle - = physicsObj . GetGravityNormal ( ) * physicsObj . GetBounds ( ) [ 1 ] . z * 0.5f ;
} else {
joint = animator . GetJointHandle ( jointname ) ;
if ( joint = = INVALID_JOINT ) {
gameLocal . Error ( " Unknown joint '%s' on %s " , jointname , GetEntityDefName ( ) ) ;
}
GetJointWorldTransform ( joint , gameLocal . time , muzzle , axis ) ;
}
}
/*
= = = = = = = = = = = = = = = =
idAI : : TriggerWeaponEffects
= = = = = = = = = = = = = = = =
*/
void idAI : : TriggerWeaponEffects ( const idVec3 & muzzle ) {
idVec3 org ;
idMat3 axis ;
if ( ! g_muzzleFlash . GetBool ( ) ) {
return ;
}
// muzzle flash
// offset the shader parms so muzzle flashes show up
renderEntity . shaderParms [ SHADERPARM_TIMEOFFSET ] = - MS2SEC ( gameLocal . time ) ;
renderEntity . shaderParms [ SHADERPARM_DIVERSITY ] = gameLocal . random . CRandomFloat ( ) ;
if ( flashJointWorld ! = INVALID_JOINT ) {
GetJointWorldTransform ( flashJointWorld , gameLocal . time , org , axis ) ;
if ( worldMuzzleFlash . lightRadius . x > 0.0f ) {
worldMuzzleFlash . axis = axis ;
worldMuzzleFlash . shaderParms [ SHADERPARM_TIMEOFFSET ] = - MS2SEC ( gameLocal . time ) ;
if ( worldMuzzleFlashHandle ! = - 1 ) {
gameRenderWorld - > UpdateLightDef ( worldMuzzleFlashHandle , & worldMuzzleFlash ) ;
} else {
worldMuzzleFlashHandle = gameRenderWorld - > AddLightDef ( & worldMuzzleFlash ) ;
}
muzzleFlashEnd = gameLocal . time + flashTime ;
UpdateVisuals ( ) ;
}
}
}
/*
= = = = = = = = = = = = = = = =
idAI : : UpdateMuzzleFlash
= = = = = = = = = = = = = = = =
*/
void idAI : : UpdateMuzzleFlash ( void ) {
2011-12-06 18:20:15 +00:00
if ( worldMuzzleFlashHandle ! = - 1 ) {
2011-11-22 21:28:15 +00:00
if ( gameLocal . time > = muzzleFlashEnd ) {
gameRenderWorld - > FreeLightDef ( worldMuzzleFlashHandle ) ;
worldMuzzleFlashHandle = - 1 ;
} else {
idVec3 muzzle ;
animator . GetJointTransform ( flashJointWorld , gameLocal . time , muzzle , worldMuzzleFlash . axis ) ;
animator . GetJointTransform ( flashJointWorld , gameLocal . time , muzzle , worldMuzzleFlash . axis ) ;
muzzle = physicsObj . GetOrigin ( ) + ( muzzle + modelOffset ) * viewAxis * physicsObj . GetGravityAxis ( ) ;
worldMuzzleFlash . origin = muzzle ;
gameRenderWorld - > UpdateLightDef ( worldMuzzleFlashHandle , & worldMuzzleFlash ) ;
}
}
}
/*
= = = = = = = = = = = = = = = =
idAI : : Hide
= = = = = = = = = = = = = = = =
*/
void idAI : : Hide ( void ) {
idActor : : Hide ( ) ;
fl . takedamage = false ;
physicsObj . SetContents ( 0 ) ;
physicsObj . GetClipModel ( ) - > Unlink ( ) ;
StopSound ( SND_CHANNEL_AMBIENT , false ) ;
SetChatSound ( ) ;
AI_ENEMY_IN_FOV = false ;
AI_ENEMY_VISIBLE = false ;
StopMove ( MOVE_STATUS_DONE ) ;
}
/*
= = = = = = = = = = = = = = = =
idAI : : Show
= = = = = = = = = = = = = = = =
*/
void idAI : : Show ( void ) {
idActor : : Show ( ) ;
if ( spawnArgs . GetBool ( " big_monster " ) ) {
physicsObj . SetContents ( 0 ) ;
} else if ( use_combat_bbox ) {
physicsObj . SetContents ( CONTENTS_BODY | CONTENTS_SOLID ) ;
2021-12-19 04:51:02 +00:00
}
//rev 2020
if ( ( spawnArgs . GetInt ( " team " , " 1 " ) ) & & spawnArgs . GetBool ( " team_non_solid " , " 1 " ) ) {
physicsObj . SetContents ( CONTENTS_CORPSE ) ; //the monster can pass through other monsters but player still detects touch of death
//gameLocal.Printf( "corpse" );
}
//rev 2020 end
else {
2011-11-22 21:28:15 +00:00
physicsObj . SetContents ( CONTENTS_BODY ) ;
}
physicsObj . GetClipModel ( ) - > Link ( gameLocal . clip ) ;
fl . takedamage = ! spawnArgs . GetBool ( " noDamage " ) ;
SetChatSound ( ) ;
StartSound ( " snd_ambient " , SND_CHANNEL_AMBIENT , 0 , false , NULL ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : SetChatSound
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : SetChatSound ( void ) {
const char * snd ;
if ( IsHidden ( ) ) {
snd = NULL ;
} else if ( enemy . GetEntity ( ) ) {
snd = spawnArgs . GetString ( " snd_chatter_combat " , NULL ) ;
chat_min = SEC2MS ( spawnArgs . GetFloat ( " chatter_combat_min " , " 5 " ) ) ;
chat_max = SEC2MS ( spawnArgs . GetFloat ( " chatter_combat_max " , " 10 " ) ) ;
} else if ( ! spawnArgs . GetBool ( " no_idle_chatter " ) ) {
snd = spawnArgs . GetString ( " snd_chatter " , NULL ) ;
chat_min = SEC2MS ( spawnArgs . GetFloat ( " chatter_min " , " 5 " ) ) ;
chat_max = SEC2MS ( spawnArgs . GetFloat ( " chatter_max " , " 10 " ) ) ;
} else {
snd = NULL ;
}
if ( snd & & * snd ) {
chat_snd = declManager - > FindSound ( snd ) ;
// set the next chat time
chat_time = gameLocal . time + chat_min + gameLocal . random . RandomFloat ( ) * ( chat_max - chat_min ) ;
} else {
chat_snd = NULL ;
}
}
/*
= = = = = = = = = = = = = = = =
idAI : : CanPlayChatterSounds
Used for playing chatter sounds on monsters .
= = = = = = = = = = = = = = = =
*/
bool idAI : : CanPlayChatterSounds ( void ) const {
if ( AI_DEAD ) {
return false ;
}
if ( IsHidden ( ) ) {
return false ;
}
if ( enemy . GetEntity ( ) ) {
return true ;
}
if ( spawnArgs . GetBool ( " no_idle_chatter " ) ) {
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : PlayChatter
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : PlayChatter ( void ) {
// check if it's time to play a chat sound
if ( AI_DEAD | | ! chat_snd | | ( chat_time > gameLocal . time ) ) {
return ;
}
StartSoundShader ( chat_snd , SND_CHANNEL_VOICE , 0 , false , NULL ) ;
// set the next chat time
chat_time = gameLocal . time + chat_min + gameLocal . random . RandomFloat ( ) * ( chat_max - chat_min ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : UpdateParticles
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : UpdateParticles ( void ) {
if ( ( thinkFlags & TH_UPDATEPARTICLES ) & & ! IsHidden ( ) ) {
idVec3 realVector ;
idMat3 realAxis ;
int particlesAlive = 0 ;
for ( int i = 0 ; i < particles . Num ( ) ; i + + ) {
if ( particles [ i ] . particle & & particles [ i ] . time ) {
particlesAlive + + ;
if ( af . IsActive ( ) ) {
realAxis = mat3_identity ;
realVector = GetPhysics ( ) - > GetOrigin ( ) ;
} else {
animator . GetJointTransform ( particles [ i ] . joint , gameLocal . time , realVector , realAxis ) ;
realAxis * = renderEntity . axis ;
realVector = physicsObj . GetOrigin ( ) + ( realVector + modelOffset ) * ( viewAxis * physicsObj . GetGravityAxis ( ) ) ;
}
if ( ! gameLocal . smokeParticles - > EmitSmoke ( particles [ i ] . particle , particles [ i ] . time , gameLocal . random . CRandomFloat ( ) , realVector , realAxis ) ) {
if ( restartParticles ) {
particles [ i ] . time = gameLocal . time ;
} else {
particles [ i ] . time = 0 ;
particlesAlive - - ;
}
}
}
}
if ( particlesAlive = = 0 ) {
BecomeInactive ( TH_UPDATEPARTICLES ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idAI : : TriggerParticles
= = = = = = = = = = = = = = = = = = = = =
*/
void idAI : : TriggerParticles ( const char * jointName ) {
jointHandle_t jointNum ;
jointNum = animator . GetJointHandle ( jointName ) ;
for ( int i = 0 ; i < particles . Num ( ) ; i + + ) {
if ( particles [ i ] . joint = = jointNum ) {
particles [ i ] . time = gameLocal . time ;
BecomeActive ( TH_UPDATEPARTICLES ) ;
}
}
}
/***********************************************************************
Head & torso aiming
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
= = = = = = = = = = = = = = = =
idAI : : UpdateAnimationControllers
= = = = = = = = = = = = = = = =
*/
bool idAI : : UpdateAnimationControllers ( void ) {
idVec3 local ;
idVec3 focusPos ;
idQuat jawQuat ;
idVec3 left ;
2011-12-06 18:20:15 +00:00
idVec3 dir ;
idVec3 orientationJointPos ;
idVec3 localDir ;
idAngles newLookAng ;
2011-11-22 21:28:15 +00:00
idAngles diff ;
idMat3 mat ;
idMat3 axis ;
idMat3 orientationJointAxis ;
idAFAttachment * headEnt = head . GetEntity ( ) ;
idVec3 eyepos ;
idVec3 pos ;
int i ;
idAngles jointAng ;
float orientationJointYaw ;
if ( AI_DEAD ) {
return idActor : : UpdateAnimationControllers ( ) ;
}
if ( orientationJoint = = INVALID_JOINT ) {
orientationJointAxis = viewAxis ;
orientationJointPos = physicsObj . GetOrigin ( ) ;
orientationJointYaw = current_yaw ;
} else {
GetJointWorldTransform ( orientationJoint , gameLocal . time , orientationJointPos , orientationJointAxis ) ;
orientationJointYaw = orientationJointAxis [ 2 ] . ToYaw ( ) ;
orientationJointAxis = idAngles ( 0.0f , orientationJointYaw , 0.0f ) . ToMat3 ( ) ;
}
if ( focusJoint ! = INVALID_JOINT ) {
if ( headEnt ) {
headEnt - > GetJointWorldTransform ( focusJoint , gameLocal . time , eyepos , axis ) ;
} else {
GetJointWorldTransform ( focusJoint , gameLocal . time , eyepos , axis ) ;
}
eyeOffset . z = eyepos . z - physicsObj . GetOrigin ( ) . z ;
if ( ai_debugMove . GetBool ( ) ) {
gameRenderWorld - > DebugLine ( colorRed , eyepos , eyepos + orientationJointAxis [ 0 ] * 32.0f , gameLocal . msec ) ;
}
} else {
eyepos = GetEyePosition ( ) ;
}
if ( headEnt ) {
CopyJointsFromBodyToHead ( ) ;
}
// Update the IK after we've gotten all the joint positions we need, but before we set any joint positions.
// Getting the joint positions causes the joints to be updated. The IK gets joint positions itself (which
// are already up to date because of getting the joints in this function) and then sets their positions, which
// forces the heirarchy to be updated again next time we get a joint or present the model. If IK is enabled,
// or if we have a seperate head, we end up transforming the joints twice per frame. Characters with no
// head entity and no ik will only transform their joints once. Set g_debuganim to the current entity number
// in order to see how many times an entity transforms the joints per frame.
idActor : : UpdateAnimationControllers ( ) ;
idEntity * focusEnt = focusEntity . GetEntity ( ) ;
if ( ! allowJointMod | | ! allowEyeFocus | | ( gameLocal . time > = focusTime ) ) {
2011-12-06 18:20:15 +00:00
focusPos = GetEyePosition ( ) + orientationJointAxis [ 0 ] * 512.0f ;
2011-11-22 21:28:15 +00:00
} else if ( focusEnt = = NULL ) {
// keep looking at last position until focusTime is up
focusPos = currentFocusPos ;
} else if ( focusEnt = = enemy . GetEntity ( ) ) {
focusPos = lastVisibleEnemyPos + lastVisibleEnemyEyeOffset - eyeVerticalOffset * enemy . GetEntity ( ) - > GetPhysics ( ) - > GetGravityNormal ( ) ;
} else if ( focusEnt - > IsType ( idActor : : Type ) ) {
focusPos = static_cast < idActor * > ( focusEnt ) - > GetEyePosition ( ) - eyeVerticalOffset * focusEnt - > GetPhysics ( ) - > GetGravityNormal ( ) ;
} else {
focusPos = focusEnt - > GetPhysics ( ) - > GetOrigin ( ) ;
}
currentFocusPos = currentFocusPos + ( focusPos - currentFocusPos ) * eyeFocusRate ;
// determine yaw from origin instead of from focus joint since joint may be offset, which can cause us to bounce between two angles
dir = focusPos - orientationJointPos ;
newLookAng . yaw = idMath : : AngleNormalize180 ( dir . ToYaw ( ) - orientationJointYaw ) ;
newLookAng . roll = 0.0f ;
newLookAng . pitch = 0.0f ;
#if 0
gameRenderWorld - > DebugLine ( colorRed , orientationJointPos , focusPos , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorYellow , orientationJointPos , orientationJointPos + orientationJointAxis [ 0 ] * 32.0f , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( colorGreen , orientationJointPos , orientationJointPos + newLookAng . ToForward ( ) * 48.0f , gameLocal . msec ) ;
# endif
// determine pitch from joint position
dir = focusPos - eyepos ;
dir . NormalizeFast ( ) ;
orientationJointAxis . ProjectVector ( dir , localDir ) ;
newLookAng . pitch = - idMath : : AngleNormalize180 ( localDir . ToPitch ( ) ) ;
newLookAng . roll = 0.0f ;
diff = newLookAng - lookAng ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( eyeAng ! = diff ) {
eyeAng = diff ;
eyeAng . Clamp ( eyeMin , eyeMax ) ;
idAngles angDelta = diff - eyeAng ;
if ( ! angDelta . Compare ( ang_zero , 0.1f ) ) {
alignHeadTime = gameLocal . time ;
} else {
alignHeadTime = gameLocal . time + ( 0.5f + 0.5f * gameLocal . random . RandomFloat ( ) ) * focusAlignTime ;
}
}
if ( idMath : : Fabs ( newLookAng . yaw ) < 0.1f ) {
alignHeadTime = gameLocal . time ;
}
if ( ( gameLocal . time > = alignHeadTime ) | | ( gameLocal . time < forceAlignHeadTime ) ) {
alignHeadTime = gameLocal . time + ( 0.5f + 0.5f * gameLocal . random . RandomFloat ( ) ) * focusAlignTime ;
destLookAng = newLookAng ;
destLookAng . Clamp ( lookMin , lookMax ) ;
}
diff = destLookAng - lookAng ;
if ( ( lookMin . pitch = = - 180.0f ) & & ( lookMax . pitch = = 180.0f ) ) {
if ( ( diff . pitch > 180.0f ) | | ( diff . pitch < = - 180.0f ) ) {
diff . pitch = 360.0f - diff . pitch ;
}
}
if ( ( lookMin . yaw = = - 180.0f ) & & ( lookMax . yaw = = 180.0f ) ) {
if ( diff . yaw > 180.0f ) {
diff . yaw - = 360.0f ;
} else if ( diff . yaw < = - 180.0f ) {
diff . yaw + = 360.0f ;
}
}
lookAng = lookAng + diff * headFocusRate ;
lookAng . Normalize180 ( ) ;
jointAng . roll = 0.0f ;
for ( i = 0 ; i < lookJoints . Num ( ) ; i + + ) {
jointAng . pitch = lookAng . pitch * lookJointAngles [ i ] . pitch ;
jointAng . yaw = lookAng . yaw * lookJointAngles [ i ] . yaw ;
animator . SetJointAxis ( lookJoints [ i ] , JOINTMOD_WORLD , jointAng . ToMat3 ( ) ) ;
}
if ( move . moveType = = MOVETYPE_FLY ) {
// lean into turns
AdjustFlyingAngles ( ) ;
}
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( headEnt ) {
idAnimator * headAnimator = headEnt - > GetAnimator ( ) ;
if ( allowEyeFocus ) {
idMat3 eyeAxis = ( lookAng + eyeAng ) . ToMat3 ( ) ; idMat3 headTranspose = headEnt - > GetPhysics ( ) - > GetAxis ( ) . Transpose ( ) ;
axis = eyeAxis * orientationJointAxis ;
left = axis [ 1 ] * eyeHorizontalOffset ;
eyepos - = headEnt - > GetPhysics ( ) - > GetOrigin ( ) ;
headAnimator - > SetJointPos ( leftEyeJoint , JOINTMOD_WORLD_OVERRIDE , eyepos + ( axis [ 0 ] * 64.0f + left ) * headTranspose ) ;
headAnimator - > SetJointPos ( rightEyeJoint , JOINTMOD_WORLD_OVERRIDE , eyepos + ( axis [ 0 ] * 64.0f - left ) * headTranspose ) ;
} else {
headAnimator - > ClearJoint ( leftEyeJoint ) ;
headAnimator - > ClearJoint ( rightEyeJoint ) ;
}
} else {
if ( allowEyeFocus ) {
idMat3 eyeAxis = ( lookAng + eyeAng ) . ToMat3 ( ) ;
axis = eyeAxis * orientationJointAxis ;
left = axis [ 1 ] * eyeHorizontalOffset ;
eyepos + = axis [ 0 ] * 64.0f - physicsObj . GetOrigin ( ) ;
animator . SetJointPos ( leftEyeJoint , JOINTMOD_WORLD_OVERRIDE , eyepos + left ) ;
animator . SetJointPos ( rightEyeJoint , JOINTMOD_WORLD_OVERRIDE , eyepos - left ) ;
} else {
animator . ClearJoint ( leftEyeJoint ) ;
animator . ClearJoint ( rightEyeJoint ) ;
}
}
return true ;
}
/***********************************************************************
idCombatNode
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
const idEventDef EV_CombatNode_MarkUsed ( " markUsed " ) ;
CLASS_DECLARATION ( idEntity , idCombatNode )
EVENT ( EV_CombatNode_MarkUsed , idCombatNode : : Event_MarkUsed )
EVENT ( EV_Activate , idCombatNode : : Event_Activate )
END_CLASS
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : idCombatNode
= = = = = = = = = = = = = = = = = = = = =
*/
idCombatNode : : idCombatNode ( void ) {
min_dist = 0.0f ;
max_dist = 0.0f ;
cone_dist = 0.0f ;
min_height = 0.0f ;
max_height = 0.0f ;
cone_left . Zero ( ) ;
cone_right . Zero ( ) ;
offset . Zero ( ) ;
disabled = false ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : Save
= = = = = = = = = = = = = = = = = = = = =
*/
void idCombatNode : : Save ( idSaveGame * savefile ) const {
savefile - > WriteFloat ( min_dist ) ;
savefile - > WriteFloat ( max_dist ) ;
savefile - > WriteFloat ( cone_dist ) ;
savefile - > WriteFloat ( min_height ) ;
savefile - > WriteFloat ( max_height ) ;
savefile - > WriteVec3 ( cone_left ) ;
savefile - > WriteVec3 ( cone_right ) ;
savefile - > WriteVec3 ( offset ) ;
savefile - > WriteBool ( disabled ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : Restore
= = = = = = = = = = = = = = = = = = = = =
*/
void idCombatNode : : Restore ( idRestoreGame * savefile ) {
savefile - > ReadFloat ( min_dist ) ;
savefile - > ReadFloat ( max_dist ) ;
savefile - > ReadFloat ( cone_dist ) ;
savefile - > ReadFloat ( min_height ) ;
savefile - > ReadFloat ( max_height ) ;
savefile - > ReadVec3 ( cone_left ) ;
savefile - > ReadVec3 ( cone_right ) ;
savefile - > ReadVec3 ( offset ) ;
savefile - > ReadBool ( disabled ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : Spawn
= = = = = = = = = = = = = = = = = = = = =
*/
void idCombatNode : : Spawn ( void ) {
float fov ;
float yaw ;
float height ;
min_dist = spawnArgs . GetFloat ( " min " ) ;
max_dist = spawnArgs . GetFloat ( " max " ) ;
height = spawnArgs . GetFloat ( " height " ) ;
2021-12-19 04:51:02 +00:00
fov = spawnArgs . GetFloat ( " fov " , " 120 " ) ; //was 60 rev 2018
2011-11-22 21:28:15 +00:00
offset = spawnArgs . GetVector ( " offset " ) ;
const idVec3 & org = GetPhysics ( ) - > GetOrigin ( ) + offset ;
min_height = org . z - height * 0.5f ;
max_height = min_height + height ;
const idMat3 & axis = GetPhysics ( ) - > GetAxis ( ) ;
yaw = axis [ 0 ] . ToYaw ( ) ;
idAngles leftang ( 0.0f , yaw + fov * 0.5f - 90.0f , 0.0f ) ;
cone_left = leftang . ToForward ( ) ;
idAngles rightang ( 0.0f , yaw - fov * 0.5f + 90.0f , 0.0f ) ;
cone_right = rightang . ToForward ( ) ;
disabled = spawnArgs . GetBool ( " start_off " ) ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : IsDisabled
= = = = = = = = = = = = = = = = = = = = =
*/
bool idCombatNode : : IsDisabled ( void ) const {
return disabled ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : DrawDebugInfo
= = = = = = = = = = = = = = = = = = = = =
*/
void idCombatNode : : DrawDebugInfo ( void ) {
idEntity * ent ;
idCombatNode * node ;
idPlayer * player = gameLocal . GetLocalPlayer ( ) ;
idVec4 color ;
idBounds bounds ( idVec3 ( - 16 , - 16 , 0 ) , idVec3 ( 16 , 16 , 0 ) ) ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
for ( ent = gameLocal . spawnedEntities . Next ( ) ; ent ! = NULL ; ent = ent - > spawnNode . Next ( ) ) {
if ( ! ent - > IsType ( idCombatNode : : Type ) ) {
continue ;
}
node = static_cast < idCombatNode * > ( ent ) ;
if ( node - > disabled ) {
color = colorMdGrey ;
} else if ( player & & node - > EntityInView ( player , player - > GetPhysics ( ) - > GetOrigin ( ) ) ) {
color = colorYellow ;
} else {
color = colorRed ;
}
idVec3 leftDir ( - node - > cone_left . y , node - > cone_left . x , 0.0f ) ;
idVec3 rightDir ( node - > cone_right . y , - node - > cone_right . x , 0.0f ) ;
idVec3 org = node - > GetPhysics ( ) - > GetOrigin ( ) + node - > offset ;
bounds [ 1 ] . z = node - > max_height ;
leftDir . NormalizeFast ( ) ;
rightDir . NormalizeFast ( ) ;
const idMat3 & axis = node - > GetPhysics ( ) - > GetAxis ( ) ;
float cone_dot = node - > cone_right * axis [ 1 ] ;
if ( idMath : : Fabs ( cone_dot ) > 0.1 ) {
float cone_dist = node - > max_dist / cone_dot ;
idVec3 pos1 = org + leftDir * node - > min_dist ;
idVec3 pos2 = org + leftDir * cone_dist ;
idVec3 pos3 = org + rightDir * node - > min_dist ;
idVec3 pos4 = org + rightDir * cone_dist ;
gameRenderWorld - > DebugLine ( color , node - > GetPhysics ( ) - > GetOrigin ( ) , ( pos1 + pos3 ) * 0.5f , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( color , pos1 , pos2 , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( color , pos1 , pos3 , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( color , pos3 , pos4 , gameLocal . msec ) ;
gameRenderWorld - > DebugLine ( color , pos2 , pos4 , gameLocal . msec ) ;
gameRenderWorld - > DebugBounds ( color , bounds , org , gameLocal . msec ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : EntityInView
= = = = = = = = = = = = = = = = = = = = =
*/
bool idCombatNode : : EntityInView ( idActor * actor , const idVec3 & pos ) {
if ( ! actor | | ( actor - > health < = 0 ) ) {
return false ;
}
const idBounds & bounds = actor - > GetPhysics ( ) - > GetBounds ( ) ;
if ( ( pos . z + bounds [ 1 ] . z < min_height ) | | ( pos . z + bounds [ 0 ] . z > = max_height ) ) {
return false ;
}
const idVec3 & org = GetPhysics ( ) - > GetOrigin ( ) + offset ;
const idMat3 & axis = GetPhysics ( ) - > GetAxis ( ) ;
idVec3 dir = pos - org ;
float dist = dir * axis [ 0 ] ;
2011-12-06 18:20:15 +00:00
2011-11-22 21:28:15 +00:00
if ( ( dist < min_dist ) | | ( dist > max_dist ) ) {
return false ;
}
float left_dot = dir * cone_left ;
if ( left_dot < 0.0f ) {
return false ;
}
float right_dot = dir * cone_right ;
if ( right_dot < 0.0f ) {
return false ;
}
return true ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : Event_Activate
= = = = = = = = = = = = = = = = = = = = =
*/
void idCombatNode : : Event_Activate ( idEntity * activator ) {
disabled = ! disabled ;
}
/*
= = = = = = = = = = = = = = = = = = = = =
idCombatNode : : Event_MarkUsed
= = = = = = = = = = = = = = = = = = = = =
*/
void idCombatNode : : Event_MarkUsed ( void ) {
if ( spawnArgs . GetBool ( " use_once " ) ) {
disabled = true ;
}
}
2018-08-26 21:52:13 +00:00