0
0
Fork 0
mirror of https://github.com/id-Software/DOOM-3-BFG.git synced 2025-03-15 23:21:35 +00:00
doom3-bfg/base/script/ai_monster_boss_guardian_seeker.script
2022-08-27 13:19:00 +02:00

444 lines
9.7 KiB
Text

/***********************************************************************
ai_monster_boss_guardian_seeker.script
monster_boss_guardian_seeker
***********************************************************************/
#define SEEKER_TOOCLOSE_RANGE 200
#define SEEKER_TOOFAR_RANGE 650
#define SEEKER_IDEAL_RANGE 430
#define SEEKER_MAXIMUM_RETREAT_RANGE ( SEEKER_TOOFAR_RANGE + 200 )
#define SEEKER_RUNTIME 2
#define SEEKER_GETCLOSER_DELAY 3
#define SEEKER_GETCLOSER_TIME 1
#define SEEKER_SIGHTTIME 2
#define SEEKER_ESCAPE_SPEED 800
object monster_boss_guardian_seeker : seeker_base {
//
// States
//
void state_Inactive();
void state_Idle();
void state_WatchPlayer();
void state_GetCloser();
void state_FindPlayer();
void state_Retreat();
void state_RunAway();
void init();
// Torso anim states
void Torso_Idle();
void Torso_Fly();
void Torso_Pain();
void Torso_MeleeAttack();
monster_boss_guardian guardian;
entity light1;
entity light2;
entity light_beam;
float last_enemy_sight;
float fly_speed;
};
void monster_boss_guardian_seeker::Torso_Idle() {
playCycle( ANIMCHANNEL_TORSO, "idle" );
eachFrame {
if ( AI_PAIN ) { animState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); }
if ( AI_FORWARD ) { animState( ANIMCHANNEL_TORSO, "Torso_Fly", 4 ); }
}
}
void monster_boss_guardian_seeker::Torso_Fly() {
playCycle( ANIMCHANNEL_TORSO, "fly" );
eachFrame {
if ( AI_PAIN ) { animState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); }
if ( !AI_FORWARD ) { animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); }
}
}
void monster_boss_guardian_seeker::Torso_Pain() {
string animname;
float nextpain;
float currenttime;
animname = getPainAnim();
playAnim( ANIMCHANNEL_TORSO, animname );
guardian.AI_PAIN = true;
nextpain = sys.getTime() + 0.25;
while( !animDone( ANIMCHANNEL_TORSO, 2 ) ) {
if ( AI_PAIN ) {
currenttime = sys.getTime();
if ( currenttime > nextpain ) {
animState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 );
}
}
waitFrame();
}
finishAction( "pain" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 2 );
}
void monster_boss_guardian_seeker::Torso_MeleeAttack() {
playAnim( ANIMCHANNEL_TORSO, "melee_attack" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "melee_attack" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
}
void monster_boss_guardian_seeker::Torso_Death() {
waitUntil( 0 );
}
void monster_boss_guardian_seeker::see_enemy() {
if ( last_enemy_sight < sys.getTime() ) {
startSound( "snd_sight", SND_CHANNEL_VOICE, false );
}
last_enemy_sight = sys.getTime() + SEEKER_SIGHTTIME;
guardian.sightEnemy();
//light1.setColor( 1, 0.8, 0.4 );
//light2.setColor( 1, 0.8, 0.4 );
}
void monster_boss_guardian_seeker::lost_enemy() {
light1.setColor( 0.5, 0.2, 0.1 );
light2.setColor( 0.5, 0.2, 0.1 );
}
void monster_boss_guardian_seeker::init() {
string name;
entity ent;
string classname;
ent = getTarget( 0 );
if ( ent ) {
guardian = getTarget( 0 );
if ( !guardian ) {
sys.error( "Entity '" + ent.getName() + "' targeted by '" + getName() + "' is not monster_boss_guardian" );
}
guardian.addSeeker( self );
}
fly_speed = getFloatKey( "fly_speed" );
// point light
//sys.setSpawnArg( "name", getName() + "_light1" );
//classname = getKey( "def_light1" );
//light1 = sys.spawn( classname );
//light1.bindToJoint( self, "head", true );
//light1.setOrigin( '0 0 30' );
// spot light
//float distance = 3096;
//sys.setSpawnArg( "light_target", "0 1 0" );
//sys.setSpawnArg( "light_up", "0 0 0.5" );
//sys.setSpawnArg( "light_right", "-0.5 0 0" );
//sys.setSpawnArg( "light_end", "0 " + distance + " 0" );
//sys.setSpawnArg( "name", getName() + "_light2" );
//classname = getKey( "def_light2" );
//light2 = sys.spawn( classname );
//light2.bindToJoint( self, "head", true );
//light2.setOrigin( '0 0 30' );
// light beam
sys.setSpawnArg( "name", getName() + "_lightbeam" );
sys.setSpawnArg( "model", getKey( "model_lightbeam" ) );
sys.setSpawnArg( "noclipmodel", "1" );
classname = getKey( "def_light2" );
light_beam = sys.spawn( "func_static" );
light_beam.bindToJoint( self, "head", true );
light_beam.setOrigin( '0 0 30' );
light_beam.setAngles( '0 0 -90' );
last_enemy_sight = 0;
lost_enemy();
setMoveType( MOVETYPE_FLY );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
if ( guardian ) {
setState( "state_Inactive" );
} else {
setState( "state_Idle" );
}
}
void monster_boss_guardian_seeker::destory() {
light_beam.remove();
//light1.remove();
//light2.remove();
}
void monster_boss_guardian_seeker::respawnSeeker( entity enemy ) {
setEnemy( enemy );
setState( "state_Respawn" );
}
void monster_boss_guardian_seeker::state_Respawn() {
float health;
vector ang;
vector dir;
ang_y = sys.random( 360 );
dir = sys.angToForward( ang );
setAngles( ang );
setOrigin( guardian.spawner.getWorldOrigin() - '0 0 48' + dir * 82 );
waitUntil( canBecomeSolid() );
clearBurn();
show();
health = getFloatKey( "health" );
setHealth( health );
setMoveType( MOVETYPE_FLY );
setEnemy( guardian.getEnemy() );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
moveToPosition( getOrigin() + dir * 512 );
// invulnerable for a bit so player can't just keep firing rockets at spawn location
ignoreDamage();
waitFrame();
light_beam.show();
//light1.On();
//light2.On();
sys.wait( 2 );
allowDamage();
setState( "state_Idle" );
}
void monster_boss_guardian_seeker::state_Inactive() {
AI_DEAD = true;
light_beam.hide();
//light1.Off();
//light2.Off();
hide();
while( 1 ) {
waitFrame();
}
}
void monster_boss_guardian_seeker::state_Idle() {
wait_for_enemy();
guardian.setEnemy( getEnemy() );
setState( "state_FindPlayer" );
}
void monster_boss_guardian_seeker::state_Killed() {
guardian.AI_PAIN = true;
light_beam.hide();
//light1.Off();
//light2.Off();
stopMove();
animState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
burn();
startSound( "snd_burn", SND_CHANNEL_BODY, false );
sys.wait( 4 );
hide();
clearBurn();
if ( !guardian ) {
remove();
}
waitUntil( 0 );
}
void monster_boss_guardian_seeker::state_WatchPlayer() {
float getCloserTime;
float range;
getCloserTime = sys.getTime() + SEEKER_GETCLOSER_DELAY;
eachFrame {
faceEnemy();
moveToEnemyHeight();
if ( AI_ENEMY_DEAD ) {
enemy_dead();
continue;
}
if ( AI_DAMAGE ) {
setState( "state_RunAway" );
}
if ( !AI_ENEMY_VISIBLE ) {
lost_enemy();
setState( "state_FindPlayer" );
}
range = enemyRange2D();
if ( range < SEEKER_TOOCLOSE_RANGE ) {
lost_enemy();
setState( "state_Retreat" );
}
if ( range > SEEKER_TOOFAR_RANGE ) {
if ( sys.getTime() > getCloserTime ) {
setState( "state_GetCloser" );
}
}
if ( AI_ENEMY_IN_FOV ) {
see_enemy();
}
}
}
void monster_boss_guardian_seeker::state_GetCloser() {
float getCloserTime;
float range;
setFlySpeed( fly_speed );
getCloserTime = sys.getTime() + SEEKER_GETCLOSER_TIME;
moveToEnemy();
while( !AI_ENEMY_DEAD ) {
if ( AI_MOVE_DONE && !enemyPositionValid() ) {
// Cheat
locateEnemy();
moveToEnemy();
}
if ( !AI_ENEMY_VISIBLE ) {
lost_enemy();
setState( "state_FindPlayer" );
}
if ( AI_DAMAGE ) {
setState( "state_RunAway" );
}
range = enemyRange2D() - sys.vecLength( getLinearVelocity() ) * 0.25;
if ( range < SEEKER_TOOCLOSE_RANGE ) {
lost_enemy();
setState( "state_Retreat" );
}
if ( range < SEEKER_IDEAL_RANGE ) {
setState( "state_WatchPlayer" );
}
if ( AI_ENEMY_IN_FOV ) {
see_enemy();
}
waitFrame();
}
setState( "state_WatchPlayer" );
}
void monster_boss_guardian_seeker::state_FindPlayer() {
float visTime;
boolean visible;
visible = false;
moveToEnemy();
while( !AI_ENEMY_DEAD ) {
if ( AI_MOVE_DONE && !enemyPositionValid() ) {
// Cheat
locateEnemy();
moveToEnemy();
}
if ( AI_DAMAGE ) {
setState( "state_RunAway" );
}
// don't spot the player instantly
if ( AI_ENEMY_VISIBLE ) {
if ( !visible ) {
visTime = sys.getTime() + 0.5;
visible = true;
} else if ( sys.getTime() > visTime ) {
break;
}
} else {
visible = false;
}
waitFrame();
}
setFlySpeed( fly_speed );
setState( "state_WatchPlayer" );
}
void monster_boss_guardian_seeker::state_Retreat() {
float run_time;
setFlySpeed( SEEKER_ESCAPE_SPEED );
run_time = sys.getTime() + SEEKER_RUNTIME;
moveOutOfRange( getEnemy(), enemyRange2D() + 200 );
while( !AI_ENEMY_DEAD && AI_ENEMY_VISIBLE ) {
if ( AI_MOVE_DONE ) {
// can't find a place to run to so just move randomly
wander();
}
if ( AI_DAMAGE ) {
setState( "state_RunAway" );
}
if ( AI_ENEMY_IN_FOV ) {
see_enemy();
}
if ( ( sys.getTime() > run_time ) || ( ( enemyRange2D() + sys.vecLength( getLinearVelocity() ) * 0.5 ) > SEEKER_MAXIMUM_RETREAT_RANGE ) ) {
break;
}
waitFrame();
}
setState( "state_WatchPlayer" );
}
void monster_boss_guardian_seeker::state_RunAway() {
float run_time;
float lastSightTime;
setFlySpeed( SEEKER_ESCAPE_SPEED );
AI_DAMAGE = false;
sys.wait( 0.5 );
moveOutOfRange( getEnemy(), enemyRange2D() + 200 );
run_time = sys.getTime() + SEEKER_RUNTIME;
lastSightTime = sys.getTime();
while( !AI_ENEMY_DEAD ) {
if ( AI_MOVE_DONE ) {
// can't find a place to run to so just move randomly
wander();
}
// run away until we haven't seen the player for a bit
if ( AI_ENEMY_VISIBLE ) {
lastSightTime = sys.getTime();
if ( AI_ENEMY_IN_FOV ) {
see_enemy();
}
} else if ( sys.getTime() > lastSightTime + 0.5 ) {
break;
}
if ( AI_DAMAGE ) {
run_time = sys.getTime() + SEEKER_RUNTIME;
AI_DAMAGE = false;
}
if ( sys.getTime() > run_time ) {
if ( ( enemyRange2D() + sys.vecLength( getLinearVelocity() ) * 0.5 ) > SEEKER_MAXIMUM_RETREAT_RANGE ) {
break;
}
}
waitFrame();
}
setFlySpeed( fly_speed );
setState( "state_WatchPlayer" );
}