mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
1049 lines
27 KiB
C++
1049 lines
27 KiB
C++
#include "precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "prey_local.h"
|
|
|
|
const idEventDef EV_ScanForClosestTarget( "<scanForClosestTarget>" );
|
|
const idEventDef EV_StartAttack( "<startAttack>" );
|
|
const idEventDef EV_NextCycleAnim( "<nextCycleAnim>", "ds" );
|
|
|
|
CLASS_DECLARATION( hhAnimatedEntity, hhMountedGun )
|
|
EVENT( EV_Activate, hhMountedGun::Event_Activate )
|
|
EVENT( EV_Deactivate, hhMountedGun::Event_Deactivate )
|
|
EVENT( EV_PostSpawn, hhMountedGun::Event_PostSpawn )
|
|
EVENT( EV_ScanForClosestTarget, hhMountedGun::Event_ScanForClosestTarget )
|
|
EVENT( EV_StartAttack, hhMountedGun::Event_StartAttack )
|
|
|
|
EVENT( EV_NextCycleAnim, hhMountedGun::Event_NextCycleAnim )
|
|
END_CLASS
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::hhMountedGun
|
|
================
|
|
*/
|
|
hhMountedGun::hhMountedGun() {
|
|
yawVelocity = 0.0f;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::Spawn
|
|
================
|
|
*/
|
|
void hhMountedGun::Spawn( void ) {
|
|
targetingLaser = NULL;
|
|
boneHub.Clear();
|
|
boneGun.Clear();
|
|
boneOrigin.Clear();
|
|
|
|
fl.takedamage = false; // Gun doesn't take damage unless it is active
|
|
|
|
deathwalkDelay = SEC2MS( spawnArgs.GetInt( "deathwalk_delay", "1" ) );
|
|
burstRateOfFire = SEC2MS( spawnArgs.GetFloat("burstRateOfFire") );
|
|
shotsPerBurst = spawnArgs.GetInt("shotsPerBurst");
|
|
shotsPerBurstCounter = 0;
|
|
|
|
firingRange = spawnArgs.GetFloat( "firingRange" );
|
|
nextFireTime = 0;
|
|
projectileDict = gameLocal.FindEntityDefDict( spawnArgs.GetString( "def_projectile" ) );
|
|
if ( !projectileDict ) {
|
|
gameLocal.Error( "Unknown def_projectile: %s\n", spawnArgs.GetString( "def_projectile" ) );
|
|
}
|
|
|
|
// Set up muzzle flash light
|
|
memset( &muzzleFlash, 0, sizeof(renderLight_t) );
|
|
muzzleFlash.lightId = 500 + entityNumber;
|
|
muzzleFlash.shader = declManager->FindMaterial( spawnArgs.GetString("mtr_flashShader"), false );
|
|
muzzleFlashHandle = -1;
|
|
muzzleFlash.pointLight = true;
|
|
muzzleFlashEnd = gameLocal.GetTime();
|
|
muzzleFlash.lightRadius = spawnArgs.GetVector( "flashSize" );
|
|
muzzleFlashDuration = SEC2MS( spawnArgs.GetFloat("flashTime") );
|
|
idVec3 flashColor = spawnArgs.GetVector( "flashColor" );
|
|
muzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0];
|
|
muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1];
|
|
muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2];
|
|
muzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
|
|
|
|
SpawnParts();
|
|
|
|
physicsObj.SetSelf( this );
|
|
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
|
|
physicsObj.SetContents( CONTENTS_SOLID );
|
|
physicsObj.SetClipMask( GetPhysics()->GetContents() );
|
|
physicsObj.SetOrigin( GetOrigin() );
|
|
physicsObj.SetAxis( GetAxis() );
|
|
SetPhysics( &physicsObj );
|
|
|
|
prevIdealLookAngle.Zero();
|
|
|
|
pitchController.Clear();
|
|
pitchController.Setup( this, spawnArgs.GetString("bone_Hub"), idAngles(-90, 0, 0), idAngles(90, 0, 0), idAngles(90, 0, 0), idAngles(1,0,0) );
|
|
|
|
channelBody = ChannelName2Num( "body" );
|
|
channelBarrel = ChannelName2Num( "barrel" );
|
|
|
|
PlayCycle( channelBody, "idle", 0 );
|
|
|
|
PVSArea = gameLocal.pvs.GetPVSArea( GetOrigin() );
|
|
|
|
PostEventMS( &EV_PostSpawn, 0 );
|
|
|
|
gunMode = GM_DORMANT;
|
|
team = spawnArgs.GetInt( "team", "1" );
|
|
|
|
PlayAnim( ANIMCHANNEL_ALL, "idle_close", 0 );
|
|
}
|
|
|
|
void hhMountedGun::Save(idSaveGame *savefile) const {
|
|
savefile->WriteInt( team );
|
|
savefile->WriteInt( gunMode );
|
|
pitchController.Save( savefile );
|
|
trackMover.Save( savefile );
|
|
targetingLaser.Save( savefile );
|
|
boneHub.Save( savefile );
|
|
boneGun.Save( savefile );
|
|
boneExhaust.Save( savefile );
|
|
boneOrigin.Save( savefile );
|
|
enemy.Save( savefile );
|
|
savefile->WriteFloat( enemyRange );
|
|
savefile->WriteFloat( firingRange );
|
|
savefile->WriteInt( nextFireTime );
|
|
savefile->WriteFloat( burstRateOfFire );
|
|
savefile->WriteInt( shotsPerBurst );
|
|
savefile->WriteInt( shotsPerBurstCounter );
|
|
savefile->WriteRenderLight( muzzleFlash );
|
|
//HUMANHEAD PCF mdl 05/04/06 - Don't save light handles
|
|
//savefile->WriteInt( muzzleFlashHandle );
|
|
savefile->WriteInt( muzzleFlashEnd );
|
|
savefile->WriteInt( muzzleFlashDuration );
|
|
savefile->WriteAngles( idealLookAngle );
|
|
savefile->WriteAngles( prevIdealLookAngle );
|
|
savefile->WriteStaticObject( physicsObj );
|
|
savefile->WriteInt( animDoneTime );
|
|
savefile->WriteInt( PVSArea );
|
|
savefile->WriteInt( channelBody );
|
|
savefile->WriteInt( channelBarrel );
|
|
savefile->WriteFloat( yawVelocity );
|
|
savefile->WriteInt( deathwalkDelay );
|
|
}
|
|
|
|
void hhMountedGun::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadInt( team );
|
|
savefile->ReadInt( reinterpret_cast<int &> ( gunMode ) );
|
|
pitchController.Restore( savefile );
|
|
trackMover.Restore( savefile );
|
|
targetingLaser.Restore( savefile );
|
|
boneHub.Restore( savefile );
|
|
boneGun.Restore( savefile );
|
|
boneExhaust.Restore( savefile );
|
|
boneOrigin.Restore( savefile );
|
|
enemy.Restore( savefile );
|
|
savefile->ReadFloat( enemyRange );
|
|
savefile->ReadFloat( firingRange );
|
|
savefile->ReadInt( nextFireTime );
|
|
savefile->ReadFloat( burstRateOfFire );
|
|
savefile->ReadInt( shotsPerBurst );
|
|
savefile->ReadInt( shotsPerBurstCounter );
|
|
savefile->ReadRenderLight( muzzleFlash );
|
|
//HUMANHEAD PCF mdl 05/04/06 - Don't save light handles
|
|
//savefile->ReadInt( muzzleFlashHandle );
|
|
savefile->ReadInt( muzzleFlashEnd );
|
|
savefile->ReadInt( muzzleFlashDuration );
|
|
savefile->ReadAngles( idealLookAngle );
|
|
savefile->ReadAngles( prevIdealLookAngle );
|
|
savefile->ReadStaticObject( physicsObj );
|
|
savefile->ReadInt( animDoneTime );
|
|
savefile->ReadInt( PVSArea );
|
|
savefile->ReadInt( channelBody );
|
|
savefile->ReadInt( channelBarrel );
|
|
savefile->ReadFloat( yawVelocity );
|
|
savefile->ReadInt( deathwalkDelay );
|
|
|
|
projectileDict = gameLocal.FindEntityDefDict( spawnArgs.GetString( "def_projectile" ) );
|
|
if ( !projectileDict ) {
|
|
gameLocal.Error( "Unknown def_projectile: %s\n", spawnArgs.GetString( "def_projectile" ) );
|
|
}
|
|
RestorePhysics( &physicsObj );
|
|
//HUMANHEAD PCF mdl 05/04/06 - Don't save light handles
|
|
muzzleFlashHandle = -1;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::~hhMountedGun
|
|
================
|
|
*/
|
|
hhMountedGun::~hhMountedGun( void ) {
|
|
SAFE_REMOVE( targetingLaser );
|
|
|
|
SAFE_FREELIGHT( muzzleFlashHandle );
|
|
|
|
trackMover = NULL;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::SpawnParts
|
|
================
|
|
*/
|
|
void hhMountedGun::SpawnParts() {
|
|
boneHub.name = spawnArgs.GetString("bone_Hub");
|
|
boneHub.handle = GetAnimator()->GetJointHandle( boneHub.name.c_str() );
|
|
|
|
boneGun.name = spawnArgs.GetString("bone_Gun");
|
|
boneGun.handle = GetAnimator()->GetJointHandle( boneGun.name.c_str() );
|
|
|
|
boneOrigin.name = spawnArgs.GetString("bone_Origin");
|
|
boneOrigin.handle = GetAnimator()->GetJointHandle( boneOrigin.name.c_str() );
|
|
|
|
UpdateBoneInfo();
|
|
|
|
targetingLaser = hhBeamSystem::SpawnBeam( boneGun.origin, spawnArgs.GetString("beam") );
|
|
if( targetingLaser.IsValid() ) {
|
|
targetingLaser->MoveToJoint( this, boneGun.name.c_str() );
|
|
targetingLaser->BindToJoint( this, boneGun.name.c_str(), false );
|
|
targetingLaser->Activate( false );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Hide
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Hide() {
|
|
hhAnimatedEntity::Hide();
|
|
|
|
if( targetingLaser.IsValid() ) {
|
|
targetingLaser->Hide();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Show
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Show() {
|
|
hhAnimatedEntity::Show();
|
|
|
|
if( targetingLaser.IsValid() ) {
|
|
targetingLaser->Show();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::DetermineTargetingLaserEndPoint
|
|
=====================
|
|
*/
|
|
idVec3 hhMountedGun::DetermineTargetingLaserEndPoint() {
|
|
trace_t traceInfo;
|
|
idVec3 start = boneGun.origin;
|
|
idVec3 dist = boneGun.axis[0] * CM_MAX_TRACE_DIST;
|
|
|
|
gameLocal.clip.TracePoint( traceInfo, start, start + dist, MASK_SHOT_BOUNDINGBOX, this );
|
|
|
|
return (enemy.IsValid()) ? traceInfo.endpos + enemy->GetAxis()[2] * -10.0f : traceInfo.endpos;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Ticker
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Ticker( void ) {
|
|
if( !pitchController.IsFinishedMoving(PITCH) ) {
|
|
pitchController.Update( gameLocal.GetTime() );
|
|
}
|
|
|
|
UpdateBoneInfo();
|
|
|
|
if( targetingLaser.IsValid() && !targetingLaser->IsHidden() ) {
|
|
targetingLaser->SetTargetLocation( DetermineTargetingLaserEndPoint() );
|
|
}
|
|
|
|
UpdateMuzzleFlash();
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::UpdateOrientation
|
|
================
|
|
*/
|
|
void hhMountedGun::UpdateOrientation() {
|
|
SetAngles( idAngles( 0, idealLookAngle.yaw, 0 ) );
|
|
pitchController.TurnTo( idealLookAngle );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::UpdateBoneInfo
|
|
================
|
|
*/
|
|
void hhMountedGun::UpdateBoneInfo() {
|
|
idVec3 TempVec;
|
|
|
|
GetJointWorldTransform( boneHub.handle, boneHub.origin, boneHub.axis );
|
|
GetJointWorldTransform( boneGun.handle, boneGun.origin, boneGun.axis );
|
|
|
|
GetJointWorldTransform( boneOrigin.handle, boneOrigin.origin, boneOrigin.axis );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::SetIdealLookAngle
|
|
================
|
|
*/
|
|
void hhMountedGun::SetIdealLookAngle( const idAngles& LookAngle ) {
|
|
idealLookAngle.pitch = -hhMath::AngleNormalize180( LookAngle.pitch );
|
|
idealLookAngle.yaw = hhMath::AngleNormalize360( LookAngle.yaw );
|
|
idealLookAngle.roll = 0.0f;
|
|
|
|
if( prevIdealLookAngle == idealLookAngle ) {
|
|
return;
|
|
}
|
|
|
|
prevIdealLookAngle = idealLookAngle;
|
|
|
|
UpdateOrientation();
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::AttemptToRemoveMuzzleFlash
|
|
================
|
|
*/
|
|
void hhMountedGun::AttemptToRemoveMuzzleFlash() {
|
|
if( gameLocal.GetTime() >= muzzleFlashEnd ) {
|
|
SAFE_FREELIGHT( muzzleFlashHandle );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::UpdateMuzzleFlash
|
|
================
|
|
*/
|
|
void hhMountedGun::UpdateMuzzleFlash() {
|
|
AttemptToRemoveMuzzleFlash();
|
|
|
|
if( muzzleFlashHandle != -1 ) {
|
|
UpdateMuzzleFlashPosition();
|
|
gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::UpdateMuzzleFlashPosition
|
|
================
|
|
*/
|
|
void hhMountedGun::UpdateMuzzleFlashPosition() {
|
|
muzzleFlash.axis = boneGun.axis;
|
|
muzzleFlash.origin = boneGun.origin;
|
|
|
|
//TEST
|
|
trace_t trace;
|
|
idVec3 flashSize = spawnArgs.GetVector( "flashSize" );
|
|
if( gameLocal.clip.TracePoint(trace, muzzleFlash.origin, muzzleFlash.origin + muzzleFlash.axis[0] * flashSize[0], MASK_SHOT_BOUNDINGBOX, this) ) {
|
|
flashSize[0] *= trace.fraction;
|
|
}
|
|
muzzleFlash.lightRadius = flashSize;
|
|
muzzleFlash.origin += muzzleFlash.axis[0] * flashSize[0] * 0.5f;
|
|
muzzleFlash.lightCenter.Set( flashSize[0] * -0.45f, 0.0f, 0.0f );
|
|
|
|
hhUtils::Swap<float>( muzzleFlash.lightRadius[0], muzzleFlash.lightRadius[2] );
|
|
hhUtils::Swap<float>( muzzleFlash.lightCenter[0], muzzleFlash.lightCenter[2] );
|
|
muzzleFlash.axis = hhUtils::SwapXZ( muzzleFlash.axis );
|
|
//TEST
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::MuzzleFlash
|
|
================
|
|
*/
|
|
void hhMountedGun::MuzzleFlash() {
|
|
if( !muzzleFlash.lightRadius[0] ) {
|
|
return;
|
|
}
|
|
|
|
UpdateMuzzleFlashPosition();
|
|
|
|
// these will be different each fire
|
|
muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.GetTime() );
|
|
muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.RandomFloat();
|
|
|
|
// the light will be removed at this time
|
|
muzzleFlashEnd = gameLocal.GetTime() + muzzleFlashDuration;
|
|
|
|
if ( muzzleFlashHandle != -1 ) {
|
|
gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
|
|
} else {
|
|
muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::TraceTargetVerified
|
|
================
|
|
*/
|
|
bool hhMountedGun::TraceTargetVerified( const idActor* target, int traceEntNum ) const {
|
|
int entNum = target->InVehicle() ? target->GetVehicleInterface()->GetVehicle()->entityNumber : target->entityNumber;
|
|
return entNum == traceEntNum;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::ScanForClosestTarget
|
|
================
|
|
*/
|
|
idActor *hhMountedGun::ScanForClosestTarget() {
|
|
trace_t TraceInfo;
|
|
idEntity *entity;
|
|
idActor *actor;
|
|
hhPlayer *player;
|
|
float firingRangeSquared = firingRange * firingRange;
|
|
float closestDistSqr = firingRangeSquared;
|
|
idActor *closestActor = NULL;
|
|
pvsHandle_t pvs;
|
|
|
|
pvs = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
|
|
|
|
for ( entity = gameLocal.activeEntities.Next(); entity != NULL; entity = entity->activeNode.Next() ) {
|
|
if ( entity->fl.hidden || entity->fl.isDormant || !entity->IsType( idActor::Type ) ) {
|
|
continue;
|
|
}
|
|
|
|
actor = static_cast<idActor *>( entity );
|
|
|
|
// Check if this actor is in the PVS (needed?)
|
|
if ( !gameLocal.pvs.InCurrentPVS( pvs, actor->GetPVSAreas(), actor->GetNumPVSAreas() ) ) {
|
|
continue;
|
|
}
|
|
|
|
// Check if the actor can be damaged and should be targeted by the gun
|
|
// Also, check the actor's team, only player team (humans, player) should be targetted
|
|
if ( !actor->fl.takedamage || actor->GetHealth() <= 0 || actor->team != 0 ) {
|
|
if ( !actor->InVehicle() ) { //HUMANHEAD jsh target vehicles
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Player-specific checks -- ignore the spirit player
|
|
if ( actor->IsType( hhPlayer::Type ) ) {
|
|
player = static_cast<hhPlayer *>( actor );
|
|
if ( !player || player->IsSpiritOrDeathwalking() ) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Calculate the distance to the actor
|
|
float distSqr = (actor->GetEyePosition() - GetOrigin()).LengthSqr();
|
|
|
|
// Ignore actors beyond the max distance
|
|
if( distSqr > firingRangeSquared ) {
|
|
continue;
|
|
}
|
|
|
|
// Only check if this distance is closer than the others
|
|
if( distSqr > closestDistSqr ) {
|
|
continue;
|
|
}
|
|
|
|
// Check if this actor is visible from the gun
|
|
if( !gameLocal.clip.TracePoint(TraceInfo, GetOrigin(), actor->GetEyePosition(), MASK_SHOT_BOUNDINGBOX, this) ) {
|
|
continue;
|
|
}
|
|
|
|
if( !TraceTargetVerified(actor, TraceInfo.c.entityNum) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( actor->IsType( hhPlayer::Type ) ) {
|
|
hhPlayer *player = static_cast<hhPlayer*>(actor);
|
|
if ( player && player->GetLastResurrectTime() ) {
|
|
if ( gameLocal.time < player->GetLastResurrectTime() + deathwalkDelay ) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
closestActor = actor;
|
|
closestDistSqr = distSqr;
|
|
}
|
|
|
|
gameLocal.pvs.FreeCurrentPVS( pvs );
|
|
|
|
return closestActor;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::PlayAnim
|
|
================
|
|
*/
|
|
void hhMountedGun::PlayAnim( int iChannelNum, const char* pAnim, int iBlendTime ) {
|
|
PlayAnim( iChannelNum, GetAnimator()->GetAnim( pAnim ), iBlendTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::PlayAnim
|
|
================
|
|
*/
|
|
void hhMountedGun::PlayAnim( int iChannelNum, int pAnim, int iBlendTime ) {
|
|
ClearAnims( iChannelNum, iBlendTime );
|
|
|
|
GetAnimator()->PlayAnim( iChannelNum, pAnim, gameLocal.GetTime(), iBlendTime );
|
|
animDoneTime = GetAnimator()->CurrentAnim( iChannelNum )->Length();
|
|
animDoneTime = (animDoneTime > iBlendTime) ? animDoneTime - iBlendTime : animDoneTime;
|
|
|
|
animDoneTime += gameLocal.GetTime();
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::PlayCycle
|
|
================
|
|
*/
|
|
void hhMountedGun::PlayCycle( int iChannelNum, const char* pAnim, int iBlendTime ) {
|
|
PlayCycle( iChannelNum, GetAnimator()->GetAnim( pAnim ), iBlendTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::NextCycleAnim
|
|
|
|
// Cycles the next animation in after the current one completes
|
|
================
|
|
*/
|
|
void hhMountedGun::NextCycleAnim( int iChannelNum, const char* pAnim ) {
|
|
PostEventMS( &EV_NextCycleAnim, GetAnimDoneTime() - gameLocal.GetTime(), iChannelNum, pAnim );
|
|
}
|
|
|
|
void hhMountedGun::Event_NextCycleAnim( int iChannelNum, const char* pAnim ) {
|
|
PlayCycle( iChannelNum, pAnim, 0 );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::PlayCycle
|
|
================
|
|
*/
|
|
void hhMountedGun::PlayCycle( int iChannelNum, int pAnim, int iBlendTime ) {
|
|
ClearAnims( iChannelNum, iBlendTime );
|
|
|
|
GetAnimator()->CycleAnim( iChannelNum, pAnim, gameLocal.GetTime(), iBlendTime );
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::ClearAnims
|
|
================
|
|
*/
|
|
void hhMountedGun::ClearAnims( int iChannelNum, int iBlendTime ) {
|
|
GetAnimator()->Clear( iChannelNum, gameLocal.GetTime(), iBlendTime );
|
|
|
|
animDoneTime = 0;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Event_PostSpawn
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Event_PostSpawn() {
|
|
// Find the trackmover if it exists
|
|
trackMover = gameLocal.FindEntity( spawnArgs.GetString("target") );
|
|
if( trackMover.IsValid() && trackMover->IsType( hhTrackMover::Type ) ) { //HUMANHEAD PCF mdl 04/26/06 - Only bind to track movers, in case target is only for triggers
|
|
SetOrigin( trackMover->GetOrigin() );
|
|
Bind( trackMover.GetEntity(), false );
|
|
}
|
|
|
|
SetIdealLookAngle( GetAxis().ToAngles() );
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Event_Activate
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Event_Activate( idEntity* pActivator ) {
|
|
if ( gunMode == GM_DORMANT ) {
|
|
gunMode = GM_IDLE;
|
|
|
|
BecomeActive( TH_THINK | TH_TICKER );
|
|
|
|
ProcessEvent( &EV_ScanForClosestTarget );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Awaken
|
|
=====================
|
|
*/
|
|
|
|
void hhMountedGun::Awaken() {
|
|
if ( gunMode != GM_DEAD ) {
|
|
if( targetingLaser.IsValid() && !targetingLaser->IsActivated() ) {
|
|
targetingLaser->Activate( true );
|
|
}
|
|
|
|
PreAttack();
|
|
fl.takedamage = true; // Gun can now be shot
|
|
|
|
PlayAnim( ANIMCHANNEL_ALL, "open", 0 );
|
|
NextCycleAnim( ANIMCHANNEL_ALL, "idle_open" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Event_Deactivate
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Event_Deactivate() {
|
|
if (gunMode != GM_DEAD) {
|
|
if( targetingLaser.IsValid() && targetingLaser->IsActivated() ) {
|
|
targetingLaser->Activate( false );
|
|
}
|
|
|
|
if( trackMover.IsValid() ) {
|
|
trackMover->ProcessEvent( &EV_Deactivate );
|
|
}
|
|
|
|
CancelEvents( &EV_ScanForClosestTarget );
|
|
CancelEvents( &EV_StartAttack );
|
|
|
|
gunMode = GM_DORMANT;
|
|
fl.takedamage = false;
|
|
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
|
|
PlayAnim( ANIMCHANNEL_ALL, "close", 0 );
|
|
NextCycleAnim( ANIMCHANNEL_ALL, "idle_close" );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Sleep
|
|
|
|
// Very similar to dormant, except the gun continues to think and look for enemies while closed
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Sleep() {
|
|
if( targetingLaser.IsValid() && targetingLaser->IsActivated() ) {
|
|
targetingLaser->Activate( false );
|
|
targetingLaser->SetShaderParm( SHADERPARM_MODE, 0 );
|
|
}
|
|
|
|
// Note: A sleeping gun doesn't disable the track mover
|
|
CancelEvents( &EV_StartAttack );
|
|
|
|
gunMode = GM_IDLE;
|
|
fl.takedamage = false;
|
|
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
|
|
PlayAnim( ANIMCHANNEL_ALL, "close", 0 );
|
|
NextCycleAnim( ANIMCHANNEL_ALL, "idle_close" );
|
|
}
|
|
|
|
//===============================================================================================================
|
|
//===============================================================================================================
|
|
//===============================================================================================================
|
|
//===============================================================================================================
|
|
//===============================================================================================================
|
|
|
|
void hhMountedGun::Think() {
|
|
hhAnimatedEntity::Think();
|
|
|
|
if (thinkFlags & TH_THINK) {
|
|
// Run the current state
|
|
switch ( gunMode ) {
|
|
case GM_DORMANT:
|
|
case GM_IDLE:
|
|
case GM_DEAD:
|
|
break;
|
|
case GM_PREATTACKING:
|
|
TurnTowardsEnemy( DEG2RAD( spawnArgs.GetFloat( "turnMax", "3" ) ), DEG2RAD( spawnArgs.GetFloat( "turnAccel", "0.2" ) ) );
|
|
break;
|
|
case GM_ATTACKING:
|
|
TurnTowardsEnemy( DEG2RAD( spawnArgs.GetFloat( "attackTurnMax", "0.5" ) ), DEG2RAD( spawnArgs.GetFloat( "attackTurnAccel", "0.05" ) ) );
|
|
Attack();
|
|
break;
|
|
case GM_RELOADING:
|
|
// TODO: Handled by an event
|
|
if ( ReadyToFire() ) { // hack!
|
|
PreAttack();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool hhMountedGun::GetFacePosAngle( const idVec3 &sourceOrigin, float angle1, const idVec3 &targetOrigin, float &delta ) {
|
|
float diff;
|
|
float angle2;
|
|
|
|
angle2 = hhUtils::PointToAngle( targetOrigin.x - sourceOrigin.x, targetOrigin.y - sourceOrigin.y );
|
|
if(angle2 > angle1) {
|
|
diff = angle2 - angle1;
|
|
|
|
if( diff > DEG2RAD(180.0f) ) {
|
|
delta = DEG2RAD(359.9f) - diff;
|
|
return false;
|
|
}
|
|
else {
|
|
delta = diff;
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
diff = angle1 - angle2;
|
|
if( diff > DEG2RAD(180.0f) ) {
|
|
delta = DEG2RAD(359.9f) - diff;
|
|
return true;
|
|
}
|
|
else {
|
|
delta = diff;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhMountedGun::TurnTowardsEnemy( float maxYawVelocity, float yawAccel ) {
|
|
idVec3 dirToEnemy;
|
|
idVec3 localDirToEnemy;
|
|
|
|
bool dir;
|
|
float deltaYaw;
|
|
|
|
if( !EnemyIsVisible() ) { // Enemy isn't valid or visible, so go to sleep until the enemy is available again
|
|
return;
|
|
}
|
|
|
|
if ( !enemy->IsType( hhPlayer::Type ) ) { // Turn twice as fast if the enemy is not a player (to guarantee that the gun will kill other humans/creatures)
|
|
maxYawVelocity *= 2.0f;
|
|
yawAccel *= 2.0f;
|
|
}
|
|
|
|
idVec3 currentVec = this->GetAxis()[0];
|
|
float currentYaw = DEG2RAD( currentVec.ToYaw() );
|
|
|
|
// Face toward the enemy
|
|
dir = GetFacePosAngle( GetOrigin(), currentYaw, enemy->GetOrigin(), deltaYaw );
|
|
|
|
// Acceleration
|
|
if ( dir ) {
|
|
yawVelocity += yawAccel;
|
|
} else {
|
|
yawVelocity -= yawAccel;
|
|
}
|
|
|
|
if ( yawVelocity > maxYawVelocity ) {
|
|
yawVelocity = maxYawVelocity;
|
|
} else if ( yawVelocity < -maxYawVelocity ) {
|
|
yawVelocity = -maxYawVelocity;
|
|
}
|
|
|
|
deltaYaw = yawVelocity;
|
|
|
|
// Compute the pitch to the enemy
|
|
dirToEnemy = ( ( enemy->GetEyePosition() + enemy->GetOrigin() ) * 0.5f ) - boneGun.origin;
|
|
dirToEnemy.Normalize();
|
|
GetAxis().ProjectVector( dirToEnemy, localDirToEnemy );
|
|
|
|
SetIdealLookAngle( idAngles( localDirToEnemy.ToPitch(), RAD2DEG( currentYaw + deltaYaw ), 0.0f ) );
|
|
|
|
StartSound( "snd_rotate", SND_CHANNEL_BODY3 );
|
|
}
|
|
|
|
bool hhMountedGun::EnemyIsInRange() {
|
|
idVec3 dirToEnemy;
|
|
|
|
if ( !enemy.IsValid() ) {
|
|
return false;
|
|
}
|
|
|
|
dirToEnemy = enemy->GetEyePosition() - boneHub.origin;
|
|
if ( dirToEnemy.LengthSqr() > firingRange * firingRange ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool hhMountedGun::EnemyCanBeAttacked() {
|
|
if ( !enemy.IsValid() ) { // No enemy
|
|
return false;
|
|
}
|
|
|
|
// Check if the enemy is visible and is alive
|
|
if ( ClearLineOfFire() && enemy->health > 0 ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::Event_ScanForClosestTarget
|
|
=====================
|
|
*/
|
|
void hhMountedGun::Event_ScanForClosestTarget() {
|
|
idActor *entity = ScanForClosestTarget();
|
|
|
|
enemy = entity;
|
|
if ( entity && gunMode == GM_IDLE ) { // Sleeping and found a target
|
|
Awaken();
|
|
}
|
|
else if ( !entity && gunMode != GM_IDLE ) { // No target, so go to sleep if not already sleeping
|
|
Sleep();
|
|
}
|
|
|
|
PostEventSec( &EV_ScanForClosestTarget, spawnArgs.GetFloat("scanPeriod") );
|
|
}
|
|
|
|
void hhMountedGun::PreAttack() {
|
|
gunMode = GM_PREATTACKING;
|
|
|
|
// Change the beam to something more threatening
|
|
targetingLaser->SetShaderParm( SHADERPARM_MODE, 1 );
|
|
|
|
// Play a preattack warning sound
|
|
StartSound( "snd_preattack", SND_CHANNEL_ANY );
|
|
|
|
// Delay before attacking
|
|
PostEventSec( &EV_StartAttack, spawnArgs.GetFloat( "attackDelay", "0.2" ) ); // Delay a bit before attacking
|
|
}
|
|
|
|
void hhMountedGun::Event_StartAttack() {
|
|
gunMode = GM_ATTACKING;
|
|
}
|
|
|
|
void hhMountedGun::Attack() {
|
|
hhPlayer *player = NULL;
|
|
|
|
if ( !enemy.IsValid() ) {
|
|
return;
|
|
}
|
|
|
|
if ( enemy->IsType( hhPlayer::Type ) ) {
|
|
player = static_cast<hhPlayer*>( enemy.GetEntity() );
|
|
if ( player->IsSpiritOrDeathwalking() ) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Actually fire the gun
|
|
if ( ReadyToFire() ) {
|
|
|
|
Fire();
|
|
|
|
shotsPerBurstCounter++;
|
|
if ( shotsPerBurstCounter >= shotsPerBurst || enemy->GetHealth() <= 0 ) {
|
|
Reload();
|
|
}
|
|
}
|
|
}
|
|
|
|
void hhMountedGun::Reload() {
|
|
gunMode = GM_RELOADING;
|
|
PlayAnim( ANIMCHANNEL_ALL, "reload", 0 );
|
|
|
|
nextFireTime = gameLocal.GetTime() + SEC2MS( 2.0f ); // HACK: fake way of currently forcing a wait when reloading
|
|
shotsPerBurstCounter = 0;
|
|
|
|
targetingLaser->SetShaderParm( SHADERPARM_MODE, 0 );
|
|
|
|
if ( enemy->GetHealth() <= 0 ) { // Enemy was killed, so clear it and wait for the gun to find a new enemy
|
|
enemy.Clear();
|
|
}
|
|
|
|
StopSound( SND_CHANNEL_BODY3 ); // Stop the rotation sound
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::DetermineNextFireTime
|
|
=====================
|
|
*/
|
|
int hhMountedGun::DetermineNextFireTime() {
|
|
return gameLocal.GetTime() + burstRateOfFire;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::ReadyToFire
|
|
=====================
|
|
*/
|
|
bool hhMountedGun::ReadyToFire() {
|
|
return gameLocal.GetTime() > nextFireTime;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::Fire
|
|
================
|
|
*/
|
|
void hhMountedGun::Fire() {
|
|
idVec3 LaunchDir;
|
|
idMat3 LaunchAxis;
|
|
|
|
PlayAnim( channelBarrel, "fireA", 0 );
|
|
nextFireTime = DetermineNextFireTime();
|
|
|
|
MuzzleFlash();
|
|
|
|
hhProjectile* pProjectile = hhProjectile::SpawnProjectile( projectileDict );
|
|
if( !pProjectile ) {
|
|
return;
|
|
}
|
|
|
|
LaunchAxis = hhUtils::RandomSpreadDir( boneGun.axis, DEG2RAD(spawnArgs.GetFloat("spread"))).ToMat3();
|
|
pProjectile->Create( this, boneGun.origin, LaunchAxis );
|
|
pProjectile->Launch( boneGun.origin, LaunchAxis, vec3_zero );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::EnemyIsVisible
|
|
|
|
// If the enemy is visible at any angle from the gun
|
|
=====================
|
|
*/
|
|
bool hhMountedGun::EnemyIsVisible() {
|
|
trace_t traceInfo;
|
|
|
|
if( !enemy.IsValid() || !EnemyIsInPVS() ) {
|
|
return false;
|
|
}
|
|
|
|
if( !gameLocal.clip.TracePoint( traceInfo, GetOrigin(), enemy->GetEyePosition(), MASK_SHOT_BOUNDINGBOX, this ) ) {
|
|
return true;
|
|
}
|
|
|
|
if( TraceTargetVerified( enemy.GetEntity(), traceInfo.c.entityNum ) ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::ClearLineOfFire
|
|
|
|
// If the enemy can be shot (line of sight from the gun barrel to the enemy)
|
|
=====================
|
|
*/
|
|
bool hhMountedGun::ClearLineOfFire() {
|
|
trace_t TraceInfo;
|
|
idVec3 BonePos;
|
|
idMat3 BoneAxis;
|
|
|
|
if( !enemy.IsValid() || !EnemyIsInPVS() ) {
|
|
return false;
|
|
}
|
|
|
|
if( !gameLocal.clip.TracePoint(TraceInfo, boneGun.origin, boneGun.origin + boneGun.axis[0] * firingRange, MASK_SHOT_BOUNDINGBOX, this) ) {
|
|
return false;
|
|
}
|
|
|
|
if( !TraceTargetVerified(enemy.GetEntity(), TraceInfo.c.entityNum) ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
hhMountedGun::EnemyIsInPVS
|
|
=====================
|
|
*/
|
|
bool hhMountedGun::EnemyIsInPVS() {
|
|
pvsHandle_t PVSHandle = gameLocal.pvs.SetupCurrentPVS( PVSArea );
|
|
|
|
bool bResult = gameLocal.pvs.InCurrentPVS( PVSHandle, enemy->GetPVSAreas(), enemy->GetNumPVSAreas() );
|
|
|
|
gameLocal.pvs.FreeCurrentPVS( PVSHandle );
|
|
|
|
return bResult;
|
|
}
|
|
|
|
/*
|
|
================
|
|
hhMountedGun::Killed
|
|
================
|
|
*/
|
|
void hhMountedGun::Killed( idEntity *pInflictor, idEntity *pAttacker, int iDamage, const idVec3 &Dir, int iLocation ) {
|
|
hhFxInfo fxInfo;
|
|
|
|
if ( gunMode != GM_DEAD ) {
|
|
BecomeInactive( TH_THINK|TH_TICKER );
|
|
CancelEvents( &EV_ScanForClosestTarget );
|
|
CancelEvents( &EV_StartAttack );
|
|
CancelEvents( &EV_NextCycleAnim );
|
|
|
|
fxInfo.SetNormal( -GetAxis()[2] );
|
|
fxInfo.RemoveWhenDone( true );
|
|
BroadcastFxInfo( spawnArgs.GetString("fx_detonate"), boneHub.origin, GetAxis(), &fxInfo );
|
|
|
|
hhUtils::SpawnDebrisMass(spawnArgs.GetString("def_debrisspawner"), this);
|
|
|
|
if( targetingLaser.IsValid() ) {
|
|
targetingLaser->Activate( false );
|
|
}
|
|
SAFE_REMOVE( targetingLaser );
|
|
SAFE_FREELIGHT( muzzleFlashHandle );
|
|
|
|
if( trackMover.IsValid() ) {
|
|
trackMover->ProcessEvent( &EV_Deactivate );
|
|
}
|
|
|
|
const char *killedModel = spawnArgs.GetString("model_killed", NULL);
|
|
if (killedModel) {
|
|
SetModel(killedModel);
|
|
UpdateVisuals();
|
|
GetPhysics()->SetContents(0);
|
|
fl.takedamage = false;
|
|
}
|
|
|
|
RemoveBinds();
|
|
|
|
StopSound( SND_CHANNEL_ANY, false );
|
|
|
|
StartSound( "snd_die", SND_CHANNEL_ANY);
|
|
|
|
ActivateTargets( pAttacker );
|
|
|
|
gunMode = GM_DEAD;
|
|
}
|
|
}
|
|
|
|
void hhMountedGun::jointInfo_t::Save( idSaveGame *savefile ) const {
|
|
savefile->WriteString( name );
|
|
savefile->WriteJoint( handle );
|
|
savefile->WriteVec3( origin );
|
|
savefile->WriteMat3( axis );
|
|
}
|
|
|
|
void hhMountedGun::jointInfo_t::Restore( idRestoreGame *savefile ) {
|
|
savefile->ReadString( name );
|
|
savefile->ReadJoint( handle );
|
|
savefile->ReadVec3( origin );
|
|
savefile->ReadMat3( axis );
|
|
}
|
|
|