mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-15 23:21:35 +00:00
494 lines
11 KiB
Text
494 lines
11 KiB
Text
/***********************************************************************
|
|
|
|
ai_monster_boss_sabaoth.script
|
|
|
|
monster_boss_sabaoth
|
|
|
|
***********************************************************************/
|
|
|
|
#define SABAOTH_ATTACK_RATE 4
|
|
#define SABAOTH_ATTACK_RATE_MELEE 1
|
|
#define SABAOTH_SLOW_DIST 64
|
|
#define SECTION_MOVE_DIST 190
|
|
#define CENTER_DIST 304
|
|
#define MELEE_INIT 290
|
|
|
|
object monster_boss_sabaoth : monster_base {
|
|
float nextAttack;
|
|
float movespeed;
|
|
entity projectile;
|
|
|
|
//
|
|
// States
|
|
//
|
|
void state_Begin();
|
|
void state_Idle();
|
|
void state_Combat();
|
|
void state_Killed();
|
|
|
|
// attacks
|
|
float check_attacks( float meleeOnly );
|
|
void do_attack( float attack_flags );
|
|
void combat_range();
|
|
void combat_melee();
|
|
void fire_bfg();
|
|
|
|
void init();
|
|
|
|
void move_to_corner( entity path, boolean noslowtostop );
|
|
void turn_to_corner( entity path );
|
|
void closeSection( entity path );
|
|
void openSection( entity path );
|
|
|
|
// torso anim states
|
|
void Torso_Idle();
|
|
void Torso_RangeAttack();
|
|
void Torso_MeleeAttack();
|
|
|
|
// legs anim states
|
|
void Legs_Idle();
|
|
void Legs_Walk();
|
|
|
|
void open_section_1();
|
|
void close_section_1();
|
|
void open_section_2();
|
|
void close_section_2();
|
|
void close_section_3();
|
|
void open_section_3();
|
|
void close_section_4();
|
|
void open_section_4();
|
|
void open_section_5();
|
|
void close_section_5();
|
|
void open_section_6();
|
|
void close_section_6();
|
|
void open_section_7();
|
|
void close_section_7();
|
|
void open_section_8();
|
|
void close_section_8();
|
|
|
|
};
|
|
|
|
/***********************************************************************
|
|
|
|
Torso animation control
|
|
|
|
***********************************************************************/
|
|
|
|
void monster_boss_sabaoth::Torso_Idle() {
|
|
idleAnim( ANIMCHANNEL_TORSO, "stand" );
|
|
}
|
|
|
|
void monster_boss_sabaoth::Torso_MeleeAttack() {
|
|
playAnim( ANIMCHANNEL_TORSO, "melee_attack" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 3 ) ) {
|
|
lookAtEnemy( 1 );
|
|
waitFrame();
|
|
}
|
|
finishAction( "melee_attack" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
|
|
}
|
|
|
|
|
|
void monster_boss_sabaoth::Torso_RangeAttack() {
|
|
playAnim( ANIMCHANNEL_TORSO, "range_attack" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 3 ) ) {
|
|
lookAtEnemy( 1 );
|
|
waitFrame();
|
|
}
|
|
finishAction( "range_attack" );
|
|
nextAttack = DelayTime( SABAOTH_ATTACK_RATE );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
Legs animation control
|
|
|
|
***********************************************************************/
|
|
|
|
void monster_boss_sabaoth::Legs_Idle() {
|
|
idleAnim( ANIMCHANNEL_LEGS, "stand" );
|
|
|
|
eachFrame {
|
|
if ( AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); }
|
|
}
|
|
}
|
|
|
|
void monster_boss_sabaoth::Legs_Walk() {
|
|
playCycle( ANIMCHANNEL_LEGS, "move_forward" );
|
|
|
|
eachFrame {
|
|
if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); }
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
AI
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::init
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::init() {
|
|
open_section_2();
|
|
open_section_3();
|
|
open_section_4();
|
|
open_section_5();
|
|
open_section_6();
|
|
open_section_7();
|
|
open_section_8();
|
|
|
|
// bring center section up
|
|
$func_mover_13.move( UP, CENTER_DIST );
|
|
|
|
|
|
movespeed = getFloatKey( "fly_speed" );
|
|
setState( "state_Begin" );
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
States
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::state_Killed
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::state_Killed() {
|
|
stopMove();
|
|
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
|
|
animState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
|
|
waitAction( "dead" );
|
|
setState( "state_Dead" );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::state_Begin
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::state_Begin() {
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
|
|
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
|
|
monster_begin();
|
|
setMoveType( MOVETYPE_SLIDE );
|
|
setState( "state_Idle" );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::state_Idle
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::state_Idle() {
|
|
wait_for_enemy();
|
|
|
|
nextAttack = 0;
|
|
current_path = randomPath();
|
|
setState( "state_Combat" );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::move_to_corner
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::move_to_corner( entity path, boolean noslowtostop ) {
|
|
float attack_flags;
|
|
float dist;
|
|
float randomSpeed = sys.random ( 100 ) + 75;
|
|
float currentMoveSpeed = movespeed + randomSpeed;
|
|
setFlySpeed( currentMoveSpeed );
|
|
|
|
startSound ("snd_move_start", SND_CHANNEL_BODY, false );
|
|
startSound ("snd_move", SND_CHANNEL_BODY2, false );
|
|
|
|
moveToEntity( path );
|
|
while( !AI_MOVE_DONE ) {
|
|
dist = distanceTo( path );
|
|
if ( dist < SABAOTH_SLOW_DIST ) {
|
|
if ( noslowtostop ) {
|
|
return;
|
|
}
|
|
setFlySpeed( currentMoveSpeed * ( dist / SABAOTH_SLOW_DIST ) );
|
|
}
|
|
|
|
if ( AI_ENEMY_VISIBLE ) {
|
|
lookAtEnemy( 1 );
|
|
}
|
|
|
|
if ( AI_ENEMY_DEAD ) {
|
|
enemy_dead();
|
|
}
|
|
|
|
attack_flags = check_attacks( 0 );
|
|
if ( attack_flags ) {
|
|
do_attack( attack_flags );
|
|
continue;
|
|
}
|
|
|
|
if ( !enemyPositionValid() ) {
|
|
locateEnemy();
|
|
}
|
|
|
|
waitFrame();
|
|
}
|
|
if ( !noslowtostop ) {
|
|
setFlySpeed( 0 );
|
|
}
|
|
startSound ("snd_move_stop", SND_CHANNEL_BODY2, false );
|
|
stopMove();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::turn_to_corner
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::turn_to_corner( entity path ) {
|
|
float attack_flags;
|
|
|
|
startSound ("snd_move_start", SND_CHANNEL_BODY, false );
|
|
startSound ("snd_move", SND_CHANNEL_BODY2, false );
|
|
|
|
turnToEntity( path );
|
|
while( !facingIdeal() ) {
|
|
if ( AI_ENEMY_VISIBLE ) {
|
|
lookAtEnemy( 1 );
|
|
}
|
|
|
|
if ( AI_ENEMY_DEAD ) {
|
|
enemy_dead();
|
|
}
|
|
|
|
attack_flags = check_attacks( 1 );
|
|
if ( attack_flags ) {
|
|
do_attack( attack_flags );
|
|
continue;
|
|
}
|
|
|
|
|
|
if ( !enemyPositionValid() ) {
|
|
locateEnemy();
|
|
}
|
|
|
|
waitFrame();
|
|
}
|
|
stopMove();
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::closeSection
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::closeSection( entity path ) {
|
|
string func;
|
|
|
|
func = path.getKey( "close" );
|
|
if ( func != "" ) {
|
|
callFunction( func );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::openSection
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::openSection( entity path ) {
|
|
string func;
|
|
|
|
func = path.getKey( "open" );
|
|
if ( func != "" ) {
|
|
callFunction( func );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::state_Combat
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::state_Combat() {
|
|
entity next_path;
|
|
entity prev_path;
|
|
boolean no_turn;
|
|
|
|
next_path = current_path.randomPath();
|
|
no_turn = false;
|
|
while( current_path ) {
|
|
if ( !no_turn ) {
|
|
turn_to_corner( current_path );
|
|
} else {
|
|
sys.print( "no turn to " + current_path.getName() + "\n" );
|
|
}
|
|
no_turn = current_path.getIntKey( "no_turn" );
|
|
if ( no_turn ) {
|
|
sys.print( "no slow to " + current_path.getName() + "\n" );
|
|
}
|
|
move_to_corner( current_path, no_turn );
|
|
|
|
openSection( prev_path );
|
|
|
|
prev_path = current_path;
|
|
current_path = next_path;
|
|
next_path = current_path.randomPath();
|
|
|
|
closeSection( next_path );
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
attacks
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::do_attack
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::do_attack( float attack_flags ) {
|
|
if ( attack_flags & ATTACK_MISSILE ) {
|
|
combat_range();
|
|
} else if ( attack_flags & ATTACK_MELEE ) {
|
|
combat_melee();
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::check_attacks
|
|
=====================
|
|
*/
|
|
float monster_boss_sabaoth::check_attacks( float meleeOnly ) {
|
|
float attack_flags;
|
|
float checkMelee;
|
|
|
|
attack_flags = 0;
|
|
if ( !inAnimState( ANIMCHANNEL_TORSO, "Torso_Idle" ) ) {
|
|
return attack_flags;
|
|
}
|
|
if ( AI_ENEMY_VISIBLE ) {
|
|
if ( enemyRange() < MELEE_INIT ) {
|
|
attack_flags |= ATTACK_MELEE;
|
|
} else if ( !meleeOnly && ( !projectile || ( projectile.getProjectileState() != PROJECTILE_LAUNCHED ) ) && ( !canReachEnemy() || ( sys.getTime() >= nextAttack ) ) ) {
|
|
if ( canHitEnemyFromJoint( "mech_bfg" ) ) {
|
|
attack_flags |= ATTACK_MISSILE;
|
|
}
|
|
}
|
|
}
|
|
return attack_flags;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::combat_range
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::combat_range() {
|
|
animState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
|
|
|
|
// animation will set the next attack time, so just delay it for a long time for now
|
|
nextAttack = sys.getTime() + 10000;
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::combat_melee
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::combat_melee() {
|
|
animState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 4 );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_boss_sabaoth::fire_bfg
|
|
=====================
|
|
*/
|
|
void monster_boss_sabaoth::fire_bfg() {
|
|
projectile = attackMissile( "mech_bfg" );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_1() {
|
|
$func_mover_7.move( NORTH, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_1() {
|
|
$func_mover_7.move( NORTH, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_2() {
|
|
$func_mover_8.move( NORTH, SECTION_MOVE_DIST );
|
|
$func_mover_9.move( EAST, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_2() {
|
|
$func_mover_8.move( NORTH, -SECTION_MOVE_DIST );
|
|
$func_mover_9.move( EAST, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_3() {
|
|
$func_mover_10.move( EAST, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_3() {
|
|
$func_mover_10.move( EAST, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_4() {
|
|
$func_mover_11.move( EAST, SECTION_MOVE_DIST );
|
|
$func_mover_12.move( SOUTH, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_4() {
|
|
$func_mover_11.move( EAST, -SECTION_MOVE_DIST );
|
|
$func_mover_12.move( SOUTH, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_5() {
|
|
$func_mover_1.move( SOUTH, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_5() {
|
|
$func_mover_1.move( SOUTH, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_6() {
|
|
$func_mover_2.move( SOUTH, SECTION_MOVE_DIST );
|
|
$func_mover_3.move( WEST, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_6() {
|
|
$func_mover_2.move( SOUTH, -SECTION_MOVE_DIST );
|
|
$func_mover_3.move( WEST, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_7() {
|
|
$func_mover_4.move( WEST, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_7() {
|
|
$func_mover_4.move( WEST, -SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::open_section_8() {
|
|
$func_mover_5.move( WEST, SECTION_MOVE_DIST );
|
|
$func_mover_6.move( NORTH, SECTION_MOVE_DIST );
|
|
}
|
|
|
|
void monster_boss_sabaoth::close_section_8() {
|
|
$func_mover_5.move( WEST, -SECTION_MOVE_DIST );
|
|
$func_mover_6.move( NORTH, -SECTION_MOVE_DIST );
|
|
}
|