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_demon_sentry.script
2022-08-27 13:19:00 +02:00

531 lines
11 KiB
Text

/***********************************************************************
ai_monster_demon_sentry.script
monster_demon_sentry
***********************************************************************/
#define DEMON_SENTRY_RUNDISTANCE 192
#define DEMON_SENTRY_FIRERATE 0.1
#define DEMON_SENTRY_MIN_SHOTS 8
#define DEMON_SENTRY_WALKTURN 65
#define DEMON_SENTRY_WALKTURN2 30
#define DEMON_SENTRY_ATTACK_MAX_LENGTH 3
#define DEMON_SENTRY_ATTACK_MIN_LENGTH 1.2
#define DEMON_SENTRY_WAIT_MAX_LENGTH 1
#define DEMON_SENTRY_WAIT_MIN_LENGTH 0.3
#define DEMON_SENTRY_ATTACK_DELAY 2
object monster_demon_sentry : monster_base {
boolean fire;
entity light;
float nextAttack;
//
// States
//
void state_Idle();
void state_Begin();
void state_Killed();
void init();
void destroy();
void spawn_flashlight();
void flashlight_off();
void flashlight_on();
float should_turn_left();
float should_turn_right();
// attack checks
float check_attacks();
void do_attack( float attack_flags );
void combat_range();
// torso anim states
void Torso_Death();
void Torso_Idle();
void Torso_Pain();
void Torso_RangeAttack();
// legs anim states
void Legs_Death();
void Legs_Idle();
void Legs_Walk();
void Legs_TurnLeft();
void Legs_TurnRight();
};
/***********************************************************************
Torso animation control
***********************************************************************/
void monster_demon_sentry::Torso_Death() {
finishAction( "dead" );
// never exit
waitUntil( 0 );
}
void monster_demon_sentry::Torso_Idle() {
idleAnim( ANIMCHANNEL_TORSO, "stand" );
eachFrame {
if ( AI_PAIN ) {
Torso_Pain();
idleAnim( ANIMCHANNEL_TORSO, "stand" );
}
if ( fire ) {
animState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
}
}
}
void monster_demon_sentry::Torso_Pain() {
string animname;
animname = getPainAnim();
setBlendFrames( ANIMCHANNEL_TORSO, 2 );
playAnim( ANIMCHANNEL_TORSO, animname );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
// no pain for 2 seconds
preventPain( 2 );
finishAction( "pain" );
setBlendFrames( ANIMCHANNEL_TORSO, 4 );
}
void monster_demon_sentry::Torso_RangeAttack() {
float endtime;
float firetime;
float numshots;
setAnimPrefix( "" );
playAnim( ANIMCHANNEL_TORSO, "range_attack" );
while( !animDone( ANIMCHANNEL_TORSO, 0 ) ) {
if ( AI_PAIN ) {
Torso_Pain();
}
waitFrame();
}
numshots = 0;
while( fire || ( numshots < DEMON_SENTRY_MIN_SHOTS ) ) {
endtime = RandomDelay( DEMON_SENTRY_ATTACK_MIN_LENGTH, DEMON_SENTRY_ATTACK_MAX_LENGTH );
setBlendFrames( ANIMCHANNEL_TORSO, 2 );
playCycle( ANIMCHANNEL_TORSO, "range_attack_loop" );
firetime = sys.getTime();
while( ( fire || ( numshots < DEMON_SENTRY_MIN_SHOTS ) ) && ( sys.getTime() < endtime ) ) {
if ( sys.getTime() >= firetime ) {
startSound( "snd_fire", SND_CHANNEL_WEAPON, false );
attackMissile( "Barrel" );
numshots++;
firetime = sys.getTime() + DEMON_SENTRY_FIRERATE;
}
if ( AI_PAIN ) {
Torso_Pain();
playCycle( ANIMCHANNEL_TORSO, "range_attack_loop" );
}
waitFrame();
}
if ( !fire && ( numshots >= DEMON_SENTRY_MIN_SHOTS ) ) {
break;
}
setBlendFrames( ANIMCHANNEL_TORSO, 2 );
playCycle( ANIMCHANNEL_TORSO, "range_attack_aim" );
endtime = RandomDelay( DEMON_SENTRY_WAIT_MIN_LENGTH, DEMON_SENTRY_WAIT_MAX_LENGTH );
while( ( fire || ( numshots < DEMON_SENTRY_MIN_SHOTS ) ) && ( sys.getTime() < endtime ) ) {
if ( AI_PAIN ) {
Torso_Pain();
}
waitFrame();
}
}
playAnim( ANIMCHANNEL_TORSO, "range_attack_end" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
if ( AI_PAIN ) {
Torso_Pain();
}
waitFrame();
}
finishAction( "range_attack" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
}
/***********************************************************************
Legs animation control
***********************************************************************/
void monster_demon_sentry::Legs_Death() {
while( AI_DEAD ) {
waitFrame();
}
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
float monster_demon_sentry::should_turn_left() {
float turnAmount;
turnAmount = getTurnDelta();
if ( turnAmount > 10 ) {
if ( hasAnim( ANIMCHANNEL_LEGS, "turn_left" ) ) {
return true;
}
}
return false;
}
float monster_demon_sentry::should_turn_right() {
float turnAmount;
turnAmount = getTurnDelta();
if ( turnAmount < -10 ) {
if ( hasAnim( ANIMCHANNEL_LEGS, "turn_right" ) ) {
return true;
}
}
return false;
}
void monster_demon_sentry::Legs_Idle() {
float delta;
if ( !AI_FORWARD && !facingIdeal() ) {
if ( should_turn_left() ) {
animState( ANIMCHANNEL_LEGS, "Legs_TurnLeft", 4 );
}
if ( should_turn_right() ) {
animState( ANIMCHANNEL_LEGS, "Legs_TurnRight", 4 );
}
}
idleAnim( ANIMCHANNEL_LEGS, "stand" );
eachFrame {
if ( AI_FORWARD ) {
delta = getTurnDelta();
if ( ( delta <= DEMON_SENTRY_WALKTURN ) && ( delta >= -DEMON_SENTRY_WALKTURN ) ) {
animState( ANIMCHANNEL_LEGS, "Legs_Walk", 12 );
}
}
if ( !facingIdeal() ) {
if ( should_turn_left() ) {
animState( ANIMCHANNEL_LEGS, "Legs_TurnLeft", 4 );
}
if ( should_turn_right() ) {
animState( ANIMCHANNEL_LEGS, "Legs_TurnRight", 4 );
}
}
}
}
void monster_demon_sentry::Legs_Walk() {
float delta;
playCycle( ANIMCHANNEL_LEGS, "walk" );
while( AI_FORWARD ) {
delta = getTurnDelta();
if ( ( delta > DEMON_SENTRY_WALKTURN ) || ( delta < -DEMON_SENTRY_WALKTURN ) ) {
break;
}
waitFrame();
}
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 12 );
}
void monster_demon_sentry::Legs_TurnLeft() {
float turnAmount;
float delta;
turnAmount = getTurnDelta();
if ( turnAmount > 110 ) {
// do it in two turns
turnAmount *= 0.5;
}
playAnim( ANIMCHANNEL_LEGS, "turn_left" );
while( !animDone( ANIMCHANNEL_LEGS, 0 ) ) {
if ( AI_FORWARD ) {
delta = getTurnDelta();
if ( ( delta <= DEMON_SENTRY_WALKTURN2 ) && ( delta >= -DEMON_SENTRY_WALKTURN2 ) ) {
animState( ANIMCHANNEL_LEGS, "Legs_Walk", 12 );
}
}
waitFrame();
}
playAnim( ANIMCHANNEL_LEGS, "turn_right" );
while( !animDone( ANIMCHANNEL_LEGS, 8 ) ) {
if ( AI_FORWARD ) {
delta = getTurnDelta();
if ( ( delta <= DEMON_SENTRY_WALKTURN2 ) && ( delta >= -DEMON_SENTRY_WALKTURN2 ) ) {
animState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 );
}
}
waitFrame();
}
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
}
void monster_demon_sentry::Legs_TurnRight() {
float turnAmount;
float delta;
turnAmount = getTurnDelta();
if ( turnAmount < -110 ) {
// do it in two turns
turnAmount *= 0.5;
}
playAnim( ANIMCHANNEL_LEGS, "turn_right" );
while( !animDone( ANIMCHANNEL_LEGS, 0 ) ) {
if ( AI_FORWARD ) {
delta = getTurnDelta();
if ( ( delta <= DEMON_SENTRY_WALKTURN2 ) && ( delta >= -DEMON_SENTRY_WALKTURN2 ) ) {
animState( ANIMCHANNEL_LEGS, "Legs_Walk", 12 );
}
}
waitFrame();
}
playAnim( ANIMCHANNEL_LEGS, "turn_left" );
while( !animDone( ANIMCHANNEL_LEGS, 8 ) ) {
if ( AI_FORWARD ) {
delta = getTurnDelta();
if ( ( delta <= DEMON_SENTRY_WALKTURN2 ) && ( delta >= -DEMON_SENTRY_WALKTURN2 ) ) {
animState( ANIMCHANNEL_LEGS, "Legs_Walk", 8 );
}
}
waitFrame();
}
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
}
/* ===================================================================================== */
/* ===================================================================================== */
/* ===================================================================================== */
/*
=====================
monster_demon_sentry::init
=====================
*/
void monster_demon_sentry::init() {
run_distance = DEMON_SENTRY_RUNDISTANCE;
nextAttack = 0;
setState( "state_Begin" );
}
/*
=====================
monster_demon_sentry::state_Begin
=====================
*/
void monster_demon_sentry::state_Begin() {
fire = false;
setBoneMod( true );
if ( getIntKey( "flashlight" ) ) {
spawn_flashlight();
}
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
monster_begin();
setMoveType( MOVETYPE_ANIM );
setState( "state_Idle" );
}
/*
=====================
monster_demon_sentry::state_Idle
=====================
*/
void monster_demon_sentry::state_Idle() {
setState( "state_Combat" );
}
/*
=====================
monster_demon_sentry::state_Killed
=====================
*/
void monster_demon_sentry::state_Killed() {
stopMove();
flashlight_off();
animState( ANIMCHANNEL_TORSO, "Torso_Death", 0 );
animState( ANIMCHANNEL_LEGS, "Legs_Death", 0 );
waitAction( "dead" );
float burnDelay = getFloatKey( "burnaway" );
if ( burnDelay != 0 ) {
preBurn();
sys.wait( burnDelay );
burn();
startSound( "snd_burn", SND_CHANNEL_BODY, false );
sys.wait( 3 );
remove();
}
stopThinking();
}
/*
=====================
monster_demon_sentry::destroy
=====================
*/
void monster_demon_sentry::destroy() {
light.remove();
}
/*
=====================
monster_demon_sentry::spawn_flashlight
=====================
*/
void monster_demon_sentry::spawn_flashlight() {
string texture;
float distance;
distance = getFloatKey( "flashlight_distance" );
if ( !distance ) {
distance = 640;
}
// use ik_pose to bind light
playAnim( ANIMCHANNEL_TORSO, "ik_pose" );
if ( getIntKey( "flashlight" ) == 2 ) {
// spot light
sys.setSpawnArg( "light_target", "1 0 0" );
sys.setSpawnArg( "light_up", "0 0 .5" );
sys.setSpawnArg( "light_right", "0 -.5 0" );
sys.setSpawnArg( "light_end", distance + " 0 0" );
texture = getKey( "mtr_flashlight" );
sys.setSpawnArg( "texture", texture );
sys.setSpawnArg( "name", getName() + "_light" );
light = sys.spawn( "light" );
light.setAngles( getAngles() );
light.bindToJoint( self, "light", true );
light.setOrigin( '0 0 0' );
} else {
// radial light
sys.setSpawnArg( "name", getName() + "_light" );
light = sys.spawn( "light" );
light.setRadius( 256 );
light.setAngles( getAngles() + '0 90 0' );
light.bindToJoint( self, "light", true );
texture = getKey( "mtr_flashlight" );
light.setShader( texture );
light.setOrigin( '0 0 0' );
}
stopAnim( ANIMCHANNEL_TORSO, 0 );
flashlight_on();
}
/*
=====================
monster_demon_sentry::flashlight_off
=====================
*/
void monster_demon_sentry::flashlight_off() {
string skin;
if ( light ) {
light.Off();
skin = getKey( "skin_flashlight_off" );
setSkin( skin );
}
}
/*
=====================
monster_demon_sentry::flashlight_on
=====================
*/
void monster_demon_sentry::flashlight_on() {
string skin;
if ( light ) {
light.On();
skin = getKey( "skin_flashlight_on" );
setSkin( skin );
}
}
/*
=====================
monster_demon_sentry::do_attack
=====================
*/
void monster_demon_sentry::do_attack( float attack_flags ) {
if ( attack_flags & ATTACK_MISSILE ) {
combat_range();
}
}
/*
=====================
monster_demon_sentry::check_attacks
=====================
*/
float monster_demon_sentry::check_attacks() {
float attack_flags = 0;
float currentTime = sys.getTime();
if ( canHitEnemyFromAnim( "range_attack_loop" ) && ( currentTime > nextAttack ) ) {
attack_flags |= ATTACK_MISSILE;
}
return attack_flags;
}
/*
=====================
monster_demon_sentry::combat_range
=====================
*/
void monster_demon_sentry::combat_range() {
faceEnemy();
fire = true;
while( canHitEnemyFromAnim( "range_attack_loop" ) ) {
waitFrame();
}
fire = false;
waitUntil( !inAnimState( ANIMCHANNEL_TORSO, "Torso_RangeAttack" ) );
// don't attack for a bit
nextAttack = DelayTime( DEMON_SENTRY_ATTACK_DELAY );
}