mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
660 lines
20 KiB
C++
660 lines
20 KiB
C++
|
|
|
|
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
const idEventDef EV_StartBurst("startBurst");
|
|
const idEventDef EV_EndBurst("endBurst");
|
|
const idEventDef EV_StartStaticBeam("startStaticBeam");
|
|
const idEventDef EV_EndStaticBeam("endStaticBeam");
|
|
const idEventDef EV_StartChargeShot("startChargeShot");
|
|
const idEventDef EV_EndChargeShot("endChargeShot");
|
|
const idEventDef EV_StartPathing("startPathing");
|
|
const idEventDef EV_EndPathing("endPathing");
|
|
const idEventDef EV_EndZipBeams("<endZipBeams>");
|
|
const idEventDef EV_HealerReset("<healerReset>");
|
|
|
|
CLASS_DECLARATION(hhMonsterAI, hhDroid)
|
|
EVENT(EV_StartStaticBeam, hhDroid::Event_StartStaticBeam)
|
|
EVENT(EV_EndStaticBeam, hhDroid::Event_EndStaticBeam)
|
|
EVENT(EV_StartBurst, hhDroid::Event_StartBurst)
|
|
EVENT(EV_EndBurst, hhDroid::Event_EndBurst)
|
|
EVENT(EV_StartChargeShot, hhDroid::Event_StartChargeShot)
|
|
EVENT(EV_EndChargeShot, hhDroid::Event_EndChargeShot)
|
|
EVENT(EV_StartPathing, hhDroid::Event_StartPathing)
|
|
EVENT(EV_EndPathing, hhDroid::Event_EndPathing)
|
|
EVENT(EV_EndZipBeams, hhDroid::Event_EndZipBeams)
|
|
EVENT(EV_HealerReset, hhDroid::Event_HealerReset)
|
|
END_CLASS
|
|
|
|
#ifndef ID_DEMO_BUILD //HUMANHEAD jsh PCF 5/26/06: code removed for demo build
|
|
hhDroid::hhDroid() {
|
|
burstLength = 0.0;
|
|
burstSpread = 0.0;
|
|
burstDuration = 0.0;
|
|
staticDuration = 0.0;
|
|
staticRange = 0.0;
|
|
numBurstBeams = 0;
|
|
beamOffset = vec3_zero;
|
|
chargeShotSize = 0.0;
|
|
old_fly_bob_strength = 0.0;
|
|
spinAngle = 0.0f;
|
|
}
|
|
|
|
hhDroid::~hhDroid() {
|
|
for ( int i = 0; i < numBurstBeams; i ++ ) {
|
|
SAFE_REMOVE( beamBurstList[i] );
|
|
}
|
|
}
|
|
|
|
void hhDroid::Show( void ) {
|
|
hhMonsterAI::Show();
|
|
|
|
PostEventSec(&EV_StartStaticBeam, gameLocal.random.RandomFloat() * spawnArgs.GetFloat( "staticFreq", "2.5" ) );
|
|
}
|
|
|
|
void hhDroid::Spawn(void)
|
|
{
|
|
Event_SetMoveType(MOVETYPE_FLY);
|
|
lives = spawnArgs.GetInt( "lives", "3" );
|
|
numBurstBeams = spawnArgs.GetInt( "numBurstBeams", "5" );
|
|
burstLength = spawnArgs.GetFloat( "burstLength", "250" );
|
|
staticDuration = spawnArgs.GetFloat( "staticDuration", "2.0" );
|
|
burstDuration = spawnArgs.GetFloat( "burstDuration", "2.0" );
|
|
beamOffset = spawnArgs.GetVector( "beamOffset", "0 0 0" );
|
|
burstSpread = spawnArgs.GetFloat( "burstSpread", "1.0" );
|
|
enemyRange = spawnArgs.GetFloat( "enemy_range", "3000" );
|
|
flyDampening = spawnArgs.GetFloat( "fly_dampening", "0.01" );
|
|
bHealer = spawnArgs.GetBool( "healer" );
|
|
|
|
beamZip = hhBeamSystem::SpawnBeam( vec3_origin, spawnArgs.GetString( "def_beamZip" ) );
|
|
if( beamZip.IsValid() ) {
|
|
beamZip->Activate( false );
|
|
beamZip->SetOrigin( GetPhysics()->GetOrigin() + beamOffset );
|
|
beamZip->Bind( this, false );
|
|
}
|
|
|
|
for ( int i = 0; i < numBurstBeams; i ++ ) {
|
|
if ( gameLocal.random.RandomFloat() > 0.5f ) {
|
|
beamBurstList.Append( hhBeamSystem::SpawnBeam( vec3_origin, spawnArgs.GetString( "def_beamBurst1" ) ) );
|
|
} else {
|
|
beamBurstList.Append( hhBeamSystem::SpawnBeam( vec3_origin, spawnArgs.GetString( "def_beamBurst2" ) ) );
|
|
}
|
|
if( beamBurstList[i].IsValid() ) {
|
|
beamBurstList[i]->Activate( false );
|
|
beamBurstList[i]->SetOrigin( GetPhysics()->GetOrigin() + beamOffset );
|
|
beamBurstList[i]->Bind( this, false );
|
|
}
|
|
}
|
|
|
|
PostEventSec(&EV_StartStaticBeam, gameLocal.random.RandomFloat() * spawnArgs.GetFloat( "staticFreq", "2.5" ) );
|
|
}
|
|
|
|
bool hhDroid::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
bool bPain = hhMonsterAI::Pain( inflictor, attacker, damage, dir, location );
|
|
if ( !bPain ) {
|
|
return bPain;
|
|
}
|
|
|
|
//activate some beams for damage fx
|
|
for ( int i = 0; i < beamBurstList.Num(); i ++ ) {
|
|
if ( beamBurstList[i].IsValid() ) {
|
|
beamBurstList[i]->Activate( true );
|
|
beamBurstList[i]->SetTargetLocation( beamBurstList[i]->GetOrigin() + burstLength * hhUtils::RandomSpreadDir( dir.ToMat3(), burstSpread ) );
|
|
PostEventSec(&EV_EndBurst, staticDuration);
|
|
}
|
|
}
|
|
return bPain;
|
|
}
|
|
|
|
void hhDroid::Think() {
|
|
PROFILE_SCOPE("AI", PROFMASK_NORMAL|PROFMASK_AI);
|
|
if (ai_skipThink.GetBool()) {
|
|
return;
|
|
}
|
|
|
|
hhMonsterAI::Think();
|
|
|
|
//update burst beams
|
|
idMat3 dir;
|
|
if ( enemy.IsValid() ) {
|
|
dir = (enemy->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).ToMat3();
|
|
} else {
|
|
dir = GetPhysics()->GetAxis();
|
|
}
|
|
for ( int i = 0; i < beamBurstList.Num(); i ++ ) {
|
|
if ( beamBurstList[i].IsValid() && beamBurstList[i]->IsActivated() ) {
|
|
beamBurstList[i]->SetTargetLocation( beamBurstList[i]->GetOrigin() + burstLength * hhUtils::RandomSpreadDir( hhUtils::RandomVector().ToMat3(), burstSpread ) );
|
|
}
|
|
}
|
|
|
|
if ( staticPoint.IsValid() && beamBurstList[0].IsValid() ) {
|
|
beamBurstList[0]->SetTargetLocation( staticPoint->GetPhysics()->GetOrigin() );
|
|
}
|
|
|
|
if ( chargeShot.IsValid() ) {
|
|
chargeShot->SetShaderParm( 9, 1 );
|
|
chargeShot->SetShaderParm( 10, chargeShotSize );
|
|
chargeShotSize += spawnArgs.GetFloat( "chargeSizeDelta" );
|
|
chargeShot->GetPhysics()->DisableClip();
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_StartChargeShot(void) {
|
|
//spawn chargeshot projectile
|
|
const idDict *projectileDef = gameLocal.FindEntityDefDict( spawnArgs.GetString("def_chargeshot") );
|
|
if ( projectileDef ) {
|
|
chargeShot = hhProjectile::SpawnProjectile( projectileDef );
|
|
if ( chargeShot.IsValid() ) {
|
|
chargeShotSize = spawnArgs.GetFloat( "chargeSizeStart", "1.0" );
|
|
idVec3 launchStart = GetPhysics()->GetOrigin() + spawnArgs.GetVector( "chargeOffset", "15 0 0" ) * GetRenderEntity()->axis;
|
|
chargeShot->Create(this, launchStart, GetRenderEntity()->axis);
|
|
chargeShot->Launch(chargeShot->GetPhysics()->GetOrigin(), chargeShot->GetPhysics()->GetAxis(), vec3_zero );
|
|
chargeShot->Bind(this, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_EndChargeShot(void) {
|
|
//launch projectile
|
|
if ( chargeShot.IsValid() ) {
|
|
chargeShot->Unbind();
|
|
chargeShot->StartTracking();
|
|
chargeShot.Clear();
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_StartBurst(void) {
|
|
gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), this, this, this, this, spawnArgs.GetString("def_burstdamage") );
|
|
|
|
//burst effect
|
|
idMat3 dir;
|
|
if ( enemy.IsValid() ) {
|
|
dir = (enemy->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).ToMat3();
|
|
} else {
|
|
dir = GetPhysics()->GetAxis();
|
|
}
|
|
for ( int i = 0; i < beamBurstList.Num(); i++ ) {
|
|
if ( beamBurstList[i].IsValid() ) {
|
|
beamBurstList[i]->Activate( true );
|
|
beamBurstList[i]->SetTargetLocation( beamBurstList[i]->GetOrigin() + burstLength * hhUtils::RandomSpreadDir( hhUtils::RandomVector().ToMat3(), burstSpread ) );
|
|
}
|
|
}
|
|
PostEventSec(&EV_EndBurst, burstDuration);
|
|
}
|
|
|
|
void hhDroid::Event_EndBurst(void) {
|
|
for ( int i = 0; i < beamBurstList.Num(); i ++ ) {
|
|
if ( beamBurstList[i].IsValid() ) {
|
|
beamBurstList[i]->Activate( false );
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_EndStaticBeam(void) {
|
|
if ( beamBurstList[0].IsValid() ) {
|
|
beamBurstList[0]->Activate( false );
|
|
}
|
|
if ( staticPoint.IsValid() ) {
|
|
staticPoint.Clear();
|
|
}
|
|
|
|
//wait random time until next static beam
|
|
if ( !IsHidden() && !AI_DEAD ) {
|
|
PostEventSec(&EV_StartStaticBeam, gameLocal.random.RandomFloat() * spawnArgs.GetFloat( "staticFreq", "2.5" ) );
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_StartStaticBeam(void) {
|
|
idVec3 targetPoint;
|
|
idEntity *entityList[ MAX_GENTITIES ];
|
|
|
|
if ( IsHidden() || AI_DEAD ) {
|
|
return;
|
|
}
|
|
|
|
//find nearest staticPoint
|
|
float radius = spawnArgs.GetFloat( "staticRange" );
|
|
int listedEntities = gameLocal.EntitiesWithinRadius( GetPhysics()->GetOrigin(), radius, entityList, MAX_GENTITIES );
|
|
for( int i = 0; i < listedEntities; i++ ) {
|
|
if ( entityList[ i ] && entityList[ i ]->spawnArgs.GetInt( "droidBeam", "0" ) ) {
|
|
staticPoint = entityList[ i ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
//activate one beam
|
|
if ( staticPoint.IsValid() && beamBurstList[0].IsValid() ) {
|
|
StartSound( "snd_staticBeam", SND_CHANNEL_BODY, 0, true, NULL );
|
|
beamBurstList[0]->Activate( true );
|
|
beamBurstList[0]->SetTargetLocation( beamBurstList[0]->GetOrigin() + burstLength * hhUtils::RandomVector() );
|
|
staticPoint->ActivateTargets( this );
|
|
PostEventSec(&EV_EndStaticBeam, staticDuration);
|
|
}
|
|
}
|
|
|
|
void hhDroid::FlyTurn( void ) {
|
|
if ( AI_PATHING ) {
|
|
hhMonsterAI::FlyTurn();
|
|
return;
|
|
}
|
|
if ( AI_ENEMY_VISIBLE || 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 hhDroid::Event_StartPathing(void) {
|
|
AI_PATHING = true;
|
|
old_fly_bob_strength = fly_bob_strength;
|
|
fly_bob_strength = 0.0;
|
|
ignore_obstacles = true;
|
|
}
|
|
|
|
void hhDroid::Event_EndPathing(void) {
|
|
AI_PATHING = false;
|
|
fly_bob_strength = old_fly_bob_strength;
|
|
ignore_obstacles = false;
|
|
}
|
|
|
|
void hhDroid::FlyMove( void ) {
|
|
idVec3 goalPos;
|
|
idVec3 oldorigin;
|
|
idVec3 newDest;
|
|
|
|
AI_BLOCKED = false;
|
|
if ( ( move.moveCommand != MOVE_NONE ) && ReachedPos( move.moveDest, move.moveCommand ) ) {
|
|
if ( AI_PATHING ) {
|
|
physicsObj.SetLinearVelocity( idVec3(0,0,0) );
|
|
}
|
|
StopMove( MOVE_STATUS_DONE );
|
|
}
|
|
|
|
if ( move.moveCommand != MOVE_TO_POSITION_DIRECT ) {
|
|
idVec3 vel = physicsObj.GetLinearVelocity();
|
|
|
|
if ( GetMovePos( goalPos ) ) {
|
|
move.obstacle = NULL;
|
|
}
|
|
|
|
if ( move.speed ) {
|
|
FlySeekGoal( vel, goalPos );
|
|
}
|
|
|
|
// add in bobbing
|
|
AddFlyBob( vel );
|
|
|
|
if ( enemy.GetEntity() && ( move.moveCommand != MOVE_TO_POSITION ) ) {
|
|
float dist = ( GetPhysics()->GetOrigin() - enemy->GetPhysics()->GetOrigin() ).LengthFast();
|
|
if ( dist < enemyRange && enemyRange > 0 ) {
|
|
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 );
|
|
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();
|
|
}
|
|
}
|
|
|
|
idProjectile *hhDroid::LaunchProjectile( const char *jointname, idEntity *target, bool clampToAttackCone, const idDict* desiredProjectileDef ) {
|
|
//jsh overridden to allow per-projectile accuracy
|
|
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
|
|
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 ( !AI_WALLWALK && 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 hhDroid::Event_FlyZip() {
|
|
idVec3 old_origin = GetOrigin();
|
|
hhMonsterAI::Event_FlyZip();
|
|
if ( old_origin != GetOrigin() ) {
|
|
if ( beamZip.IsValid() ) {
|
|
beamZip->SetTargetLocation( old_origin );
|
|
}
|
|
if ( beamZip.IsValid() ) {
|
|
beamZip->Activate( true );
|
|
}
|
|
PostEventSec( &EV_EndZipBeams, spawnArgs.GetFloat( "zip_beam_duration", "1.0" ) );
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_EndZipBeams() {
|
|
if ( beamZip.IsValid() ) {
|
|
beamZip->Activate( false );
|
|
}
|
|
}
|
|
|
|
void hhDroid::Event_HealerReset() {
|
|
fl.takedamage = true;
|
|
}
|
|
|
|
void hhDroid::Killed(idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
|
|
if ( bHealer ) {
|
|
lives--;
|
|
if ( lives <= 0 ) {
|
|
hhMonsterAI::Killed( inflictor, attacker, damage, dir, location );
|
|
} else {
|
|
const char *defName = spawnArgs.GetString( "fx_pain" );
|
|
if (defName && defName[0]) {
|
|
hhFxInfo fxInfo;
|
|
fxInfo.RemoveWhenDone( true );
|
|
idEntityFx *painFx = SpawnFxLocal( defName, GetOrigin(), GetAxis(), &fxInfo, gameLocal.isClient );
|
|
if ( painFx ) {
|
|
painFx->Bind( this, true );
|
|
}
|
|
}
|
|
health = spawnArgs.GetInt( "health" );
|
|
fl.takedamage = false;
|
|
PostEventSec( &EV_HealerReset, spawnArgs.GetFloat( "reset_delay", "0.8" ) );
|
|
}
|
|
return;
|
|
}
|
|
if ( AI_DEAD ) {
|
|
AI_DAMAGE = true;
|
|
return;
|
|
}
|
|
hhMonsterAI::Killed( inflictor, attacker, damage, dir, location );
|
|
for ( int i = 0; i < beamBurstList.Num(); i ++ ) {
|
|
if ( beamBurstList[i].IsValid() ) {
|
|
beamBurstList[i]->Activate( true );
|
|
beamBurstList[i]->SetTargetLocation( beamBurstList[i]->GetOrigin() + burstLength * hhUtils::RandomSpreadDir( dir.ToMat3(), burstSpread ) );
|
|
PostEventSec(&EV_EndBurst, staticDuration);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhDroid::AdjustFlySpeed( idVec3 &vel ) {
|
|
float speed;
|
|
|
|
// apply dampening
|
|
vel -= vel * flyDampening * 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;
|
|
}
|
|
|
|
void hhDroid::UpdateModelTransform( void ) {
|
|
idVec3 origin;
|
|
idMat3 axis;
|
|
|
|
if ( GetPhysicsToVisualTransform( origin, axis ) ) {
|
|
renderEntity.axis = axis * GetPhysics()->GetAxis();
|
|
renderEntity.origin = GetPhysics()->GetOrigin() + origin * renderEntity.axis;
|
|
|
|
if ( bHealer ) {
|
|
renderEntity.axis = idAngles( 0, 0, spinAngle ).ToMat3() * renderEntity.axis;
|
|
spinAngle += 10;
|
|
if ( spinAngle > 360.0f ) {
|
|
spinAngle = 0.0f;
|
|
}
|
|
}
|
|
} else {
|
|
renderEntity.axis = GetPhysics()->GetAxis();
|
|
renderEntity.origin = GetPhysics()->GetOrigin();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhDroid::Save
|
|
=====================
|
|
*/
|
|
void hhDroid::Save( idSaveGame *savefile ) const {
|
|
int num = beamBurstList.Num();
|
|
savefile->WriteInt( num );
|
|
for ( int i = 0; i < num; i++ ) {
|
|
beamBurstList[i].Save( savefile );
|
|
}
|
|
|
|
staticPoint.Save( savefile );
|
|
chargeShot.Save( savefile );
|
|
beamZip.Save( savefile );
|
|
|
|
savefile->WriteVec3( savedGravity );
|
|
savefile->WriteFloat( chargeShotSize );
|
|
savefile->WriteFloat( burstLength );
|
|
savefile->WriteFloat( burstSpread );
|
|
savefile->WriteFloat( burstDuration );
|
|
savefile->WriteFloat( staticDuration );
|
|
savefile->WriteFloat( staticRange );
|
|
savefile->WriteInt( numBurstBeams );
|
|
savefile->WriteVec3( beamOffset );
|
|
savefile->WriteFloat( old_fly_bob_strength );
|
|
savefile->WriteFloat( enemyRange );
|
|
savefile->WriteFloat( flyDampening );
|
|
savefile->WriteFloat( spinAngle );
|
|
savefile->WriteBool( bHealer );
|
|
savefile->WriteInt( lives );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhDroid::Restore
|
|
=====================
|
|
*/
|
|
void hhDroid::Restore( idRestoreGame *savefile ) {
|
|
int num;
|
|
savefile->ReadInt( num );
|
|
beamBurstList.SetNum( num );
|
|
for ( int i = 0; i < num; i++ ) {
|
|
beamBurstList[i].Restore( savefile );
|
|
}
|
|
|
|
staticPoint.Restore( savefile );
|
|
chargeShot.Restore( savefile );
|
|
beamZip.Restore( savefile );
|
|
|
|
savefile->ReadVec3( savedGravity );
|
|
savefile->ReadFloat( chargeShotSize );
|
|
savefile->ReadFloat( burstLength );
|
|
savefile->ReadFloat( burstSpread );
|
|
savefile->ReadFloat( burstDuration );
|
|
savefile->ReadFloat( staticDuration );
|
|
savefile->ReadFloat( staticRange );
|
|
savefile->ReadInt( numBurstBeams );
|
|
savefile->ReadVec3( beamOffset );
|
|
savefile->ReadFloat( old_fly_bob_strength );
|
|
savefile->ReadFloat( enemyRange );
|
|
savefile->ReadFloat( flyDampening );
|
|
savefile->ReadFloat( spinAngle );
|
|
savefile->ReadBool( bHealer );
|
|
savefile->ReadInt( lives );
|
|
}
|
|
#endif //HUMANHEAD jsh PCF 5/26/06: code removed for demo build
|