Files
DoomRTX/neo/prey/Prey/prey_projectiletracking.cpp
2026-05-05 15:01:01 -07:00

330 lines
10 KiB
C++

#include "precompiled.h"
#pragma hdrstop
#include "prey_local.h"
/***********************************************************************
hhProjectileTracking
***********************************************************************/
const idEventDef EV_Guide( "<guide>" );
const idEventDef EV_Hover( "<hover>" );
const idEventDef EV_StartTracking( "<startTracking>" );
const idEventDef EV_StopTracking( "<stopTracking>" );
CLASS_DECLARATION( hhProjectile, hhProjectileTracking )
EVENT( EV_Guide, hhProjectileTracking::Event_TrackTarget )
EVENT( EV_Hover, hhProjectileTracking::Event_Hover )
EVENT( EV_StartTracking, hhProjectileTracking::Event_StartTracking )
EVENT( EV_StopTracking, hhProjectileTracking::Event_StopTracking )
EVENT( EV_Collision_Flesh, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Metal, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_AltMetal, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Wood, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Stone, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Glass, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Liquid, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_CardBoard, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Tile, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Forcefield, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Chaff, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Wallwalk, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_Collision_Pipe, hhProjectileTracking::Event_Collision_Explode )
EVENT( EV_AllowCollision_Chaff, hhProjectileTracking::Event_AllowCollision_Collide )
END_CLASS
hhProjectileTracking::hhProjectileTracking() {
spinAngle = 0.0f; // Needs to be initialized before spawn since GetPhysicsToVisualTransform() is called before spawn
}
/*
================
hhProjectileTracking::Spawn
================
*/
void hhProjectileTracking::Spawn() {
angularVelocity.Zero();
velocity.Zero();
cachedFovCos = idMath::Cos( DEG2RAD(spawnArgs.GetFloat("fov", "90")) );
turnFactor = spawnArgs.GetFloat("turnfactor");
updateRate = spawnArgs.GetInt("trackingUpdateRate");
turnFactorAcc = spawnArgs.GetFloat( "turn_factor_accel", "1.1" );
spinDelta = spawnArgs.GetFloat( "spin_delta" );
//rww - some things are hhProjectileTracking when they shouldn't be or don't have proper properties. if this is so, complain.
if (!updateRate) {
gameLocal.Warning("hhProjectileTracking with an updateRate of 0 (possible infinite event queue).");
}
}
/*
================
hhProjectileTracking::IsEnemyValid
================
*/
bool hhProjectileTracking::EnemyIsValid( idEntity* ent ) const {
if ( ent && ent->IsType( idActor::Type ) ) {
idActor *entActor = static_cast<idActor*>(ent);
return ( entActor->GetHealth() > 0 && entActor->team!= 0 && !entActor->IsHidden() && gameLocal.InPlayerPVS(entActor) );
}
return ( ent && ent->GetHealth() > 0 && !ent->IsHidden() && gameLocal.InPlayerPVS(ent) );
}
/*
================
hhProjectileTracking::WhosClosest
================
*/
idEntity* hhProjectileTracking::WhosClosest( idEntity* possibleEnemy, idEntity* currentEnemy, float& currentEnemyDot, float& currentEnemyDist ) const {
if( !possibleEnemy ) {
return currentEnemy;
}
idVec3 enemyDir = DetermineEnemyPosition( possibleEnemy ) - GetOrigin();
float cachedDist = enemyDir.Normalize();
float cachedDot = enemyDir * GetAxis()[0];
//AOB: Think about putting in tolerances for deciding which is better,
if( cachedDot > cachedFovCos && cachedDot > currentEnemyDot && cachedDist <= currentEnemyDist ) {
currentEnemyDot = cachedDot;
currentEnemyDist = cachedDist;
return possibleEnemy;
}
return currentEnemy;
}
/*
================
hhProjectileTracking::DetermineEnemy
================
*/
idEntity* hhProjectileTracking::DetermineEnemy() {
idEntity* possibleEnemy = NULL;
float currentEnemyDot = 0.0f;
float currentEnemyDist = CM_MAX_TRACE_DIST;
idEntity* localEnemy = NULL;
if ( spawnArgs.GetInt( "trackPlayersOnly", "0" ) ) {
for( int i = 0; i < gameLocal.numClients; i++ ) {
if ( gameLocal.entities[ i ] ) {
possibleEnemy = gameLocal.entities[i];
if ( possibleEnemy && possibleEnemy->IsType( hhPlayer::Type ) ) {
hhPlayer *player = static_cast<hhPlayer*>(possibleEnemy);
if ( player && player->IsSpiritOrDeathwalking() ) {
possibleEnemy = player->GetSpiritProxy();
}
}
localEnemy = WhosClosest( possibleEnemy, localEnemy, currentEnemyDot, currentEnemyDist );
}
}
return localEnemy;
}
int num = hhMonsterAI::allSimpleMonsters.Num();
for( int index = 0; index < num; ++index ) {
possibleEnemy = hhMonsterAI::allSimpleMonsters[ index ];
if( EnemyIsValid(possibleEnemy) ) {
localEnemy = WhosClosest( possibleEnemy, localEnemy, currentEnemyDot, currentEnemyDist );
}
}
return localEnemy;
}
/*
================
hhProjectileTracking::Launch
================
*/
void hhProjectileTracking::Launch( const idVec3 &start, const idMat3 &axis, const idVec3 &pushVelocity, const float timeSinceFire, const float launchPower, const float dmgPower ) {
hhProjectile::Launch( start, axis, pushVelocity, timeSinceFire, launchPower, dmgPower );
enemy = DetermineEnemy();
float randomStartSpread = spawnArgs.GetFloat( "randomStartSpread", "0" );
if ( randomStartSpread > 0.0 ) {
velocity = spawnArgs.GetVector( "velocity" ).Length() * hhUtils::RandomSpreadDir( GetPhysics()->GetAxis(), 1.0 );
} else {
velocity = spawnArgs.GetVector( "velocity" );
}
angularVelocity = spawnArgs.GetAngles( "angular_velocity" );
float trackDelay = spawnArgs.GetFloat( "trackDelay", "0" );
if( trackDelay > 0.0 ) {
PostEventSec( &EV_Hover, spawnArgs.GetFloat( "trackStop", "0" ) );
PostEventSec( &EV_StartTracking, trackDelay );
} else if ( enemy.IsValid() ) {
PostEventMS( &EV_Guide, updateRate );
}
float trackDuration = spawnArgs.GetFloat( "trackDuration", "0" );
if ( trackDuration > 0.0 ) {
PostEventSec( &EV_StopTracking, trackDuration );
}
}
void hhProjectileTracking::Event_Hover() {
physicsObj.SetLinearVelocity( velocity * spawnArgs.GetFloat( "hoverScale", "0.15" ) );
}
void hhProjectileTracking::Event_StartTracking() {
float randomStartSpread = spawnArgs.GetFloat( "randomStartSpread", "0" );
if ( randomStartSpread > 0.0 ) {
velocity = spawnArgs.GetVector( "velocity" );
velocity.y = gameLocal.random.CRandomFloat() * randomStartSpread;
velocity.z = gameLocal.random.CRandomFloat() * randomStartSpread;
}
PostEventMS( &EV_Guide, updateRate );
}
void hhProjectileTracking::Event_StopTracking() {
CancelEvents(&EV_Guide);
}
/*
================
hhProjectileTracking::DetermineEnemyPosition
================
*/
idVec3 hhProjectileTracking::DetermineEnemyPosition( const idEntity* ent ) const {
if ( ent && ent->IsType( idActor::Type ) ) {
const idActor *entActor = static_cast<const idActor*>(ent);
return entActor->GetEyePosition();
}
return ent->GetOrigin();
}
/*
================
hhProjectileTracking::DetermineEnemyDir
================
*/
idVec3 hhProjectileTracking::DetermineEnemyDir( const idEntity* actor ) const {
idVec3 enemyPos = DetermineEnemyPosition( actor );
idVec3 enemyDir = enemyPos - GetOrigin();
enemyDir.Normalize();
return enemyDir;
}
/*
================
hhProjectileTracking::Explode
================
*/
void hhProjectileTracking::Explode( const trace_t *collision, const idVec3& velocity, int removeDelay ) {
hhProjectile::Explode( collision, velocity, removeDelay );
CancelEvents(&EV_Guide);
}
/*
================
hhProjectileTracking::GetPhysicsToVisualTransform
================
*/
bool hhProjectileTracking::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
if ( spinDelta != 0.0f ) {
axis = idAngles(0,0,spinAngle).ToMat3();
spinAngle += spinDelta;
if ( spinAngle > 360.0f ) {
spinAngle = 0.0f;
}
return true;
}
return hhProjectile::GetPhysicsToVisualTransform( origin, axis );
}
/*
================
hhProjectileTracking::Event_TrackTarget
================
*/
void hhProjectileTracking::Event_TrackTarget() {
if ( !spawnArgs.GetInt( "constantEnemy", "1" ) ) {
enemy = DetermineEnemy();
}
if( !enemy.IsValid() ) {
physicsObj.SetLinearVelocity( GetAxis()[ 0 ] * velocity[ 0 ] + GetAxis()[ 1 ] * velocity[ 1 ] + GetAxis()[ 2 ] * velocity[ 2 ] );
physicsObj.SetAngularVelocity( angularVelocity.ToAngularVelocity() * GetAxis() );
return;
}
if( !enemy->GetHealth() ) {
//if enemy is dead, just explode
trace_t collision;
memset( &collision, 0, sizeof( collision ) );
collision.endAxis = GetPhysics()->GetAxis();
collision.endpos = GetPhysics()->GetOrigin();
collision.c.point = GetPhysics()->GetOrigin();
collision.c.normal.Set( 0, 0, 1 );
Explode( &collision, idVec3(0,0,0), 3 );
}
if ( turnFactor < 1.0 && turnFactorAcc > 1.0 ) {
turnFactor *= turnFactorAcc; // Accelerate the turn as time goes on so we don't get stuck in any orbits
}
idVec3 enemyDir = DetermineEnemyDir( enemy.GetEntity() );
idVec3 currentDir = GetAxis()[0];
idVec3 newDir = currentDir*(1-turnFactor) + enemyDir*turnFactor;
newDir.Normalize();
if ( driver.IsValid() ) {
driver->SetAxis(newDir.ToMat3());
} else {
SetAxis(newDir.ToMat3());
}
physicsObj.SetLinearVelocity( GetAxis()[ 0 ] * velocity[ 0 ] + GetAxis()[ 1 ] * velocity[ 1 ] + GetAxis()[ 2 ] * velocity[ 2 ] );
physicsObj.SetAngularVelocity( angularVelocity.ToAngularVelocity() * GetAxis() );
PostEventMS( &EV_Guide, updateRate );
}
/*
================
hhProjectileTracking::Save
================
*/
void hhProjectileTracking::Save( idSaveGame *savefile ) const {
savefile->WriteFloat( turnFactor );
savefile->WriteInt( updateRate );
enemy.Save( savefile );
savefile->WriteAngles( angularVelocity );
savefile->WriteVec3( velocity );
savefile->WriteFloat( cachedFovCos );
savefile->WriteFloat( spinAngle );
savefile->WriteFloat( turnFactorAcc );
savefile->WriteFloat( spinDelta );
}
/*
================
hhProjectileTracking::Restore
================
*/
void hhProjectileTracking::Restore( idRestoreGame *savefile ) {
savefile->ReadFloat( turnFactor );
savefile->ReadInt( updateRate );
enemy.Restore( savefile );
savefile->ReadAngles( angularVelocity );
savefile->ReadVec3( velocity );
savefile->ReadFloat( cachedFovCos );
savefile->ReadFloat( spinAngle );
savefile->ReadFloat( turnFactorAcc );
savefile->ReadFloat( spinDelta );
}
void hhProjectileTracking::StartTracking() {
Event_StartTracking();
}