mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-15 23:21:35 +00:00
1219 lines
No EOL
26 KiB
Text
1219 lines
No EOL
26 KiB
Text
/***********************************************************************
|
|
|
|
ai_monster_hunter_invul.script
|
|
|
|
monster_hunter_invul
|
|
|
|
***********************************************************************/
|
|
|
|
#define INVUL_STATE_WAIT 0
|
|
#define INVUL_STATE_INVUL_SURVIVAL 1
|
|
#define INVUL_STATE_INVUL 2
|
|
#define INVUL_STATE_NORMAL 3
|
|
|
|
#define INVUL_ATTACK_RATE_NORMAL 3
|
|
#define INVUL_ATTACK_RATE_INVUL 4
|
|
#define INVUL_ATTACK_RATE_INVUL_SURVIVAL 0
|
|
|
|
#define SURVIVAL_MODE_TIME 15
|
|
|
|
|
|
#define ATTACK_GROUNDPOUND_NODAMAGE ATTACK_SPECIAL1
|
|
#define ATTACK_GROUNDPOUND ATTACK_SPECIAL2
|
|
#define ATTACK_ELECTRIC_WALL ATTACK_SPECIAL3
|
|
|
|
|
|
#define BEGIN_HEALTH 5500
|
|
#define STAGE_0_HEALTH 3900
|
|
#define STAGE_1_HEALTH 2400
|
|
#define STAGE_2_HEALTH 1000
|
|
|
|
|
|
#define ARCH_ELECTRIC_WALL_NUM 20
|
|
#define ARCH_ELECTRIC_WALL_SEPERATION 48
|
|
#define ARCH_ELECTRIC_WALL_RANGE ( ARCH_ELECTRIC_WALL_NUM * ARCH_ELECTRIC_WALL_SEPERATION )
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
monster_hunter_invul
|
|
|
|
***********************************************************************/
|
|
object monster_hunter_invul : monster_base {
|
|
|
|
|
|
float nextAttack;
|
|
|
|
string range_attack_anim;
|
|
|
|
float survivalStart;
|
|
float endInvul;
|
|
|
|
//vector invul_location;
|
|
|
|
float combatStage;
|
|
float hunterState;
|
|
|
|
|
|
|
|
// torso anim states
|
|
void Torso_Idle();
|
|
void Torso_Pain();
|
|
void Torso_MeleeAttack();
|
|
void Torso_RangeAttack();
|
|
void Torso_ElectricWallAttack();
|
|
void Torso_InvulRange_Short();
|
|
void Torso_InvulRange_Tall();
|
|
void Torso_Shockwave();
|
|
void Torso_ShockwaveNoDamage();
|
|
void Torso_SummonShell();
|
|
void Torso_PreSummon();
|
|
void Torso_ShutdownShell();
|
|
|
|
|
|
// legs anim states
|
|
void Legs_Idle();
|
|
void Legs_Walk();
|
|
|
|
//Initialization
|
|
void init();
|
|
void destroy();
|
|
void endInvuln();
|
|
|
|
//
|
|
// States
|
|
//
|
|
void state_Begin();
|
|
void state_Idle();
|
|
void state_Dead();
|
|
|
|
//Invulnerability States
|
|
void state_Invulnerability();
|
|
void state_MoveToInvul();
|
|
|
|
void state_BeginInvulSurvival();
|
|
void state_EndInvulSurvival();
|
|
|
|
void state_BeginInvul();
|
|
void state_EndInvul();
|
|
|
|
//Normal States
|
|
//void state_Normal();
|
|
void state_BeginNormal();
|
|
//void state_EndNormal();
|
|
|
|
//Combat States
|
|
void state_CombatNormal();
|
|
void state_CombatInvul();
|
|
void state_CombatInvulSurvival();
|
|
|
|
//Testing States
|
|
void state_EasyKill();
|
|
|
|
// attacks
|
|
float check_attacks();
|
|
float check_attacks_normal();
|
|
float check_attacks_invul();
|
|
float check_attacks_invul_survival();
|
|
void do_attack( float attack_flags );
|
|
void combat_range();
|
|
void combat_melee();
|
|
|
|
void combat_shockwave();
|
|
void combat_electricwall();
|
|
void combat_shockwave_nodamage();
|
|
//void combat_invulrange_short();
|
|
//void combat_invulrange_tall();
|
|
|
|
|
|
void spawn_electric_wall( vector org );
|
|
void electric_wall_loop();
|
|
void electric_wall();
|
|
|
|
void electric_effect_loop();
|
|
|
|
float attackCount;
|
|
float attack0;
|
|
float attack1;
|
|
float attack2;
|
|
float attack3;
|
|
float attack4;
|
|
|
|
void ClearAttacks();
|
|
void PushAttack(float attack);
|
|
float SelectRandomAttack();
|
|
|
|
|
|
};
|
|
|
|
void monster_hunter_invul::ClearAttacks() {
|
|
attackCount = 0;
|
|
}
|
|
|
|
void monster_hunter_invul::PushAttack(float attack) {
|
|
|
|
if (attackCount == 0) {
|
|
attack0 = attack;
|
|
} else if (attackCount == 1) {
|
|
attack1 = attack;
|
|
} else if (attackCount == 2) {
|
|
attack2 = attack;
|
|
} else if (attackCount == 3) {
|
|
attack3 = attack;
|
|
} else if (attackCount == 4) {
|
|
attack4 = attack;
|
|
}
|
|
attackCount++;
|
|
}
|
|
|
|
float monster_hunter_invul::SelectRandomAttack() {
|
|
|
|
if(attackCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
float selectedAttack;
|
|
selectedAttack = sys.randomInt(attackCount);
|
|
|
|
if (selectedAttack == 0) {
|
|
return attack0;
|
|
} else if (selectedAttack == 1) {
|
|
return attack1;
|
|
} else if (selectedAttack == 2) {
|
|
return attack2;
|
|
} else if (selectedAttack == 3) {
|
|
return attack3;
|
|
} else if (selectedAttack == 4) {
|
|
return attack4;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Torso animation control
|
|
|
|
***********************************************************************/
|
|
|
|
void monster_hunter_invul::Torso_Idle() {
|
|
|
|
if(hunterState == INVUL_STATE_NORMAL) {
|
|
idleAnim( ANIMCHANNEL_TORSO, "idle" );
|
|
} else {
|
|
idleAnim( ANIMCHANNEL_TORSO, "invuln_idle" );
|
|
}
|
|
|
|
|
|
eachFrame {
|
|
if ( AI_PAIN ) {
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
|
|
}
|
|
waitFrame();
|
|
}
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_Pain() {
|
|
string animname;
|
|
float nextpain;
|
|
float currenttime;
|
|
|
|
|
|
animname = getPainAnim();
|
|
playAnim( ANIMCHANNEL_TORSO, animname );
|
|
|
|
nextpain = sys.getTime() + 0.25;
|
|
|
|
while( !animDone( ANIMCHANNEL_TORSO, 6 ) ) {
|
|
if ( AI_PAIN ) {
|
|
currenttime = sys.getTime();
|
|
if ( currenttime > nextpain ) {
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
|
|
}
|
|
}
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "pain" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
|
|
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_MeleeAttack() {
|
|
|
|
if(hunterState == INVUL_STATE_INVUL) {
|
|
playAnim( ANIMCHANNEL_TORSO, "invuln_melee_attack" );
|
|
} else {
|
|
playAnim( ANIMCHANNEL_TORSO, "melee_attack" );
|
|
}
|
|
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "melee_attack" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_RangeAttack() {
|
|
string anim;
|
|
|
|
disablePain();
|
|
|
|
|
|
allowMovement( false );
|
|
if(range_attack_anim == "") {
|
|
sys.print("No range attack defined\n");
|
|
range_attack_anim = chooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
|
|
}
|
|
playAnim( ANIMCHANNEL_TORSO, range_attack_anim );
|
|
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
lookAt( getEnemy(), 1 );
|
|
waitFrame();
|
|
}
|
|
|
|
allowMovement( true );
|
|
finishAction( "range_attack" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
}
|
|
|
|
|
|
void monster_hunter_invul::Torso_ElectricWallAttack() {
|
|
string anim;
|
|
|
|
disablePain();
|
|
|
|
setShaderParm( 4, -sys.getTime() );
|
|
setSmokeVisibility( ALL_PARTICLES, 1 );
|
|
anim = chooseAnim( ANIMCHANNEL_TORSO, "electricwall_attack" );
|
|
if ( testAnimMoveTowardEnemy( anim ) ) {
|
|
playAnim( ANIMCHANNEL_TORSO, anim );
|
|
} else {
|
|
allowMovement( false );
|
|
anim = chooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
|
|
playAnim( ANIMCHANNEL_TORSO, anim );
|
|
}
|
|
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
allowMovement( true );
|
|
finishAction( "electricwall_attack" );
|
|
setSmokeVisibility( ALL_PARTICLES, 0 );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_InvulRange_Short() {
|
|
playAnim( ANIMCHANNEL_TORSO, "missile_low" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "invulrange_short" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_InvulRange_Tall() {
|
|
playAnim( ANIMCHANNEL_TORSO, "missile_high" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "invulrange_tall" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_Shockwave() {
|
|
|
|
playAnim( ANIMCHANNEL_TORSO, "invuln_shockwave" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "shockwave" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_ShockwaveNoDamage() {
|
|
|
|
playAnim( ANIMCHANNEL_TORSO, "invuln_shockwave_nodamage" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "shockwave" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
|
|
}
|
|
|
|
|
|
void monster_hunter_invul::Torso_SummonShell() {
|
|
|
|
playAnim( ANIMCHANNEL_TORSO, "invuln_summon" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "summonshell" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_PreSummon() {
|
|
|
|
playAnim( ANIMCHANNEL_TORSO, "pre_summon" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "presummon" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::Torso_ShutdownShell() {
|
|
|
|
playAnim( ANIMCHANNEL_TORSO, "invuln_shutdown" );
|
|
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
|
|
waitFrame();
|
|
}
|
|
|
|
finishAction( "shutdownshell" );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
|
|
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
Legs animation control
|
|
|
|
***********************************************************************/
|
|
|
|
void monster_hunter_invul::Legs_Idle() {
|
|
idleAnim( ANIMCHANNEL_LEGS, "idle" );
|
|
|
|
eachFrame {
|
|
if ( AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); }
|
|
}
|
|
}
|
|
|
|
void monster_hunter_invul::Legs_Walk() {
|
|
playCycle( ANIMCHANNEL_LEGS, "walk" );
|
|
|
|
eachFrame {
|
|
if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); }
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
AI
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
=====================
|
|
monster_hunter_invul::init
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::init() {
|
|
|
|
endInvul = 0;
|
|
|
|
combatStage = 0;
|
|
setDamageCap(STAGE_0_HEALTH);
|
|
setHealth(BEGIN_HEALTH);
|
|
|
|
nextAttack = 0;
|
|
|
|
// Tatoos start off blue
|
|
setColor( 0.68, 0.95, 1 );
|
|
|
|
setState( "state_Begin" );
|
|
}
|
|
|
|
void monster_hunter_invul::destroy() {
|
|
}
|
|
|
|
void monster_hunter_invul::endInvuln() {
|
|
endInvul = 1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
|
|
States
|
|
|
|
***********************************************************************/
|
|
|
|
/*
|
|
=====================
|
|
monster_hunter_invul::state_Begin
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::state_Begin() {
|
|
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
|
|
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
|
|
monster_begin();
|
|
|
|
setState( "state_Idle" );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_hunter_invul::state_Idle
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::state_Idle() {
|
|
|
|
setEnemy($player1);
|
|
//wait_for_enemy();
|
|
|
|
//Start a thread to spawn some effects
|
|
//thread electric_effect_loop();
|
|
|
|
setState("state_BeginNormal");
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_base::state_Dead
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::state_Dead() {
|
|
|
|
//Todo: Probably need to warp to a point for the cutscene
|
|
//I am dead so for now just stop moving
|
|
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 8 );
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Pain", 8 );
|
|
waitAction( "pain" );
|
|
|
|
remove();
|
|
}
|
|
|
|
|
|
//Invulnerability States
|
|
|
|
void monster_hunter_invul::state_Invulnerability() {
|
|
setState("state_MoveToInvul");
|
|
}
|
|
|
|
void monster_hunter_invul::state_MoveToInvul() {
|
|
|
|
//Let the level know we are moving to the center of the room to begin invulnerability
|
|
sys.trigger($movingToInvuln);
|
|
|
|
//According to the level designers we want to make this last at least 3 seconds to
|
|
//let the level play some effects
|
|
//float waitTime;
|
|
//waitTime = 4;
|
|
|
|
float startTime, currentTime;
|
|
startTime = sys.getTime();
|
|
currentTime = sys.getTime();
|
|
|
|
//The hunter does not take damage while moving to the invulnerable point
|
|
ignoreDamage();
|
|
|
|
//The hunter moves to the center of the room before he goes into invulnerability mode
|
|
entity ent;
|
|
ent = sys.getEntity("hunter_invul_location");
|
|
|
|
moveToEntity(ent);
|
|
//while( ((currentTime-startTime) < waitTime) || !AI_MOVE_DONE && !AI_DEST_UNREACHABLE ) {
|
|
while( !AI_MOVE_DONE && !AI_DEST_UNREACHABLE ) {
|
|
currentTime = sys.getTime();
|
|
|
|
//If the player gets in my way dole out a world of hurt
|
|
float canMelee;
|
|
canMelee = testMeleeAttack();
|
|
if ( AI_ENEMY_IN_FOV ) {
|
|
if ( canMelee ) {
|
|
combat_melee();
|
|
}
|
|
}
|
|
waitFrame();
|
|
}
|
|
|
|
slideTo(ent.getOrigin(), 0);
|
|
|
|
//Play little pre invuln animation to give the level some time
|
|
//animState( ANIMCHANNEL_TORSO, "Torso_PreSummon", 4 );
|
|
//waitAction( "presummon" );
|
|
|
|
stopMove();
|
|
|
|
|
|
|
|
setState("state_BeginInvulSurvival");
|
|
}
|
|
|
|
void monster_hunter_invul::state_BeginInvulSurvival() {
|
|
hunterState = INVUL_STATE_INVUL_SURVIVAL;
|
|
|
|
//Let the level know we are starting the invulnerability survival phase
|
|
sys.trigger($beginInvulnSurvival);
|
|
|
|
//Hunter is invulnerable
|
|
ignoreDamage();
|
|
|
|
stopMove();
|
|
|
|
//Skin changing and summon
|
|
setSkin("skins/monsters/hunter_invul/invuln_summon_shell");
|
|
setShaderTime( self );
|
|
|
|
//Crossfade the color for the tatoos
|
|
//thread crossFadeEnt( self, '1 0.18 0', '0.68 0.95 1', 1 );
|
|
|
|
//Play an animation when he goes into berserk mode
|
|
animState( ANIMCHANNEL_TORSO, "Torso_SummonShell", 4 );
|
|
waitAction( "summonshell" );
|
|
|
|
setSkin("skins/monsters/hunter_invul/invuln_shell");
|
|
|
|
survivalStart = sys.getTime();
|
|
|
|
setState( "state_CombatInvulSurvival" );
|
|
}
|
|
|
|
void monster_hunter_invul::state_EndInvulSurvival() {
|
|
|
|
//Let the level know we are going out of survival mode
|
|
sys.trigger($endSurvival);
|
|
|
|
//Go into a move invulnerable mode
|
|
//setState( "state_BeginInvul" );
|
|
|
|
/*
|
|
Copied from EndInvul because he is going to skip the move around invul state
|
|
and go directly to vulnerable
|
|
*/
|
|
|
|
stopMove();
|
|
|
|
//Switching the skin back to normal
|
|
setSkin("skins/monsters/hunter_invul/invuln_shutdown_shell");
|
|
setShaderTime( self );
|
|
|
|
//Play an animation when he turns off his invuln shell
|
|
animState( ANIMCHANNEL_TORSO, "Torso_ShutdownShell", 4 );
|
|
waitAction( "shutdownshell" );
|
|
|
|
if(combatStage >= 0) {
|
|
setSkin("skins/monsters/hunter_invul/invuln_no_shell2");
|
|
} else {
|
|
setSkin("skins/monsters/hunter_invul/invuln_no_shell");
|
|
}
|
|
|
|
//Let the level know we are done with invulnerability
|
|
sys.trigger($beginNormal);
|
|
|
|
setState( "state_BeginNormal");
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::state_BeginInvul() {
|
|
hunterState = INVUL_STATE_INVUL;
|
|
|
|
//Hunter is invulnerable
|
|
ignoreDamage();
|
|
|
|
allowMovement(true);
|
|
|
|
//nextInvAttack = DelayTime( INVUL_ATTACK_RATE_INV );
|
|
|
|
//Already changed skin during survival mode
|
|
/*setSkin("skins/monsters/hunter_invul/invuln_summon_shell");
|
|
setShaderTime( self );
|
|
|
|
//Crossfade the color for the tatoos
|
|
//thread crossFadeEnt( self, '1 0.18 0', '0.68 0.95 1', 1 );
|
|
|
|
//Play an animation when he goes into berserk mode
|
|
animState( ANIMCHANNEL_TORSO, "Torso_SummonShell", 4 );
|
|
waitAction( "summonshell" );
|
|
|
|
setSkin("skins/monsters/hunter_invul/invuln_shell");*/
|
|
|
|
setState( "state_CombatInvul" );
|
|
}
|
|
|
|
void monster_hunter_invul::state_EndInvul() {
|
|
|
|
stopMove();
|
|
|
|
//Switching the skin back to normal
|
|
setSkin("skins/monsters/hunter_invul/invuln_shutdown_shell");
|
|
setShaderTime( self );
|
|
|
|
//Crossfade the color for the tatoos
|
|
//thread crossFadeEnt( self, '1 0.18 0', '0.68 0.95 1', 1 );
|
|
|
|
//Play an animation when he goes into berserk mode
|
|
animState( ANIMCHANNEL_TORSO, "Torso_ShutdownShell", 4 );
|
|
waitAction( "shutdownshell" );
|
|
|
|
if(combatStage >= 0) {
|
|
setSkin("skins/monsters/hunter_invul/invuln_no_shell2");
|
|
} else {
|
|
setSkin("skins/monsters/hunter_invul/invuln_no_shell");
|
|
}
|
|
|
|
//Let the level know we are done with invulnerability
|
|
sys.trigger($beginNormal);
|
|
|
|
setState( "state_BeginNormal");
|
|
}
|
|
|
|
/*void monster_hunter_invul::state_Normal() {
|
|
setState("state_EndInvul");
|
|
}*/
|
|
|
|
//Normal States
|
|
void monster_hunter_invul::state_BeginNormal() {
|
|
|
|
allowMovement(true);
|
|
|
|
hunterState = INVUL_STATE_NORMAL;
|
|
|
|
stopMove();
|
|
|
|
//Turn on damage while in normal mode
|
|
allowDamage();
|
|
|
|
setState( "state_CombatNormal" );
|
|
}
|
|
|
|
/*void monster_hunter_invul::state_EndNormal() {
|
|
|
|
setState( "state_BeginInvul");
|
|
}*/
|
|
|
|
|
|
|
|
|
|
void monster_hunter_invul::state_CombatNormal() {
|
|
|
|
//Reset So we attack immediate
|
|
nextAttack = 0;
|
|
|
|
//Use the default combat code while in normal mode
|
|
setState("state_Combat");
|
|
}
|
|
|
|
void monster_hunter_invul::state_CombatInvul() {
|
|
|
|
//Reset So we attack immediate
|
|
nextAttack = 0;
|
|
|
|
//Use the default combat code while in invul mode
|
|
setState( "state_Combat" );
|
|
}
|
|
|
|
void monster_hunter_invul::state_CombatInvulSurvival() {
|
|
|
|
float currentTime;
|
|
float attack;
|
|
|
|
stopMove();
|
|
|
|
//Reset So we attack immediate
|
|
nextAttack = 0;
|
|
|
|
eachFrame {
|
|
currentTime = sys.getTime();
|
|
|
|
//Don't timeout...we will just wait until the level tells us to end
|
|
/*if((currentTime - survivalStart) > SURVIVAL_MODE_TIME) {
|
|
//We are done with survival mode
|
|
setState("state_EndInvulSurvival");
|
|
}*/
|
|
|
|
|
|
|
|
if ( AI_ENEMY_VISIBLE ) {
|
|
lookAt( getEnemy(), 1 );
|
|
}
|
|
|
|
if ( AI_ENEMY_DEAD ) {
|
|
enemy_dead();
|
|
}
|
|
|
|
faceEnemy();
|
|
|
|
attack = check_attacks();
|
|
if(attack) {
|
|
do_attack(attack);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_hunter_invul::state_EasyKill
|
|
Sets the hunter immediately into a state ready to die
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::state_EasyKill() {
|
|
sys.print("Easy Kill!!!!\n");
|
|
combatStage = 3;
|
|
setHealth(1);
|
|
setDamageCap(-1);
|
|
setState("state_BeginNormal");
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
attacks
|
|
|
|
***********************************************************************/
|
|
|
|
float monster_hunter_invul::check_attacks() {
|
|
|
|
if(hunterState == INVUL_STATE_NORMAL) {
|
|
return check_attacks_normal();
|
|
} else if (hunterState == INVUL_STATE_INVUL) {
|
|
return check_attacks_invul();
|
|
} else if (hunterState == INVUL_STATE_INVUL_SURVIVAL) {
|
|
return check_attacks_invul_survival();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
float monster_hunter_invul::check_attacks_normal() {
|
|
|
|
//Check for damage so we know when it is time to jump out of normal mode
|
|
float health;
|
|
health = getHealth();
|
|
|
|
if(combatStage == 0) {
|
|
if(health <= STAGE_0_HEALTH) {
|
|
//sys.print("Moving To Stage 2\n");
|
|
setHealth(STAGE_0_HEALTH);
|
|
setDamageCap(STAGE_1_HEALTH);
|
|
combatStage = 1;
|
|
setState("state_Invulnerability");
|
|
}
|
|
}
|
|
if(combatStage == 1) {
|
|
if(health <= STAGE_1_HEALTH) {
|
|
//sys.print("Moving To Stage 3\n");
|
|
setHealth(STAGE_1_HEALTH);
|
|
setDamageCap(STAGE_2_HEALTH);
|
|
combatStage = 2;
|
|
setState("state_Invulnerability");
|
|
}
|
|
}
|
|
if(combatStage == 2) {
|
|
if(health <= STAGE_2_HEALTH) {
|
|
//sys.print("Moving To Final Stage\n");
|
|
setHealth(STAGE_2_HEALTH);
|
|
setDamageCap(-1); //Let him die
|
|
combatStage = 3;
|
|
setState("state_Invulnerability");
|
|
}
|
|
}
|
|
|
|
float attack_flags;
|
|
attack_flags = 0;
|
|
|
|
float currentTime;
|
|
currentTime = sys.getTime();
|
|
|
|
float canMelee;
|
|
canMelee = testMeleeAttack();
|
|
if ( canMelee ) {
|
|
attack_flags |= ATTACK_MELEE;
|
|
}
|
|
|
|
float playerHeight;
|
|
vector origin;
|
|
vector playerOrigin;
|
|
origin = getOrigin();
|
|
playerOrigin = $player1.getOrigin();
|
|
playerHeight = playerOrigin_z - origin_z;
|
|
|
|
//The player is below me so pound the ground just to scare hime
|
|
if(!AI_ENEMY_IN_FOV && currentTime >= nextAttack ) {
|
|
if(playerHeight < -10) {
|
|
attack_flags |= ATTACK_GROUNDPOUND;
|
|
}
|
|
}
|
|
|
|
if(AI_ENEMY_IN_FOV ) {
|
|
|
|
ClearAttacks();
|
|
|
|
if ( !canReachEnemy() || ( currentTime >= nextAttack ) ) {
|
|
|
|
string anim;
|
|
anim = chooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
|
|
if ( testAnimMoveTowardEnemy( anim ) ) {
|
|
if ( canHitEnemyFromAnim( anim ) ) {
|
|
range_attack_anim = anim;
|
|
|
|
PushAttack(ATTACK_MISSILE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(( currentTime >= nextAttack ) && playerHeight <= 16) {
|
|
//sys.print("player height = " + playerHeight + "\n");
|
|
PushAttack(ATTACK_GROUNDPOUND);
|
|
|
|
}
|
|
|
|
attack_flags |= SelectRandomAttack();
|
|
}
|
|
|
|
return attack_flags;
|
|
}
|
|
|
|
float monster_hunter_invul::check_attacks_invul() {
|
|
|
|
//Check if the level has told us it is time to end invuln mode
|
|
if(endInvul) {
|
|
endInvul = 0;
|
|
setState("state_EndInvul");
|
|
}
|
|
|
|
float attack_flags;
|
|
attack_flags = 0;
|
|
|
|
float currentTime;
|
|
currentTime = sys.getTime();
|
|
|
|
float canMelee;
|
|
canMelee = testMeleeAttack();
|
|
if ( canMelee ) {
|
|
attack_flags |= ATTACK_MELEE;
|
|
}
|
|
|
|
float playerHeight;
|
|
vector origin;
|
|
vector playerOrigin;
|
|
origin = getOrigin();
|
|
playerOrigin = $player1.getOrigin();
|
|
playerHeight = playerOrigin_z - origin_z;
|
|
|
|
//The player is below me so pound the ground just to scare hime
|
|
if(!AI_ENEMY_IN_FOV && currentTime >= nextAttack ) {
|
|
if(playerHeight < -10) {
|
|
//sys.print("player height = " + playerHeight + "\n");
|
|
attack_flags |= ATTACK_GROUNDPOUND;
|
|
}
|
|
}
|
|
|
|
if(AI_ENEMY_IN_FOV ) {
|
|
|
|
ClearAttacks();
|
|
|
|
if ( !canReachEnemy() || ( currentTime >= nextAttack ) ) {
|
|
|
|
string anim;
|
|
anim = chooseAnim( ANIMCHANNEL_LEGS, "range_attack" );
|
|
if ( testAnimMoveTowardEnemy( anim ) ) {
|
|
if ( canHitEnemyFromAnim( anim ) ) {
|
|
range_attack_anim = anim;
|
|
|
|
PushAttack(ATTACK_MISSILE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(( currentTime >= nextAttack ) && playerHeight <= 16) {
|
|
//sys.print("player height = " + playerHeight + "\n");
|
|
PushAttack(ATTACK_GROUNDPOUND);
|
|
|
|
}
|
|
|
|
attack_flags |= SelectRandomAttack();
|
|
}
|
|
|
|
return attack_flags;
|
|
}
|
|
|
|
float monster_hunter_invul::check_attacks_invul_survival() {
|
|
float attack_flags;
|
|
attack_flags = 0;
|
|
|
|
//Check if the level has told us it is time to end invuln mode
|
|
if(endInvul) {
|
|
endInvul = 0;
|
|
setState("state_EndInvulSurvival");
|
|
}
|
|
|
|
|
|
|
|
//If the player is dumb enough to get close while invulnerable then give em a world of hurt
|
|
float canMelee;
|
|
canMelee = testMeleeAttack();
|
|
if ( AI_ENEMY_IN_FOV ) {
|
|
if ( canMelee ) {
|
|
attack_flags |= ATTACK_MELEE;
|
|
}
|
|
}
|
|
|
|
float currentTime;
|
|
currentTime = sys.getTime();
|
|
|
|
if(currentTime >= nextAttack) {
|
|
|
|
ClearAttacks();
|
|
|
|
//Add attacks that are always available
|
|
PushAttack(ATTACK_MISSILE);
|
|
PushAttack(ATTACK_ELECTRIC_WALL);
|
|
|
|
//Add the groundpund attack if the player is near the ground level
|
|
float playerHeight;
|
|
vector origin;
|
|
vector playerOrigin;
|
|
origin = getOrigin();
|
|
playerOrigin = $player1.getOrigin();
|
|
playerHeight = playerOrigin_z - origin_z;
|
|
|
|
attackCount = 2;
|
|
if(playerHeight <= 16) {
|
|
PushAttack(ATTACK_GROUNDPOUND);
|
|
}
|
|
|
|
attack_flags |= SelectRandomAttack();
|
|
}
|
|
|
|
return attack_flags;
|
|
}
|
|
|
|
void monster_hunter_invul::do_attack( float attack_flags ) {
|
|
|
|
if ( attack_flags & ATTACK_MELEE ) {
|
|
combat_melee();
|
|
} else if ( attack_flags & ATTACK_MISSILE ) {
|
|
combat_range();
|
|
} else if ( attack_flags & ATTACK_GROUNDPOUND_NODAMAGE) {
|
|
combat_shockwave_nodamage();
|
|
} else if ( attack_flags & ATTACK_GROUNDPOUND) {
|
|
combat_shockwave();
|
|
} else if ( attack_flags & ATTACK_ELECTRIC_WALL) {
|
|
combat_electricwall();
|
|
}
|
|
|
|
if(hunterState == INVUL_STATE_NORMAL) {
|
|
nextAttack = DelayTime( INVUL_ATTACK_RATE_NORMAL );
|
|
} else if (hunterState == INVUL_STATE_INVUL) {
|
|
nextAttack = DelayTime( INVUL_ATTACK_RATE_INVUL );
|
|
} else if (hunterState == INVUL_STATE_INVUL_SURVIVAL) {
|
|
nextAttack = DelayTime( INVUL_ATTACK_RATE_INVUL_SURVIVAL );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
monster_hunter_invul::combat_range
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::combat_range() {
|
|
faceEnemy();
|
|
stopMove();
|
|
animState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
|
|
waitAction( "range_attack" );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
monster_hunter_invul::combat_melee
|
|
=====================
|
|
*/
|
|
void monster_hunter_invul::combat_melee() {
|
|
lookAt( getEnemy(), 100 );
|
|
faceEnemy();
|
|
animState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
|
|
waitAction( "melee_attack" );
|
|
lookAt( getEnemy(), 1 );
|
|
stopMove();
|
|
}
|
|
|
|
/*void monster_hunter_invul::combat_electroblast() {
|
|
|
|
|
|
entity ent;
|
|
vector loc;
|
|
ent = sys.getEntity("hunter_invul_location");
|
|
loc = ent.getOrigin();
|
|
slideTo(loc, 0);
|
|
allowMovement(false);
|
|
|
|
animState( ANIMCHANNEL_TORSO, "Torso_ElectroBlast", 5 );
|
|
waitAction( "electroblast" );
|
|
|
|
allowMovement(true);
|
|
|
|
}*/
|
|
|
|
void monster_hunter_invul::combat_shockwave() {
|
|
|
|
/*entity ent;
|
|
vector loc;
|
|
ent = sys.getEntity("hunter_invul_location");
|
|
loc = ent.getOrigin();
|
|
slideTo(loc, 0);*/
|
|
|
|
faceEnemy();
|
|
stopMove();
|
|
|
|
allowMovement(false);
|
|
|
|
animState( ANIMCHANNEL_TORSO, "Torso_Shockwave", 5 );
|
|
waitAction( "shockwave" );
|
|
|
|
allowMovement(true);
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::combat_electricwall() {
|
|
|
|
faceEnemy();
|
|
stopMove();
|
|
lookAtEnemy( 0 );
|
|
|
|
animState( ANIMCHANNEL_TORSO, "Torso_ElectricWallAttack", 4 );
|
|
waitAction( "electricwall_attack" );
|
|
}
|
|
|
|
void monster_hunter_invul::combat_shockwave_nodamage() {
|
|
|
|
/*entity ent;
|
|
vector loc;
|
|
ent = sys.getEntity("hunter_invul_location");
|
|
loc = ent.getOrigin();
|
|
slideTo(loc, 0);*/
|
|
|
|
faceEnemy();
|
|
stopMove();
|
|
|
|
allowMovement(false);
|
|
|
|
animState( ANIMCHANNEL_TORSO, "Torso_ShockwaveNoDamage", 5 );
|
|
waitAction( "shockwave" );
|
|
|
|
allowMovement(true);
|
|
}
|
|
|
|
void monster_hunter_invul::combat_invulrange_short() {
|
|
//sys.print("Range Attack Short\n");
|
|
allowMovement(false);
|
|
animState( ANIMCHANNEL_TORSO, "Torso_InvulRange_Short", 5 );
|
|
waitAction( "invulrange_short" );
|
|
allowMovement(true);
|
|
|
|
}
|
|
|
|
void monster_hunter_invul::combat_invulrange_tall() {
|
|
//sys.print("Range Attack Tall\n");
|
|
allowMovement(false);
|
|
animState( ANIMCHANNEL_TORSO, "Torso_InvulRange_Tall", 5 );
|
|
waitAction( "invulrange_tall" );
|
|
allowMovement(true);
|
|
}
|
|
|
|
|
|
void monster_hunter_invul::spawn_electric_wall( vector org ) {
|
|
entity ent;
|
|
vector ang;
|
|
|
|
ang_y = sys.random( 360 );
|
|
ent = sys.spawn( "invul_electricwall" );
|
|
ent.setKey( "cinematic_remove", "1" ); // make sure it gets removed in cinematics
|
|
ent.setShaderParm( 4, -sys.getTime() );
|
|
ent.setShaderParm( 5, sys.random( 1 ) );
|
|
ent.setOrigin( org );
|
|
ent.setAngles( ang );
|
|
ent.setOwner( self );
|
|
ent.disable();
|
|
sys.wait( 0.2 );
|
|
ent.enable();
|
|
sys.wait( 0.5 );
|
|
ent.disable();
|
|
sys.wait( 2.0 );
|
|
ent.remove();
|
|
}
|
|
|
|
void monster_hunter_invul::electric_wall_loop() {
|
|
float i;
|
|
vector dir;
|
|
vector ang;
|
|
vector pos;
|
|
float frac;
|
|
|
|
ang = getAngles();
|
|
dir = sys.angToForward( ang ) * ARCH_ELECTRIC_WALL_SEPERATION;
|
|
pos = getOrigin();
|
|
for( i = 0; i < ARCH_ELECTRIC_WALL_NUM; i++ ) {
|
|
pos_z += 48;
|
|
if ( sys.trace( pos, pos + dir, '-8 -8 0', '8 8 16', MASK_SOLID, self ) < 1 ) {
|
|
break;
|
|
}
|
|
pos = pos + dir;
|
|
frac = sys.trace( pos, pos - '0 0 96', '-8 -8 0', '8 8 16', MASK_SOLID, self );
|
|
if ( frac == 1 ) {
|
|
break;
|
|
}
|
|
pos_z -= 96 * frac;
|
|
thread spawn_electric_wall( pos );
|
|
sys.wait( 0.075 );
|
|
}
|
|
}
|
|
|
|
|
|
void monster_hunter_invul::electric_wall() {
|
|
thread electric_wall_loop();
|
|
}
|
|
|
|
#define EFFECT_DELAY 2
|
|
|
|
void monster_hunter_invul::electric_effect_loop() {
|
|
|
|
float currentTime;
|
|
|
|
float nextEffect;
|
|
nextEffect = 0;
|
|
|
|
/*while(1) {
|
|
if(hunterState == INVUL_STATE_INVUL_SURVIVAL || hunterState == INVUL_STATE_INVUL) {
|
|
currentTime = sys.getTime();
|
|
if(currentTime >= nextEffect) {
|
|
|
|
float selectedEffect;
|
|
entity ent;
|
|
|
|
selectedEffect = sys.randomInt(2);
|
|
|
|
//sys.print("Playing Effect " + selectedEffect + "\n");
|
|
if(selectedEffect == 0) {
|
|
sys.setSpawnArg( "fx", "fx/hunter/invulnerability/lightning_fx1" );
|
|
} else {
|
|
sys.setSpawnArg( "fx", "fx/hunter/invulnerability/lightning_fx2" );
|
|
}
|
|
|
|
sys.setSpawnArg( "start", "1" );
|
|
ent = sys.spawn( "func_fx" );
|
|
|
|
ent.setOrigin( getOrigin() );
|
|
|
|
nextEffect = DelayTime(EFFECT_DELAY);
|
|
}
|
|
}
|
|
waitFrame();
|
|
}*/
|
|
} |