mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-16 08:10:32 +02:00
533 lines
16 KiB
C++
533 lines
16 KiB
C++
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
const idEventDef EV_UpdateTarget( "<updateTarget>" );
|
|
const idEventDef EV_DirectMoveToPosition("directMoveToPosition", "v" );
|
|
const idEventDef MA_GetCircleNode("getCircleNode", NULL, 'e' );
|
|
const idEventDef MA_SpinClouds("spinClouds", "f" );
|
|
const idEventDef MA_SetSeekScale("setSeekScale", "f" );
|
|
|
|
CLASS_DECLARATION(hhMonsterAI, hhSphereBoss)
|
|
EVENT( EV_UpdateTarget, hhSphereBoss::Event_UpdateTarget )
|
|
EVENT( EV_DirectMoveToPosition, hhSphereBoss::Event_DirectMoveToPosition )
|
|
EVENT( MA_GetCircleNode, hhSphereBoss::Event_GetCircleNode )
|
|
EVENT( MA_SpinClouds, hhSphereBoss::Event_SpinClouds )
|
|
EVENT( MA_SetSeekScale, hhSphereBoss::Event_SetSeekScale )
|
|
END_CLASS
|
|
|
|
#ifndef ID_DEMO_BUILD //HUMANHEAD jsh PCF 5/26/06: code removed for demo build
|
|
|
|
void hhSphereBoss::Spawn() {
|
|
for ( int i=0;i<10;i++ ) {
|
|
lastTargetPos.Append( vec3_zero );
|
|
}
|
|
nextShieldImpact = 0;
|
|
lastNodeIndex = -1;
|
|
PostEventSec( &EV_UpdateTarget, spawnArgs.GetFloat( "target_period", "0.1" ) );
|
|
}
|
|
|
|
void hhSphereBoss::Event_UpdateTarget() {
|
|
for ( int i=0;i<10;i++ ) {
|
|
if ( i == 9 ) {
|
|
if ( enemy.IsValid() ) {
|
|
lastTargetPos[i] = enemy->GetOrigin();
|
|
} else {
|
|
lastTargetPos[i] = lastTargetPos[i - 1];
|
|
}
|
|
} else {
|
|
lastTargetPos[i] = lastTargetPos[i + 1];
|
|
}
|
|
}
|
|
PostEventSec( &EV_UpdateTarget, spawnArgs.GetFloat( "target_period", "0.1" ) );
|
|
}
|
|
|
|
//overridden to allow custom accuracy/numbers per projectile def
|
|
idProjectile *hhSphereBoss::LaunchProjectile( const char *jointname, idEntity *target, bool clampToAttackCone, const idDict* desiredProjectileDef ) {
|
|
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;
|
|
float angle;
|
|
float spin;
|
|
idAngles ang;
|
|
int num_projectiles;
|
|
int i;
|
|
idMat3 axis;
|
|
idVec3 tmp;
|
|
idProjectile *lastProjectile;
|
|
|
|
//HUMANHEAD mdc - added to support multiple projectiles
|
|
if( desiredProjectileDef ) { //try to set our projectile to the desiredProjectile
|
|
int projIndex = FindProjectileInfo( desiredProjectileDef );
|
|
if( projIndex >= 0 ) {
|
|
SetCurrentProjectile( projIndex );
|
|
}
|
|
}
|
|
//HUMANHEAD END
|
|
|
|
|
|
if ( !projectileDef ) {
|
|
gameLocal.Warning( "%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName() );
|
|
return NULL;
|
|
}
|
|
|
|
if ( projectileDef->GetFloat( "attack_accuracy" ) ) {
|
|
attack_accuracy = projectileDef->GetFloat( "attack_accuracy", "7" );
|
|
} else {
|
|
attack_accuracy = spawnArgs.GetFloat( "attack_accuracy", "7" );
|
|
}
|
|
attack_cone = spawnArgs.GetFloat( "attack_cone", "70" );
|
|
if ( projectileDef->GetFloat( "projectile_spread" ) ) {
|
|
projectile_spread = projectileDef->GetFloat( "projectile_spread", "0" );
|
|
} else {
|
|
projectile_spread = spawnArgs.GetFloat( "projectile_spread", "0" );
|
|
}
|
|
if ( projectileDef->GetFloat( "num_projectiles" ) ) {
|
|
num_projectiles = projectileDef->GetFloat( "num_projectiles", "1" );
|
|
} else {
|
|
num_projectiles = spawnArgs.GetFloat( "num_projectiles", "1" );
|
|
}
|
|
|
|
GetMuzzle( jointname, muzzle, axis );
|
|
|
|
if ( !projectile.GetEntity() ) {
|
|
CreateProjectile( muzzle, axis[ 0 ] );
|
|
}
|
|
|
|
lastProjectile = projectile.GetEntity();
|
|
|
|
if ( target != NULL ) {
|
|
tmp = target->GetPhysics()->GetAbsBounds().GetCenter() - muzzle;
|
|
tmp.Normalize();
|
|
axis = tmp.ToMat3();
|
|
} else {
|
|
axis = viewAxis;
|
|
}
|
|
|
|
// 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
|
|
if ( spawnArgs.GetBool( "lag_target", "0" ) ) {
|
|
dir = (lastTargetPos[0] - muzzle).ToNormal();
|
|
} else {
|
|
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();
|
|
|
|
float spreadRad = DEG2RAD( projectile_spread );
|
|
for( i = 0; i < num_projectiles; i++ ) {
|
|
// 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 ) );
|
|
dir.Normalize();
|
|
|
|
// launch the projectile
|
|
if ( !projectile.GetEntity() ) {
|
|
CreateProjectile( muzzle, dir );
|
|
}
|
|
lastProjectile = projectile.GetEntity();
|
|
lastProjectile->Launch( muzzle, dir, vec3_origin );
|
|
projectile = NULL;
|
|
}
|
|
|
|
TriggerWeaponEffects( muzzle,axis );
|
|
|
|
lastAttackTime = gameLocal.time;
|
|
|
|
//HUMANHEAD mdc - added to support multiple projectiles
|
|
projectile = NULL;
|
|
SetCurrentProjectile( projectileDefaultDefIndex ); //set back to our default projectile to be on the safe side
|
|
//HUMANHEAD END
|
|
|
|
return lastProjectile;
|
|
}
|
|
|
|
void hhSphereBoss::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
HandleNoGore();
|
|
if ( !AI_DEAD ) {
|
|
AI_DEAD = true;
|
|
state = GetScriptFunction( "state_SphereDeath" );
|
|
SetState( state );
|
|
SetWaitState( "" );
|
|
}
|
|
}
|
|
|
|
void hhSphereBoss::FlyTurn( void ) {
|
|
if ( AI_FACE_ENEMY ) {
|
|
TurnToward( enemy->GetOrigin() );
|
|
} else {
|
|
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();
|
|
}
|
|
|
|
void hhSphereBoss::Event_SetSeekScale( float new_scale ) {
|
|
fly_seek_scale = new_scale;
|
|
}
|
|
|
|
void hhSphereBoss::Event_GetCircleNode() {
|
|
if ( !enemy.IsValid() ) {
|
|
idThread::ReturnEntity( NULL );
|
|
return;
|
|
}
|
|
idEntity *ent = NULL;
|
|
idEntity *bestEnt = NULL;
|
|
lastNodeIndex++;
|
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
|
if ( !ent || !ent->IsType( hhAINode::Type ) ) {
|
|
continue;
|
|
}
|
|
if ( !ent->spawnArgs.GetBool( "circle_node" ) ) {
|
|
continue;
|
|
}
|
|
if ( lastNodeIndex == ent->spawnArgs.GetInt( "circle_node_index" ) ) {
|
|
bestEnt = ent;
|
|
break;
|
|
}
|
|
}
|
|
if ( !bestEnt ) {
|
|
lastNodeIndex = 0;
|
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
|
if ( !ent || !ent->IsType( hhAINode::Type ) ) {
|
|
continue;
|
|
}
|
|
if ( !ent->spawnArgs.GetBool( "circle_node" ) ) {
|
|
continue;
|
|
}
|
|
if ( lastNodeIndex == ent->spawnArgs.GetInt( "circle_node_index" ) ) {
|
|
bestEnt = ent;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ( bestEnt ) {
|
|
idThread::ReturnEntity( bestEnt );
|
|
} else {
|
|
idThread::ReturnEntity( NULL );
|
|
}
|
|
}
|
|
|
|
void hhSphereBoss::Event_GetCombatNode() {
|
|
idEntity *ent = NULL;
|
|
float bestDist = 0.0f;
|
|
float dist;
|
|
idEntity *bestEnt = NULL;
|
|
idList<idEntity*> list;
|
|
for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
|
if ( !ent || !ent->spawnArgs.GetBool( "ainode" ) ) {
|
|
continue;
|
|
}
|
|
dist = (ent->GetOrigin() - GetOrigin()).Length();
|
|
if ( dist < spawnArgs.GetFloat( "node_dist", "400" ) ) {
|
|
continue;
|
|
}
|
|
list.Append( ent );
|
|
}
|
|
if ( list.Num() > 0 ) {
|
|
idThread::ReturnEntity( list[gameLocal.random.RandomInt(list.Num())] );
|
|
} else {
|
|
idThread::ReturnEntity( NULL );
|
|
}
|
|
}
|
|
|
|
void hhSphereBoss::Event_DirectMoveToPosition(const idVec3 &pos) {
|
|
StopMove(MOVE_STATUS_DONE);
|
|
DirectMoveToPosition(pos);
|
|
}
|
|
|
|
void hhSphereBoss::FlyMove( void ) {
|
|
idVec3 goalPos;
|
|
idVec3 oldorigin;
|
|
idVec3 newDest;
|
|
|
|
AI_BLOCKED = false;
|
|
if ( ( move.moveCommand != MOVE_NONE ) && ReachedPos( move.moveDest, move.moveCommand ) ) {
|
|
if ( AI_FACE_ENEMY ) {
|
|
StopMove( MOVE_STATUS_DONE );
|
|
} else {
|
|
AI_MOVE_DONE = true;
|
|
}
|
|
}
|
|
|
|
idVec3 vel = physicsObj.GetLinearVelocity();
|
|
goalPos = move.moveDest;
|
|
if ( ReachedPos( move.moveDest, move.moveCommand ) ) {
|
|
StopMove( MOVE_STATUS_DONE );
|
|
}
|
|
|
|
if ( move.speed ) {
|
|
FlySeekGoal( vel, goalPos );
|
|
}
|
|
AddFlyBob( vel );
|
|
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 );
|
|
RunPhysics();
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
void hhSphereBoss::AdjustFlySpeed( idVec3 &vel ) {
|
|
float speed;
|
|
|
|
// apply dampening
|
|
float damp = spawnArgs.GetFloat( "fly_dampening", "0.01" );
|
|
vel -= vel * damp * 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;
|
|
}
|
|
|
|
bool hhSphereBoss::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 ) );
|
|
bnds.TranslateSelf( physicsObj.GetOrigin() );
|
|
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( -64.0, -64.0f, -64.0f ), idVec3( 64.0, 64.0f, 64.0f ) );
|
|
bnds.TranslateSelf( physicsObj.GetOrigin() );
|
|
if ( bnds.ContainsPoint( pos ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define LinkScriptVariable( name ) name.LinkTo( scriptObject, #name )
|
|
void hhSphereBoss::LinkScriptVariables() {
|
|
hhMonsterAI::LinkScriptVariables();
|
|
LinkScriptVariable( AI_FACE_ENEMY );
|
|
LinkScriptVariable( AI_CAN_DAMAGE );
|
|
}
|
|
|
|
void hhSphereBoss::Event_SpinClouds( float shouldSpin ) {
|
|
if ( shouldSpin == 0.0f ) {
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "cloud" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
ent->SetShaderParm( spawnArgs.GetInt( "spin_parm", "4" ), ent->spawnArgs.GetFloat( "shaderparm4", "0" ) );
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "cloud", kv );
|
|
}
|
|
} else {
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix( "cloud" );
|
|
while( kv ) {
|
|
idEntity *ent = gameLocal.FindEntity( kv->GetValue() );
|
|
if ( ent ) {
|
|
ent->SetShaderParm( spawnArgs.GetInt( "spin_parm", "4" ), spawnArgs.GetFloat( "spin_value", "0.5" ) );
|
|
}
|
|
kv = spawnArgs.MatchPrefix( "cloud", kv );
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhSphereBoss::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
|
|
//overridden to allow custom wound code
|
|
if ( !AI_CAN_DAMAGE && inflictor && spawnArgs.MatchPrefix("mineDamage") ) {
|
|
hhFxInfo fxInfo;
|
|
fxInfo.RemoveWhenDone( true );
|
|
if ( gameLocal.time >= nextShieldImpact ) {
|
|
nextShieldImpact = gameLocal.time + int(spawnArgs.GetFloat( "shield_impact_freq", "0.4" ) * 1000);
|
|
idVec3 offset;
|
|
if ( attacker && inflictor ) {
|
|
offset = (attacker->GetOrigin() - inflictor->GetOrigin()).ToNormal() * spawnArgs.GetFloat( "shield_impact_offset", "150" );
|
|
const char *defName = spawnArgs.GetString( "fx_shield_impact" );
|
|
idEntityFx *impactFx = SpawnFxLocal( defName, inflictor->GetOrigin() + offset, mat3_identity, &fxInfo, gameLocal.isClient );
|
|
if ( impactFx ) {
|
|
impactFx->Bind( this, true );
|
|
}
|
|
}
|
|
}
|
|
StartSound( "snd_shield_impact", SND_CHANNEL_BODY );
|
|
if ( inflictor && inflictor->IsType( idProjectile::Type ) ) {
|
|
inflictor->PostEventMS( &EV_Remove, 0 );
|
|
}
|
|
} else {
|
|
hhFxInfo fxInfo;
|
|
fxInfo.RemoveWhenDone( true );
|
|
if ( gameLocal.time >= nextShieldImpact ) {
|
|
nextShieldImpact = gameLocal.time + int(spawnArgs.GetFloat( "shield_impact_freq", "0.4" ) * 1000);
|
|
idVec3 offset;
|
|
if ( attacker && inflictor ) {
|
|
offset = (attacker->GetOrigin() - inflictor->GetOrigin()).ToNormal() * spawnArgs.GetFloat( "shield_impact_offset", "150" );
|
|
BroadcastFxInfoPrefixed( "fx_pain_impact", inflictor->GetOrigin() + offset, mat3_identity, &fxInfo );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool mine_damage = false;
|
|
if ( !AI_CAN_DAMAGE && spawnArgs.MatchPrefix("mineDamage") != NULL ) {
|
|
const idKeyValue *kv = spawnArgs.MatchPrefix("mineDamage");
|
|
while( kv && kv->GetValue().Length() ) {
|
|
if ( !kv->GetValue().Icmp(damageDefName) ) {
|
|
mine_damage = true;
|
|
break;
|
|
}
|
|
kv = spawnArgs.MatchPrefix("mineDamage", kv);
|
|
}
|
|
if (!mine_damage) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( !mine_damage ) {
|
|
hhMonsterAI::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
|
|
}
|
|
|
|
if ( !AI_CAN_DAMAGE && health > 0 ) {
|
|
SetState( GetScriptFunction( "state_Pain" ) );
|
|
SetWaitState( "" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhSphereBoss::Save
|
|
=====================
|
|
*/
|
|
void hhSphereBoss::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteInt( lastTargetPos.Num() );
|
|
savefile->WriteInt( nextShieldImpact );
|
|
for ( int i=0;i<lastTargetPos.Num(); i++) {
|
|
savefile->WriteVec3( lastTargetPos[i] );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhSphereBoss::Restore
|
|
=====================
|
|
*/
|
|
void hhSphereBoss::Restore( idRestoreGame *savefile ) {
|
|
int num = 0;
|
|
savefile->ReadInt( num );
|
|
savefile->ReadInt( nextShieldImpact );
|
|
lastTargetPos.SetNum( num );
|
|
for ( int i=0;i<num; i++) {
|
|
savefile->ReadVec3( lastTargetPos[i] );
|
|
}
|
|
}
|
|
|
|
void hhSphereBoss::AddLocalMatterWound( jointHandle_t jointNum, const idVec3 &localOrigin, const idVec3 &localNormal, const idVec3 &localDir, int damageDefIndex, const idMaterial *collisionMaterial ) {
|
|
//overridden to remove woundmanager hook and use custom wound code is Damage()
|
|
if ( AI_CAN_DAMAGE ) {
|
|
return hhMonsterAI::AddLocalMatterWound( jointNum, localOrigin, localNormal, localDir, damageDefIndex, collisionMaterial );
|
|
}
|
|
}
|
|
#endif //HUMANHEAD jsh PCF 5/26/06: code removed for demo build
|