quake4-sdk/source/game/ai/Monster_ConvoyHover.cpp
2007-06-15 00:00:00 +00:00

630 lines
17 KiB
C++

//----------------------------------------------------------------
// rvMonsterConvoyHover.h
//
// Copyright 2002-2004 Raven Software
//----------------------------------------------------------------
#include "../../idlib/precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
#ifndef __GAME_VEHICLEAI_H__
#include "VehicleAI.h"
#endif
#ifndef __GAME_PROJECTILE_H__
#include "../Projectile.h"
#endif
class rvMonsterConvoyHover : public rvVehicleMonster {
public:
CLASS_PROTOTYPE ( rvMonsterConvoyHover );
rvMonsterConvoyHover ( void );
~rvMonsterConvoyHover ( void );
void Save ( idSaveGame *savefile ) const;
void Restore ( idRestoreGame *savefile );
void InitSpawnArgsVariables ( void );
void Spawn ( void );
void Think ( void );
private:
void Think_Random ( void );
void Think_Pathing ( void );
void AttackBlaster ( void );
void AttackBeam ( void );
//void AttackBomb ( void );
float angleYaw; // actually acos( angleYaw ) from player
float minYaw;
float maxYaw;
float desiredHeight; // actually acos( desiredHeight ) from player
float minHeight;
float maxHeight;
float distance; // distance to enemy
float minDistance;
float maxDistance;
jointHandle_t jointGunRight;
jointHandle_t jointGunLeft;
int lastAttackTime;
int attackStartTime;
int blasterAttackDuration;
int blasterAttackRate;
int bombAttackDuration;
int bombAttackRate;
int shotCount;
idPhysics_RigidBody physicsObj;
static const int DAMPEN_ANGLE_SAMPLES = 8;
idAngles hoverDampening[ DAMPEN_ANGLE_SAMPLES ];
void CalcDampening ( const idAngles & cur, idAngles & out );
stateResult_t State_Idle ( const stateParms_t& parms );
stateResult_t State_BlasterAttack ( const stateParms_t& parms );
stateResult_t State_BeamAttack ( const stateParms_t& parms );
//stateResult_t State_BombAttack ( const stateParms_t& parms );
struct {
bool pathing:1;
bool faceEnemy:1;
bool dead:1;
} myfl;
virtual void OnDeath ( void );
CLASS_STATES_PROTOTYPE ( rvMonsterConvoyHover );
};
CLASS_DECLARATION ( rvVehicleMonster, rvMonsterConvoyHover )
END_CLASS
/*
================
rvMonsterConvoyHover::rvMonsterConvoyHover
================
*/
rvMonsterConvoyHover::rvMonsterConvoyHover ( void ) {
}
/*
================
rvMonsterConvoyHover::~rvMonsterConvoyHover
================
*/
rvMonsterConvoyHover::~rvMonsterConvoyHover ( void ) {
SetPhysics( NULL );
}
/*
================
rvMonsterConvoyHover::Save
================
*/
void rvMonsterConvoyHover::Save ( idSaveGame *savefile ) const {
savefile->WriteFloat ( angleYaw );
savefile->WriteFloat ( desiredHeight );
savefile->WriteFloat ( distance );
savefile->WriteInt ( lastAttackTime );
savefile->WriteInt ( attackStartTime );
savefile->WriteInt ( shotCount );
savefile->WriteStaticObject ( physicsObj );
savefile->Write ( hoverDampening, sizeof ( idAngles ) * DAMPEN_ANGLE_SAMPLES );
savefile->Write( &myfl, sizeof(myfl) ); // cnicholson: Added unsaved var
}
/*
================
rvMonsterConvoyHover::Restore
================
*/
void rvMonsterConvoyHover::Restore ( idRestoreGame *savefile ) {
savefile->ReadFloat ( angleYaw );
savefile->ReadFloat ( desiredHeight );
savefile->ReadFloat ( distance );
savefile->ReadInt ( lastAttackTime );
savefile->ReadInt ( attackStartTime );
savefile->ReadInt ( shotCount );
physicsObj.SetSelf( this );
physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
savefile->ReadStaticObject ( physicsObj );
RestorePhysics ( &physicsObj );
physicsObj.EnableClip();
savefile->Read ( hoverDampening, sizeof ( idAngles ) * DAMPEN_ANGLE_SAMPLES );
savefile->Read( &myfl, sizeof(myfl) ); // cnicholson: Added unsaved var
InitSpawnArgsVariables();
}
void rvMonsterConvoyHover::InitSpawnArgsVariables ( void )
{
jointGunRight = animator.GetJointHandle ( spawnArgs.GetString ( "joint_gun_right" ) );
jointGunLeft = animator.GetJointHandle ( spawnArgs.GetString ( "joint_gun_left" ) );
minYaw = spawnArgs.GetFloat( "minYaw", "0" );
maxYaw = spawnArgs.GetFloat( "maxYaw", "360" );
minHeight = spawnArgs.GetFloat( "minHeight", "10" );
maxHeight = spawnArgs.GetFloat( "maxHeight", "70" );
minDistance = spawnArgs.GetFloat( "minDistance", "100" );
maxDistance = spawnArgs.GetFloat( "maxDistance", "500" );
minDistance *= minDistance;
maxDistance *= maxDistance;
if ( minYaw == 0.0f && maxYaw == 0.0f ) {
maxYaw = 360.0f;
}
blasterAttackDuration = SEC2MS ( spawnArgs.GetFloat ( "blasterAttackDuration", "1" ) );
blasterAttackRate = SEC2MS ( spawnArgs.GetFloat ( "blasterAttackRate", ".25" ) );
bombAttackDuration = SEC2MS ( spawnArgs.GetFloat ( "bombAttackDuration", "1" ) );
bombAttackRate = SEC2MS ( spawnArgs.GetFloat ( "bombAttackRate", ".25" ) );
}
/*
================
rvMonsterConvoyHover::Spawn
================
*/
void rvMonsterConvoyHover::Spawn ( void ) {
physicsObj.SetSelf( this );
SetClipModel ( physicsObj );
physicsObj.SetOrigin( GetPhysics()->GetOrigin ( ) );
physicsObj.SetAxis ( GetPhysics()->GetAxis ( ) );
physicsObj.SetContents( CONTENTS_BODY );
physicsObj.SetClipMask( MASK_PLAYERSOLID|CONTENTS_VEHICLECLIP|CONTENTS_FLYCLIP );
physicsObj.SetFriction ( spawnArgs.GetFloat ( "friction_linear", "1" ), spawnArgs.GetFloat ( "friction_angular", "1" ), spawnArgs.GetFloat ( "friction_contact", "1" ) );
physicsObj.SetBouncyness ( spawnArgs.GetFloat ( "bouncyness", "0.6" ) );
physicsObj.SetGravity( vec3_origin );
SetPhysics( &physicsObj );
animator.CycleAnim ( ANIMCHANNEL_ALL, animator.GetAnim( spawnArgs.GetString( "anim", "idle" ) ), gameLocal.time, 0 );
BecomeActive( TH_THINK );
InitSpawnArgsVariables();
shotCount = 0;
angleYaw = rvRandom::flrand( minYaw, maxYaw );
desiredHeight = 0.5f * ( maxHeight - minHeight ) + minHeight;
distance = 0.5f * ( maxDistance - minDistance ) + minDistance;
lastAttackTime = 0;
SetState( "Idle" );
myfl.pathing = false;
myfl.faceEnemy = true;
myfl.dead = false;
for ( int i = 0; i < DAMPEN_ANGLE_SAMPLES; i++ ) {
hoverDampening[ i ].Zero();
}
jointHandle_t joint;
joint = GetAnimator()->GetJointHandle( spawnArgs.GetString ( "joint_thruster", "tail_thrusters" ) );
if ( joint != INVALID_JOINT ) {
PlayEffect ( "fx_exhaust", joint, true );
}
StartSound ( "snd_flyloop", SND_CHANNEL_ANY, 0, false, NULL );
}
/*
================
rvMonsterConvoyHover::Think
================
*/
void rvMonsterConvoyHover::Think ( void ) {
if ( !driver ) {
rvVehicleMonster::Think();
return;
}
if ( !driver->IsDriving() ) {
GetPhysics()->SetGravity( gameLocal.GetGravity() );
}
trace_t trace;
idVec3 dir = GetPhysics()->GetLinearVelocity();
dir.Normalize();
if ( gameLocal.TracePoint( this, trace, GetOrigin(), GetPhysics()->GetOrigin() + dir * 100.0f, MASK_SOLID|CONTENTS_FLYCLIP, 0 ) ) {
if ( trace.c.contents == CONTENTS_FLYCLIP ) {
float maxHeight = this->maxHeight * 2.0f;
GetPhysics()->ApplyImpulse( 0, GetOrigin(), GetPhysics()->GetLinearVelocity() * 2.0f );
angleYaw = rvRandom::flrand( minYaw, maxYaw );
desiredHeight = idMath::ClampFloat( minHeight, maxHeight, desiredHeight + 5.0f );
distance = idMath::ClampFloat( minDistance, maxDistance, distance + 5.0f );
}
}
if ( !driver->CanSee( driver->enemy.ent, false ) ) {
desiredHeight = idMath::ClampFloat( minHeight, maxHeight, desiredHeight + 5.0f );
distance = idMath::ClampFloat( minDistance, maxDistance, distance - 5.0f );
}
if ( myfl.pathing ) {
Think_Pathing( );
} else {
Think_Random( );
}
rvVehicleMonster::Think();
idVec3 vel = GetPhysics()->GetLinearVelocity();
// vel += idVec3( idMath::Sin( gameLocal.time + vel.x ),
// idMath::Sin( gameLocal.time + vel.y ),
// idMath::Sin( gameLocal.time + vel.z ) );
idAngles hover = idAngles( idMath::ClampFloat( -45.0f, 45.0f, vel.x / 20.0f ),
0.0f,
idMath::ClampFloat( -25.0f, 25.0f, vel.z / 20.0f ) );
CalcDampening( hover, hover );
if ( myfl.faceEnemy ) {
LookAtEntity( gameLocal.GetLocalPlayer(), 0 );
idAngles angles = GetPhysics()->GetAxis( ).ToAngles( );
angles.pitch *= 0.35f;
GetPhysics()->SetAxis( angles.ToMat3( ) * hover.ToMat3() );
} else {
GetPhysics()->SetAxis( GetPhysics()->GetAxis() * hover.ToMat3() );
}
}
/*
================
rvMonsterConvoyHover::Think_Random
================
*/
void rvMonsterConvoyHover::Think_Random ( void ) {
const idVec3 xAxis( 1.0f, 0.0f, 0.0f );
const idVec3 yAxis( 0.0f, 1.0f, 0.0f );
const idVec3 zAxis( 0.0f, 0.0f, 1.0f );
const float xSpeed = 16000.0f;
const float ySpeed = 9000.0f;
const float zSpeed = 9000.0f;
idEntity * ent = driver->enemy.ent;
if ( !ent ) {
ent = gameLocal.GetLocalPlayer();
}
idVec3 toEnemyYaw = ent->GetPhysics()->GetOrigin() - GetOrigin();
toEnemyYaw.z = 0.0f;
toEnemyYaw.Normalize();
float yaw = toEnemyYaw.ToAngles().yaw;
float height = GetOrigin().z - ent->GetPhysics()->GetOrigin().z;
float dist = GetOrigin().Dist2XY( ent->GetPhysics()->GetOrigin() );
// yaw
if ( idMath::Fabs( yaw - angleYaw ) < 10.0f ) {
angleYaw = rvRandom::flrand( minYaw, maxYaw );
} else {
idVec3 impulse = yAxis * ( ( angleYaw < yaw ) ? ySpeed : -ySpeed );
impulse *= GetPhysics()->GetAxis();
GetPhysics()->ApplyImpulse( 0, GetOrigin(), impulse );
// 5% chance of choosing a new angle
if ( rvRandom::flrand() < 0.05f ) {
angleYaw = rvRandom::flrand( minYaw, maxYaw );
}
}
// pitch (changed to height for convenience)
if ( idMath::Fabs( height - desiredHeight ) < 5.0f ) {
desiredHeight = rvRandom::flrand( minHeight, maxHeight );
} else if ( GetOrigin().z < ent->GetPhysics()->GetOrigin().z + desiredHeight ) {
GetPhysics()->ApplyImpulse( 0, GetOrigin(), zAxis * zSpeed );
} else {
GetPhysics()->ApplyImpulse( 0, GetOrigin(), zAxis * -zSpeed );
}
// distance
if ( idMath::Fabs( dist - distance ) > 20.0f ) {
idVec3 impulse = xAxis * ( ( dist < distance ) ? -xSpeed : xSpeed );
idMat3 axis = GetPhysics()->GetAxis();
axis[ 1 ] = idVec3( 0.0f, 0.0f, 1.0f );
axis[ 2 ] = axis[ 1 ].Cross( axis[ 0 ] );
impulse *= axis;
GetPhysics()->ApplyImpulse( 0, GetOrigin(), impulse );
} else {
// just choose a random distance for now
distance = rvRandom::flrand( minDistance, maxDistance );
}
}
/*
================
rvMonsterConvoyHover::Think_Pathing
================
*/
void rvMonsterConvoyHover::Think_Pathing ( void ) {
}
/*
================
rvMonsterConvoyHover::AttackBlaster
================
*/
void rvMonsterConvoyHover::AttackBlaster ( void ) {
idVec3 offset;
idMat3 axis;
jointHandle_t joint;
joint = ((shotCount++)&1) ? jointGunRight : jointGunLeft;
if ( joint == INVALID_JOINT ) {
return;
}
if ( !GetJointWorldTransform( joint, gameLocal.time, offset, axis ) ) {
return;
}
PlayEffect ( "fx_muzzleflash", joint );
idProjectile* proj = gameLocal.SpawnSafeEntityDef<idProjectile>( spawnArgs.GetString("def_attack_blaster") );
if( proj ) {
idVec3 dir = GetVectorToEnemy();
if ( dir.Normalize() == 0.0f ) {
dir = axis[ 0 ];
}
proj->Create( this, offset, dir, NULL );
proj->Launch( offset, dir, GetPhysics()->GetPushedLinearVelocity() );
}
}
/*
================
rvMonsterConvoyHover::AttackBeam
================
*/
void rvMonsterConvoyHover::AttackBeam ( void ) {
idVec3 offset;
idMat3 axis;
jointHandle_t joint;
joint = ((shotCount++)&1) ? jointGunRight : jointGunLeft;
if ( joint == INVALID_JOINT ) {
return;
}
if ( !GetJointWorldTransform( joint, gameLocal.time, offset, axis ) ) {
return;
}
PlayEffect ( "fx_muzzleflash", joint );
idProjectile* proj = gameLocal.SpawnSafeEntityDef<idProjectile>( spawnArgs.GetString("def_attack_blaster") );
if( proj ) {
idVec3 dir = GetVectorToEnemy();
if ( dir.Normalize() == 0.0f ) {
dir = axis[ 0 ];
}
proj->Create( this, offset, dir, NULL );
proj->Launch( offset, dir, GetPhysics()->GetPushedLinearVelocity() );
}
}
/*
================
rvMonsterConvoyHover::AttackBomb
================
*
void rvMonsterConvoyHover::AttackBomb ( void ) {
jointHandle_t joint;
joint = ((shotCount++)&1) ? jointGunRight : jointGunLeft;
if ( joint == INVALID_JOINT ) {
return;
}
if ( !GetJointWorldTransform( joint, gameLocal.time, offset, axis ) ) {
return;
}
StartSound ( "snd_bombrun", SND_CHANNEL_ANY, 0, false, NULL );
PlayEffect ( "fx_bombflash", joint );
idProjectile* proj = gameLocal.SpawnSafeEntityDef<idProjectile>( spawnArgs.GetString("def_attack_bomb") );
if( proj ) {
proj->Create( this, offset, axis[0], NULL );
proj->Launch( offset, axis[0], GetPhysics()->GetPushedLinearVelocity() );
}
}
*/
/*
================
rvMonsterConvoyHover::OnDeath
================
*/
void rvMonsterConvoyHover::OnDeath ( void ) {
myfl.dead = true;
idVec3 angular = idVec3( rvRandom::flrand( 180.0f, 250.0f ), rvRandom::flrand( 180.0f, 250.0f ), rvRandom::flrand( 180.0f, 250.0f ) );
physicsObj.SetGravity( gameLocal.GetGravity() );
physicsObj.SetFriction( 0.0f, 0.0f, 0.0f );
GetPhysics()->SetAngularVelocity ( angular );
gameLocal.PlayEffect ( spawnArgs, "fx_death", GetPhysics()->GetOrigin(), (-GetPhysics()->GetGravityNormal()).ToMat3() );
}
/*
================
rvMonsterConvoyHover::CalcDampening
================
*/
void rvMonsterConvoyHover::CalcDampening ( const idAngles & cur, idAngles & out ) {
idAngles current = cur; // just incase cur == out
out = cur;
for ( int i = 1; i < DAMPEN_ANGLE_SAMPLES; i ++ ) {
hoverDampening[ i - 1 ] = hoverDampening[ i ];
out += hoverDampening[ i ];
}
hoverDampening[ DAMPEN_ANGLE_SAMPLES - 1 ] = current;
out *= ( 1.0f / DAMPEN_ANGLE_SAMPLES );
}
/*
===============================================================================
States
===============================================================================
*/
CLASS_STATES_DECLARATION ( rvMonsterConvoyHover )
STATE( "Idle", rvMonsterConvoyHover::State_Idle )
STATE( "BlasterAttack", rvMonsterConvoyHover::State_BlasterAttack )
STATE( "BeamAttack", rvMonsterConvoyHover::State_BeamAttack )
// STATE( "BombAttack", rvMonsterConvoyHover::State_BombAttack )
END_CLASS_STATES
/*
================
rvMonsterConvoyHover::State_Idle
================
*/
stateResult_t rvMonsterConvoyHover::State_Idle ( const stateParms_t& parms ) {
if ( driver ) {
if ( gameLocal.time - lastAttackTime > rvRandom::irand( 1000, 1200 ) && CanSee( driver->enemy.ent, false ) ) {
// if ( rvRandom::irand( 0, 100 ) < 10 ) {
// distance = 0;
// PostState( "BeamAttack" );
// } else {
PostState( "BlasterAttack" );
// }
PostState( "Idle" );
return SRESULT_DONE;
}
return SRESULT_WAIT;
}
return SRESULT_DONE;
}
/*
================
rvMonsterConvoyHover::State_BlasterAttack
================
*/
stateResult_t rvMonsterConvoyHover::State_BlasterAttack ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_BLASTER,
STAGE_BLASTERWAIT
};
switch ( parms.stage ) {
case STAGE_INIT:
attackStartTime = gameLocal.time;
return SRESULT_STAGE ( STAGE_BLASTER );
case STAGE_BLASTER:
lastAttackTime = gameLocal.time;
AttackBlaster ( );
return SRESULT_STAGE ( STAGE_BLASTERWAIT );
case STAGE_BLASTERWAIT:
if ( gameLocal.time - attackStartTime > blasterAttackDuration ) {
return SRESULT_DONE;
}
if ( gameLocal.time - lastAttackTime > blasterAttackRate ) {
return SRESULT_STAGE ( STAGE_BLASTER );
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
/*
================
rvMonsterConvoyHover::State_BeamAttack
================
*/
stateResult_t rvMonsterConvoyHover::State_BeamAttack ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_BEAM,
STAGE_BEAMWAIT
};
switch ( parms.stage ) {
case STAGE_INIT:
attackStartTime = gameLocal.time;
return SRESULT_STAGE ( STAGE_BEAM );
case STAGE_BEAM:
lastAttackTime = gameLocal.time;
AttackBeam( );
return SRESULT_STAGE ( STAGE_BEAMWAIT );
case STAGE_BEAMWAIT:
if ( gameLocal.time - attackStartTime > blasterAttackDuration ) {
return SRESULT_DONE;
}
if ( gameLocal.time - lastAttackTime > blasterAttackRate ) {
return SRESULT_STAGE ( STAGE_BEAM );
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
/*
================
rvMonsterConvoyHover::State_BombAttack
================
*
stateResult_t rvMonsterConvoyHover::State_BombAttack ( const stateParms_t& parms ) {
enum {
STAGE_INIT,
STAGE_BOMB,
STAGE_BOMBWAIT
};
switch ( parms.stage ) {
case STAGE_INIT:
attackStartTime = gameLocal.time;
return SRESULT_STAGE ( STAGE_BOMB );
case STAGE_BOMB:
lastAttackTime = gameLocal.time;
AttackBomb ( );
return SRESULT_STAGE ( STAGE_BOMBWAIT );
case STAGE_BOMBWAIT:
if ( !enemy.fl.inFov || gameLocal.time - attackStartTime > bombAttackDuration ) {
return SRESULT_DONE;
}
if ( gameLocal.time - lastAttackTime > bombAttackRate ) {
return SRESULT_STAGE ( STAGE_BOMB );
}
return SRESULT_WAIT;
}
return SRESULT_ERROR;
}
*/