stvoy-sp-sdk/game/AI_Vohrsoth.cpp

466 lines
10 KiB
C++
Raw Permalink Normal View History

2002-11-22 00:00:00 +00:00
//The Vorhsoth
#include "AI.h"
#include "anims.h"
#include "b_local.h"
#include "g_functions.h"
extern void FX_GroundTendrilSpawner( vec3_t start, vec3_t end );
extern void FX_ChestBeamSpawner( vec3_t origin, float height );
extern void FX_Vohrsoth_CreateGroundWarning( vec3_t origin );
extern void CG_DrawEdge( vec3_t start, vec3_t end, int type );
extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time );
extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *soundPath );
//Attack state enums
enum
{
VLS_QUAD_LOBBER = 1,
VLS_TENDRIL,
VLS_ROCKET_BURST,
VLS_CHEST_BEAM,
};
//Attack times
const int QUAD_LOBBER_TIME = 4100;
const int CHEST_BEAM_TIME = 8000;
const int TENDRIL_TIME = 8100;
const int ROCKET_BURST_TIME = 4100;
/*
-------------------------
Vohrsoth_GetEnemyPosition
-------------------------
*/
static void Vohrsoth_GetEnemyPosition( vec3_t origin )
{
vec3_t end;
VectorCopy( NPC->enemy->currentOrigin, end );
end[2] -= 100;
trace_t tr;
gi.trace( &tr, NPC->enemy->currentOrigin, NULL, NULL, end, NPC->enemy->s.number, MASK_SOLID );
VectorCopy( tr.endpos, origin );
}
/*
-------------------------
tendril_think
-------------------------
*/
void tendril_think( gentity_t *self )
{
//Explode with effect
ExplodeDeath( self );
}
/*
-------------------------
Vohrsoth_CreateTendril
-------------------------
*/
#define TENDRIL_DELAY 1000
static void Vohrsoth_CreateTendril( vec3_t origin )
{
vec3_t pos;
//Create an effect to mark the position
Vohrsoth_GetEnemyPosition( pos );
FX_Vohrsoth_CreateGroundWarning( pos );
//Spawn the entity
gentity_t *ent = G_Spawn();
if ( ent == NULL )
{
gi.Printf("WARNING: Unable to spawn ground tendril\n");
return;
}
//Make it invisible
ent->s.solid = 0;
ent->contents = 0;
ent->clipmask = 0;
ent->svFlags |= SVF_NOCLIENT;
ent->s.eFlags |= EF_NODRAW;
ent->count = 0;
ent->classname = "tendril";
ent->splashDamage = 100;
ent->splashRadius = 64;
ent->spawnflags |= 4;
G_SetOrigin( ent, pos );
VectorSet( ent->s.angles, 0, 90, 0 );
ent->sounds = G_SoundIndex( va( "sound/weapons/explosions/explode%d.wav", Q_irand( 1, 4 ) ) );
ent->e_ThinkFunc = thinkF_ExplodeDeath;
ent->nextthink = level.time + TENDRIL_DELAY;
G_SoundOnEnt( ent, CHAN_WEAPON, "sound/enemies/vorhsoth/beamhit.wav" );
}
/*
-------------------------
Vohrsoth_Tendril
-------------------------
*/
static void Vohrsoth_Tendril( void )
{
//If our enemy is dead, don't do anything
if ( NPC->enemy && NPC->enemy->health <= 0 )
return;
if ( TIMER_Done( NPC, "tendrilDelay" ) == qfalse )
return;
Vohrsoth_CreateTendril( NPC->enemy->currentOrigin );
TIMER_Set( NPC, "tendrilDelay", 1000 );
if ( TIMER_Done( NPC, "tendrilDrawDelay" ) == qfalse )
return;
vec3_t start, end, forward;
AngleVectors( NPC->currentAngles, forward, NULL, NULL );
VectorMA( NPC->currentOrigin, 80, forward, start );
VectorCopy( start, end );
start[2] -= 24;
end[2] += 48;
FX_GroundTendrilSpawner( start, end );
G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/enemies/vorhsoth/beamfire.wav" );
TIMER_Set( NPC, "tendrilDrawDelay", 99999999 );
}
/*
-------------------------
Vohrsoth_CreateLob
-------------------------
*/
static void Vohrsoth_CreateLob( vec3_t start, vec3_t end )
{
vec3_t dir;
//Setup the direction
VectorSubtract( end, start, dir );
VectorNormalize( dir );
gentity_t *bolt;
bolt = G_Spawn();
if ( bolt == NULL )
return;
bolt->classname = "vorh_lob";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_FORGE_PROJ; //FIXME: For now
bolt->owner = NPC;
bolt->damage = 8 * 12;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_SCAVENGER;
bolt->clipmask = MASK_SHOT;
// There are going to be a couple of different sized projectiles, so store 'em here
bolt->count = 1;
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
VectorScale( bolt->maxs, -8, bolt->mins );
VectorScale( bolt->maxs, 8, bolt->maxs );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time;
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, 700, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
// Used by trails
VectorCopy (start, bolt->pos1 );
VectorCopy (start, bolt->pos2 );
G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/weapons/explosions/fireball.wav" );
}
/*
-------------------------
Vohrsoth_QuadLobber
-------------------------
*/
const vec3_t lobAttackOffset[4] =
{
{ 200, -90, 48 },
{ 160, 70, 35 },
{ 120, 40, 80 },
{ 160, -75, 90 },
};
const int lobAttackDelay[4] =
{
700,
400,
600,
500
};
static void Vohrsoth_QuadLobber( void )
{
//If our enemy is dead, don't do anything
if ( NPC->enemy && NPC->enemy->health <= 0 )
return;
//See if we're ready to lob another
if ( TIMER_Done( NPC, "lobDelay" ) == qfalse )
return;
if ( NPCInfo->squadState > 3 )
return;
vec3_t start, forward, right;
AngleVectors( NPC->currentAngles, forward, right, NULL );
VectorMA( NPC->currentOrigin, lobAttackOffset[NPCInfo->squadState][0], forward, start );
VectorMA( start, lobAttackOffset[NPCInfo->squadState][1], right, start );
start[2] += lobAttackOffset[NPCInfo->squadState][2];
Vohrsoth_CreateLob( start, NPC->enemy->currentOrigin );
NPCInfo->squadState++;
TIMER_Set( NPC, "lobDelay", lobAttackDelay[NPCInfo->squadState] );
}
/*
-------------------------
Vohrsoth_ChestBeam
-------------------------
*/
static void Vohrsoth_ChestBeam( void )
{
}
/*
-------------------------
Vohrsoth_Rocket
-------------------------
*/
static void Vohrsoth_Rocket( vec3_t start, vec3_t forward )
{
gentity_t *bolt;
bolt = G_Spawn();
VectorSet( bolt->mins, -4, -4, -4 );
VectorSet( bolt->maxs, 4, 4, 4 );
bolt->classname = "bot_rocket";
bolt->nextthink = level.time + 200;
bolt->e_ThinkFunc = thinkF_bot_rocket_think;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_BOT_ROCKET;
bolt->owner = NPC;
bolt->damage = 12;
bolt->dflags = 0;
bolt->splashDamage = 8;
bolt->splashRadius = 64;
bolt->methodOfDeath = MOD_BOTROCKET;
bolt->splashMethodOfDeath = MOD_BOTROCKET_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->contents = CONTENTS_SOLID;
bolt->takedamage = qtrue;
bolt->health = 10;
bolt->e_DieFunc = dieF_bot_rocket_die;
//Mark that this shouldn't account for FOV
bolt->spawnflags |= 2;
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( forward, 400 + ( crandom() * 100 ), bolt->s.pos.trDelta );
VectorCopy( forward, bolt->movedir );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( start, bolt->currentOrigin );
VectorCopy( start, bolt->pos1 );
gi.linkentity( bolt );
G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/weapons/hunter_seeker/fire.wav" );
}
/*
-------------------------
Vohrsoth_RocketBurst
-------------------------
*/
const vec3_t rocketAttackOffset[4] =
{
{ 75, 0, 80 },
{ 90, 0, 85 },
{ 110, 0, 75 },
{ 120, 0, 70 },
};
static void Vohrsoth_RocketBurst( void )
{
//If our enemy is dead, don't do anything
if ( NPC->enemy && NPC->enemy->health <= 0 )
return;
if ( TIMER_Done( NPC, "rocketDelay" ) == qfalse )
return;
vec3_t start, forward, right, dir;
AngleVectors( NPC->currentAngles, forward, right, NULL );
for ( int i = 0; i < 4; i++ )
{
VectorMA( NPC->currentOrigin, rocketAttackOffset[i][0], forward, start );
VectorMA( start, rocketAttackOffset[i][1], right, start );
start[2] += rocketAttackOffset[i][2];
VectorSubtract( start, NPC->currentOrigin, dir );
VectorNormalize( dir );
Vohrsoth_Rocket( start, dir );
}
TIMER_Set( NPC, "rocketDelay", 500000 );
}
/*
-------------------------
Vohrsoth_CheckEnemy
-------------------------
*/
static void Vohrsoth_CheckEnemy( void )
{
gentity_t *target = &g_entities[0]; //FIXME: If you want something else, put it here
NPC->enemy = target;
}
/*
-------------------------
Vohrsoth_StartAttack
-------------------------
*/
static void Vohrsoth_StartAttack( int attack )
{
int animID;
int timeOfs;
switch ( attack )
{
case VLS_QUAD_LOBBER:
animID = BOTH_ATTACK1;
timeOfs = QUAD_LOBBER_TIME;
NPCInfo->squadState = 0;
TIMER_Set( NPC, "lobDelay", lobAttackDelay[0] );
break;
case VLS_CHEST_BEAM:
animID = BOTH_ATTACK2;
timeOfs = CHEST_BEAM_TIME;
break;
case VLS_TENDRIL:
animID = BOTH_ATTACK4;
timeOfs = TENDRIL_TIME;
TIMER_Set( NPC, "tendrilDelay", 2500 );
TIMER_Set( NPC, "tendrilDrawDelay", 2500 );
break;
case VLS_ROCKET_BURST:
default:
animID = BOTH_ATTACK3;
timeOfs = ROCKET_BURST_TIME;
TIMER_Set( NPC, "rocketDelay", 800 );
break;
}
//Setup the information
NPC_SetAnim( NPC, SETANIM_BOTH, animID, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
TIMER_Set( NPC, "attackDelay", timeOfs );
NPCInfo->localState = attack;
}
/*
-------------------------
NPC_BSVohrsoth_Attack
-------------------------
*/
void NPC_BSVohrsoth_Attack( void )
{
//If we're dead, stop what we're doing
if ( NPC->health <= 0 )
return;
//Check for enemies, or loss of them
Vohrsoth_CheckEnemy();
//If our enemy is dead, don't do anything
if ( NPC->enemy && NPC->enemy->health <= 0 )
return;
//See if we're ready to attack again
if ( TIMER_Done( NPC, "attackDelay" ) )
{
Vohrsoth_StartAttack( Q_irand( VLS_QUAD_LOBBER, VLS_ROCKET_BURST ) );
}
//Decide which attack state we're in and update anything we need to
switch ( NPCInfo->localState )
{
case VLS_QUAD_LOBBER:
Vohrsoth_QuadLobber();
break;
case VLS_CHEST_BEAM:
Vohrsoth_ChestBeam();
break;
case VLS_TENDRIL:
Vohrsoth_Tendril();
break;
case VLS_ROCKET_BURST:
Vohrsoth_RocketBurst();
break;
}
}