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

1807 lines
No EOL
39 KiB
Text

/***********************************************************************
ai_monster_hunter_berserk.script
monster_hunter_berserk
***********************************************************************/
//Hunter States
#define BERSERK_STATE_WAIT 0
#define BERSERK_STATE_BERSERK 1
#define BERSERK_STATE_NORMAL 2
#define BERSERK_STATE_BERSERK_START 3
//Movement States
#define HUNTER_MOVE_WALK 0
#define HUNTER_MOVE_RUN 1
#define HUNTER_MOVE_CHARGE 2
//Leap Variables
#define BERSERK_LEAP_RANGE_MIN 50
#define BERSERK_LEAP_RANGE_MAX 10000
#define BERSERK_LEAP_RATE 1
#define BERSERK_LEAP_SPEED 900
#define BERSERK_LEAP_MAXHEIGHT 1000
#define MAX_BERSERK_DAMAGE 2000
#define BERSERK_RANGE_ATTACK_RATE 1
#define BERSERK_DAMAGE_MULTIPLIER_HELLTIME 30
#define BERSERK_DAMAGE_MULTIPLIER_NORMAL 0
#define HUNTER_BERSERK_HOP_RATE 2
#define HUNTER_BERSERK_IDEAL_RANGE 256
#define HUNTER_EVADE_RANGE 200
#define HUNTER_EVADE_RATE 1
#define HUNTER_BERSERK_DURATION 15
//1/5/2005: Decrease normal time
//#define HUNTER_NORMAL_DURATION 15
#define HUNTER_NORMAL_DURATION 10
#define HUNTER_CHARGE_SPEED 440
#define HUNTER_CHARGE_RUNPAST_DIST 100
#define CHARGE_CONTINUE_ANGLE 10
#define ATTACK_EVADE_LEFT 1
#define ATTACK_EVADE_RIGHT 2
#define ATTACK_EVADE_BACK 4
#define ATTACK_EVADE_BACK_LONG 8
#define ATTACK_EVADE_BACK_LEFT 16
#define ATTACK_EVADE_BACK_RIGHT 32
#define ATTACK_EVADE_FRONT 64
#define ATTACK_EVADE_FRONT_LEFT 128
#define ATTACK_EVADE_FRONT_RIGHT 256
/***********************************************************************
monster_hunter_berserk
***********************************************************************/
object monster_hunter_berserk : monster_base {
float nextEvade;
float nextDodge;
float nextAttack;
float nextLeap;
float nextHop;
vector jumpVelocity;
float hunterState;
float stateEndTime;
float moveState;
vector chargeTarget;
string range_attack_anim;
float leavingBerserkDamage;
float hitEnemy;
float minBerserkHealth;
//Hack Array
float array0;
float array1;
float array2;
float array3;
float array4;
float array5;
float array6;
float array7;
float array8;
float GetArrayVal(float index);
void SetArrayVal(float index, float val);
void SetHeartDamage(float damage);
//
// States
//
void state_Begin();
void state_Idle();
void state_Dead();
//berserk States
void state_BeginBerserk();
void state_Berserk();
void state_EndBerserk();
void state_BerserkPain();
//Normal States
void state_BeginNormal();
void state_EndNormal();
//Combat States
void state_Combat();
// attacks
float check_attacks();
void do_attack( float attack_flags );
float combat_berserk();
float combat_normal();
void combat_range();
void combat_leap();
void combat_melee();
void combat_melee_berserk();
void path_jump();
void combat_evade_left();
void combat_evade_right();
void combat_evade_back();
void combat_evade_back_long();
void combat_evade_back_left();
void combat_evade_back_right();
void combat_evade_front();
void combat_evade_front_left();
void combat_evade_front_right();
void combat_berserk_charge();
boolean combat_chase_berserk();
float combat_evade();
float combat_hop();
void checkDamage();
void init();
void destroy();
//A couple of skin changing functions used by the intro cinematic
void setSkinToBerserk();
void setSkinToBerserkCinematic();
void setSkinBerserk();
void setSkinToNormal();
void setSkinNormal();
// torso anim states
void Torso_Idle();
void Torso_Pain();
void Torso_Death();
void Torso_MeleeAttack();
void Torso_RangeAttack();
void Torso_LeapAttack();
void Torso_BerserkBegin();
void Torso_BerserkEnd();
void Torso_BerserkRage();
void Torso_BerserkCharge();
void Torso_BerserkPain();
// legs anim states
void Legs_Idle();
void Legs_Walk();
void Legs_Run();
void Legs_Charge();
void Legs_EvadeLeft();
void Legs_EvadeRight();
void Legs_EvadeBack();
void Legs_EvadeBackLong();
void Legs_EvadeBackLeft();
void Legs_EvadeBackRight();
void Legs_EvadeFront();
void Legs_EvadeFrontLeft();
void Legs_EvadeFrontRight();
void Legs_Slide();
};
float monster_hunter_berserk::GetArrayVal(float index) {
if (index == 0) {
return array0;
} else if (index == 1) {
return array1;
} else if (index == 2) {
return array2;
} else if (index == 3) {
return array3;
} else if (index == 4) {
return array4;
} else if (index == 5) {
return array5;
} else if (index == 6) {
return array6;
} else if (index == 7) {
return array7;
} else if (index == 8) {
return array8;
}
}
void monster_hunter_berserk::SetArrayVal(float index, float val) {
if (index == 0) {
array0 = val;
} else if (index == 1) {
array1 = val;
} else if (index == 2) {
array2 = val;
} else if (index == 3) {
array3 = val;
} else if (index == 4) {
array4 = val;
} else if (index == 5) {
array5 = val;
} else if (index == 6) {
array6 = val;
} else if (index == 7) {
array7 = val;
} else if (index == 8) {
array8 = val;
}
}
/***********************************************************************
Torso animation control
***********************************************************************/
void monster_hunter_berserk::Torso_Idle() {
idleAnim( ANIMCHANNEL_TORSO, "idle" );
eachFrame {
if ( AI_PAIN ) {
animState( ANIMCHANNEL_TORSO, "Torso_Pain", 3 );
}
waitFrame();
}
}
void monster_hunter_berserk::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_berserk::Torso_Death() {
//sys.print("Torso Death!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
if (hunterState == BERSERK_STATE_NORMAL) {
//sys.print("Summon\n");
setSkinToBerserk();
playAnim( ANIMCHANNEL_TORSO, "berzerk_summon_death" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
} else {
//sys.print("Already Berserk\n");
playAnim( ANIMCHANNEL_TORSO, "berzerk_death" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
}
finishAction( "dead" );
}
void monster_hunter_berserk::Torso_MeleeAttack() {
if (hunterState == BERSERK_STATE_BERSERK) {
playAnim( ANIMCHANNEL_TORSO, "berzerk_hit" );
} else {
playAnim( ANIMCHANNEL_TORSO, "melee" );
}
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "melee_attack" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
}
void monster_hunter_berserk::Torso_RangeAttack() {
string anim;
disablePain();
allowMovement( false );
playAnim( ANIMCHANNEL_TORSO, "ranged" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
lookAt( getEnemy(), 1 );
waitFrame();
}
allowMovement( true );
enablePain();
finishAction( "range_attack" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
}
void monster_hunter_berserk::Torso_LeapAttack() {
overrideAnim( ANIMCHANNEL_LEGS );
disablePain();
playAnim( ANIMCHANNEL_TORSO, "berzerk_leap_start" );
while( !animDone( ANIMCHANNEL_TORSO, 2 ) ) {
if(leavingBerserkDamage) {
leavingBerserkDamage = false;
setState("state_BerserkPain");
return;
}
waitFrame();
}
setBlendFrames( ANIMCHANNEL_TORSO, 2 );
attackBegin( "melee_hunter_berserk_ChargeAttack" );
setLinearVelocity( jumpVelocity );
float changedLoop;
changedLoop = false;
playCycle( ANIMCHANNEL_TORSO, "berzerk_leap_loop" );
do {
if(leavingBerserkDamage) {
if(!changedLoop) {
setBlendFrames( ANIMCHANNEL_TORSO, 4 );
playCycle( ANIMCHANNEL_TORSO, "berzerk_leap_loop_pain" );
changedLoop = true;
}
attackEnd();
}
waitFrame();
} while( !AI_ONGROUND );
attackEnd();
if(leavingBerserkDamage) {
//sys.print("Playing pain landing\n");
playAnim( ANIMCHANNEL_TORSO, "berzerk_leap_pain" );
} else {
if(hitEnemy) {
hitEnemy = false;
//sys.print("playing hit landing\n");
playAnim( ANIMCHANNEL_TORSO, "berzerk_leap_hit" );
} else {
//sys.print("playing miss landing\n");
playAnim( ANIMCHANNEL_TORSO, "berzerk_leap_miss" );
}
}
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "leap_attack" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 );
}
void monster_hunter_berserk::Torso_BerserkBegin() {
playAnim( ANIMCHANNEL_TORSO, "berzerk_summon" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "berserkbegin" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
}
void monster_hunter_berserk::Torso_BerserkEnd() {
playAnim( ANIMCHANNEL_TORSO, "berzerk_2_norm" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "berserkend" );
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 6 );
}
void monster_hunter_berserk::Torso_BerserkRage() {
finishAction( "beginrage" );
playAnim( ANIMCHANNEL_TORSO, "berzerk_rage" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "berserkrage" );
}
void monster_hunter_berserk::Torso_BerserkCharge() {
idleAnim( ANIMCHANNEL_TORSO, "idle" );
eachFrame {
waitFrame();
}
}
void monster_hunter_berserk::Torso_BerserkPain() {
playAnim( ANIMCHANNEL_TORSO, "berzerk_pain" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
waitFrame();
}
finishAction( "berserkpain" );
}
/***********************************************************************
Legs animation control
***********************************************************************/
void monster_hunter_berserk::Legs_Idle() {
idleAnim( ANIMCHANNEL_LEGS, "idle" );
eachFrame {
if ( moveState == HUNTER_MOVE_CHARGE && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Charge", 4 ); }
if ( moveState == HUNTER_MOVE_RUN && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); }
if ( moveState == HUNTER_MOVE_WALK && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); }
}
}
void monster_hunter_berserk::Legs_Walk() {
playCycle( ANIMCHANNEL_LEGS, "walk" );
eachFrame {
if ( moveState == HUNTER_MOVE_CHARGE && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Charge", 4 ); }
if ( moveState == HUNTER_MOVE_RUN && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); }
if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); }
}
}
void monster_hunter_berserk::Legs_Run() {
playCycle( ANIMCHANNEL_LEGS, "berzerk_run" );
eachFrame {
if ( moveState == HUNTER_MOVE_CHARGE && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Charge", 4 ); }
if ( moveState == HUNTER_MOVE_WALK && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); }
if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); }
}
}
void monster_hunter_berserk::Legs_Charge() {
playCycle( ANIMCHANNEL_LEGS, "berzerk_charge" );
eachFrame {
if ( moveState == HUNTER_MOVE_RUN && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); }
if ( moveState == HUNTER_MOVE_WALK && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); }
if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); }
}
}
void monster_hunter_berserk::Legs_EvadeLeft() {
playAnim( ANIMCHANNEL_LEGS, "evade_left" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeRight() {
playAnim( ANIMCHANNEL_LEGS, "evade_right" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeBack() {
playAnim( ANIMCHANNEL_LEGS, "evade_back" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeBackLong() {
playAnim( ANIMCHANNEL_LEGS, "evade_back_192" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeBackLeft() {
playAnim( ANIMCHANNEL_LEGS, "evade_backleft" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeBackRight() {
playAnim( ANIMCHANNEL_LEGS, "evade_backright" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeFront() {
playAnim( ANIMCHANNEL_LEGS, "evade_front" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeFrontLeft() {
playAnim( ANIMCHANNEL_LEGS, "evade_frontleft" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_EvadeFrontRight() {
playAnim( ANIMCHANNEL_LEGS, "evade_frontright" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "evade" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
void monster_hunter_berserk::Legs_Slide() {
playAnim( ANIMCHANNEL_LEGS, "berzerk_miss" );
while( !animDone( ANIMCHANNEL_LEGS, 4 ) ) {
waitFrame();
}
finishAction( "legslide" );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 );
}
/***********************************************************************
AI
***********************************************************************/
/*
=====================
monster_hunter_berserk::init
=====================
*/
void monster_hunter_berserk::init() {
nextHop = 0;
moveState = HUNTER_MOVE_RUN;
nextLeap = 0;
nextAttack = 0;
leavingBerserkDamage = false;
setState( "state_Begin" );
}
void monster_hunter_berserk::destroy() {
}
void monster_hunter_berserk::setSkinToBerserk() {
setSkin("skins/monsters/hunter_berserk/hunter_berserk_toberserk");
setShaderTime( self );
}
void monster_hunter_berserk::setSkinToBerserkCinematic() {
setSkin("skins/monsters/hunter_berserk/hunter_berserk_cinematic");
setShaderTime( self );
}
void monster_hunter_berserk::setSkinBerserk() {
setSkin("skins/monsters/hunter_berserk/hunter_berserk_enrage");
}
void monster_hunter_berserk::setSkinToNormal() {
setSkin("skins/monsters/hunter_berserk/hunter_berserk_tonormal");
setShaderTime( self );
}
void monster_hunter_berserk::setSkinNormal() {
setSkin("skins/monsters/hunter_berserk/hunter_berserk");
}
/***********************************************************************
States
***********************************************************************/
/*
=====================
monster_hunter_berserk::state_Begin
=====================
*/
void monster_hunter_berserk::state_Begin() {
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 );
animState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 );
monster_begin();
setState( "state_Idle" );
}
/*
=====================
monster_hunter_berserk::state_Idle
=====================
*/
void monster_hunter_berserk::state_Idle() {
hunterState = BERSERK_STATE_WAIT;
wait_for_enemy();
nextAttack = 0;
nextEvade = RandomTime( HUNTER_EVADE_RATE );
nextDodge = 0;
//Bouncy Monster - Tests the random evade selection
/*while(1) {
combat_evade();
waitFrame();
}*/
setState( "state_Berserk" );
}
/*
=====================
monster_base::state_Dead
=====================
*/
void monster_hunter_berserk::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_BerserkPain", 8 );
waitAction( "berserkpain" );
remove();
}
//berserk States
void monster_hunter_berserk::state_BeginBerserk() {
hunterState = BERSERK_STATE_BERSERK_START;
setSkinToBerserk();
//Play an animation when he goes into berserk mode
animState( ANIMCHANNEL_TORSO, "Torso_BerserkBegin", 4 );
setWaitState("berserkbegin");
while(getWaitState() == "berserkbegin") {
checkDamage();
waitFrame();
}
setState("state_Berserk");
}
void monster_hunter_berserk::state_Berserk() {
hunterState = BERSERK_STATE_BERSERK;
stateEndTime = sys.getTime() + HUNTER_BERSERK_DURATION;
minBerserkHealth = getHealth() - MAX_BERSERK_DAMAGE;
//sys.print("Min Berserk Health " + minBerserkHealth + "\n");
//Hunter is invulnerable while in berserk
//Use damage group scales to set invulnerability
setDamageGroupScaleAll(0);
SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_HELLTIME);
startEmitter("heartemitter", "heart1", "hunter_bezerk_heart_burn.prt");
avoidObstacles(0);
setSkinBerserk();
setState( "state_Combat" );
}
void monster_hunter_berserk::state_EndBerserk() {
setSkinToNormal();
//1/5/2005: The hunter is now invulnerable in normal mode
//setDamageGroupScaleAll(1);
setDamageGroupScaleAll(0);
SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_NORMAL);
stopEmitter("heartemitter");
animState( ANIMCHANNEL_TORSO, "Torso_BerserkEnd", 4 );
waitAction( "berserkend" );
avoidObstacles(1);
setSkinNormal();
setState( "state_BeginNormal");
}
void monster_hunter_berserk::state_BerserkPain() {
if(AI_FORWARD) {
//He was moving forward so blend in a slide animation to the legs
animState( ANIMCHANNEL_LEGS, "Legs_Slide", 4 );
}
animState( ANIMCHANNEL_TORSO, "Torso_BerserkPain", 4 );
waitAction( "berserkpain" );
stopMove();
setState( "state_EndBerserk");
}
//Normal States
void monster_hunter_berserk::state_BeginNormal() {
hunterState = BERSERK_STATE_NORMAL;
stateEndTime = sys.getTime() + HUNTER_NORMAL_DURATION;
//The end of berserk will play any appropriate animations
stopMove();
//Turn on damage while in normal mode
//Turn damage back on using the damage scale
setDamageGroupScaleAll(0);
SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_NORMAL);
stopEmitter("heartemitter");
setState( "state_Combat" );
}
void monster_hunter_berserk::state_EndNormal() {
//The beginning of berserk will play the animations
setState( "state_BeginBerserk");
}
/*
=====================
monster_base::state_Combat
=====================
*/
void monster_hunter_berserk::state_Combat() {
float attack_flags;
float currentTime;
eachFrame {
currentTime = sys.getTime();
//Check for damage group settings and shader settings
checkDamage();
if(leavingBerserkDamage) {
leavingBerserkDamage = false;
setState( "state_EndBerserk" );
}
//Are we ready to switch out of our current state
if(currentTime > stateEndTime) {
if (hunterState == BERSERK_STATE_BERSERK) {
setState( "state_EndBerserk" );
} else if (hunterState == BERSERK_STATE_NORMAL) {
setState( "state_EndNormal" );
}
}
///////////////////////////////////////////////////////////////////
//This section is the same as the state_combat in the monster_base
faceEnemy();
if ( AI_ENEMY_IN_FOV ) {
lookAtEnemy( 1 );
}
if ( sys.influenceActive() ) {
waitFrame();
continue;
}
if ( AI_ENEMY_DEAD ) {
enemy_dead();
}
///////////////////////////////////////////////////////////////////
if (hunterState == BERSERK_STATE_BERSERK) {
if(combat_berserk()) {
continue;
}
} else if (hunterState == BERSERK_STATE_NORMAL) {
// Do some hopping around occasionally
if ( currentTime >= nextHop ) {
if(combat_hop()) {
waitFrame();
}
nextHop = DelayTime( HUNTER_BERSERK_HOP_RATE );
}
if(combat_normal()) {
continue;
}
}
/*if ( !enemyPositionValid() ) {
locateEnemy();
}*/
waitFrame();
}
}
/***********************************************************************
attacks
***********************************************************************/
/*
=====================
monster_hunter_berserk::combat_berserk
berserk combat is a single sequnce of actions than are not interrupted
- If in melee range then melee attack
- Otherwise charge
=====================
*/
float monster_hunter_berserk::combat_berserk() {
/*float canMelee;
faceEnemy();
canMelee = testMeleeAttack();
if ( AI_ENEMY_IN_FOV ) {
if ( canMelee ) {
combat_melee();
return 1;
}
combat_berserk_charge();
}
return 0;*/
float attack_flags;
attack_flags = check_attacks();
if ( attack_flags ) {
do_attack( attack_flags );
return 1;
}
if ( !combat_chase_berserk() ) {
locateEnemy();
if ( !combat_chase_berserk() ) {
combat_lost();
}
}
return 0;
}
/*
=====================
monster_hunter_berserk::combat_chase_berserk
=====================
*/
boolean monster_hunter_berserk::combat_chase_berserk() {
float delta;
boolean do_run;
float range;
float attack_flags;
moveToEnemy();
if ( AI_MOVE_DONE ) {
return false;
}
waitFrame();
if ( AI_MOVE_DONE ) {
attack_flags = check_attacks();
if ( attack_flags ) {
do_attack( attack_flags );
return true;
}
return false;
}
while( !AI_MOVE_DONE && !AI_DEST_UNREACHABLE ) {
checkDamage();
if(leavingBerserkDamage) {
//Go immediately into a berserk pain state
leavingBerserkDamage = false;
setState("state_BerserkPain");
}
if ( AI_ENEMY_DEAD ) {
enemy_dead();
}
if ( sys.influenceActive() ) {
return true;
}
if ( AI_ENEMY_IN_FOV ) {
lookAtEnemy( 1 );
}
attack_flags = check_attacks();
if ( attack_flags ) {
do_attack( attack_flags );
return true;
}
if ( check_blocked() ) {
return true;
}
waitFrame();
}
return true;
}
/***********************************************************************
attacks
***********************************************************************/
/*
=====================
monster_hunter_berserk::do_attack
=====================
*/
void monster_hunter_berserk::do_attack( float attack_flags ) {
/*if ( attack_flags & ATTACK_DODGE_LEFT ) {
combat_dodge_left();
} else if ( attack_flags & ATTACK_DODGE_RIGHT ) {
combat_dodge_right();
} else*/ if ( attack_flags & ATTACK_MELEE ) {
if(hunterState == BERSERK_STATE_NORMAL) {
combat_melee();
} else {
combat_melee_berserk();
}
} else if ( attack_flags & ATTACK_LEAP ) {
combat_leap();
} else if ( attack_flags & ATTACK_MISSILE ) {
combat_range();
}
}
/*
=====================
monster_hunter_berserk::check_attacks
=====================
*/
float monster_hunter_berserk::check_attacks() {
float range;
float currentTime;
float canMelee;
float attack_flags;
float checkLeap;
vector vel;
float t;
string anim;
vector jumpTarget;
attack_flags = 0;
canMelee = testMeleeAttack();
currentTime = sys.getTime();
if ( !canMelee ) {
//Check for a dodge attack...He won't dodge in berserk mode
if ( hunterState == BERSERK_STATE_NORMAL && AI_PAIN && ( currentTime >= nextDodge ) ) {
if ( testAnimMove( "evade_left" ) ) {
attack_flags |= ATTACK_DODGE_LEFT;
}
if ( testAnimMove( "evade_right" ) ) {
attack_flags |= ATTACK_DODGE_RIGHT;
// if we can dodge either direction, pick one
if ( attack_flags & ATTACK_DODGE_LEFT ) {
if ( sys.random( 100 ) < 50 ) {
attack_flags &= ~ATTACK_DODGE_RIGHT;
} else {
attack_flags &= ~ATTACK_DODGE_LEFT;
}
}
}
}
}
if ( canMelee ) {
attack_flags |= ATTACK_MELEE;
}
if ( AI_ENEMY_IN_FOV ) {
range = enemyRange();
//Only leap in berserk mode
if ( hunterState == BERSERK_STATE_BERSERK && ( range >= BERSERK_LEAP_RANGE_MIN ) && ( range < BERSERK_LEAP_RANGE_MAX ) && ( currentTime >= nextLeap ) ) {
if ( canHitEnemy() ) {
t = animLength( ANIMCHANNEL_TORSO, "berzerk_leap_start" );
jumpTarget = predictEnemyPos( t );
jumpVelocity = getJumpVelocity( jumpTarget, BERSERK_LEAP_SPEED, BERSERK_LEAP_MAXHEIGHT );
if ( jumpVelocity != '0 0 0' ) {
attack_flags |= ATTACK_LEAP;
} else {
nextLeap = DelayTime( BERSERK_LEAP_RATE );
}
}
}
//Only missle attack while in normal mode
if ( hunterState == BERSERK_STATE_NORMAL && (!canReachEnemy() || ( currentTime >= nextAttack )) ) {
anim = chooseAnim( ANIMCHANNEL_TORSO, "ranged" );
if ( testAnimMoveTowardEnemy( anim ) ) {
if ( canHitEnemyFromAnim( anim ) ) {
range_attack_anim = anim;
attack_flags |= ATTACK_MISSILE;
}
}
}
}
return attack_flags;
}
/*
=====================
monster_hunter_berserk::combat_normal
=====================
*/
float monster_hunter_berserk::combat_normal() {
float currentTime;
float range;
float canMelee;
currentTime = sys.getTime();
canMelee = testMeleeAttack();
if ( AI_ENEMY_IN_FOV ) {
if ( canMelee ) {
combat_melee();
return 1;
}
}
range = enemyRange();
if(range <= HUNTER_EVADE_RANGE) {
//The player is too close...I'm sooooo scared
if(combat_evade() == 1) {
return 1;
}
}
if ( currentTime >= nextAttack ) {
if ( canHitEnemy() ) {
combat_range();
return 1;
}
}
return 0;
}
float monster_hunter_berserk::combat_evade() {
float selection;
float selectedEvade;
float evadeCount;
selectedEvade = 0;
evadeCount = 0;
//Which directions can we evade
if ( testAnimMove( "evade_left" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_LEFT);
evadeCount++;
}
if ( testAnimMove( "evade_right" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_RIGHT);
evadeCount++;
}
if ( testAnimMove( "evade_back" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_BACK);
evadeCount++;
}
//if ( testAnimMove( "evade_back_192" ) ) {
//SetArrayVal(evadeCount, ATTACK_EVADE_BACK_LONG);
//evadeCount++;
//}
if ( testAnimMove( "evade_backleft" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_BACK_LEFT);
evadeCount++;
}
if ( testAnimMove( "evade_backright" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_BACK_RIGHT);
evadeCount++;
}
// Check front evades as a last resort
if ( evadeCount == 0 ) {
if ( testAnimMove( "evade_frontleft" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_FRONT_LEFT);
evadeCount++;
}
if ( testAnimMove( "evade_frontright" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_FRONT_RIGHT);
evadeCount++;
}
}
if(evadeCount > 0) {
selection = sys.randomInt( evadeCount );
selectedEvade = GetArrayVal(selection);
if(selectedEvade == ATTACK_EVADE_LEFT) {
combat_evade_left();
return 1;
}
if(selectedEvade == ATTACK_EVADE_RIGHT) {
combat_evade_right();
return 1;
}
if(selectedEvade == ATTACK_EVADE_BACK) {
combat_evade_back();
return 1;
}
//if(selectedEvade == ATTACK_EVADE_BACK_LONG) {
//combat_evade_back_long();
//return 1;
//}
if(selectedEvade == ATTACK_EVADE_BACK_LEFT) {
combat_evade_back_left();
return 1;
}
if(selectedEvade == ATTACK_EVADE_BACK_RIGHT) {
combat_evade_back_right();
return 1;
}
if(selectedEvade == ATTACK_EVADE_FRONT_LEFT) {
combat_evade_front_left();
return 1;
}
if(selectedEvade == ATTACK_EVADE_FRONT_RIGHT) {
combat_evade_front_right();
return 1;
}
}
return 0;
}
/*
=====================
monster_hunter_berserk::combat_hop
=====================
*/
float monster_hunter_berserk::combat_hop() {
float selection;
float selectedEvade;
float evadeCount;
float range;
selectedEvade = 0;
evadeCount = 0;
// Always allow a side hop
if ( testAnimMove( "evade_left" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_LEFT);
evadeCount++;
}
if ( testAnimMove( "evade_right" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_RIGHT);
evadeCount++;
}
// We should try and keep a steady range from the enemy
range = enemyRange();
if ( range > HUNTER_BERSERK_IDEAL_RANGE ) {
if ( testAnimMove( "evade_front" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_FRONT);
evadeCount++;
}
if ( testAnimMove( "evade_frontleft" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_FRONT_LEFT);
evadeCount++;
}
if ( testAnimMove( "evade_frontright" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_FRONT_RIGHT);
evadeCount++;
}
} else {
if ( testAnimMove( "evade_back" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_BACK);
evadeCount++;
}
if ( testAnimMove( "evade_backleft" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_BACK_LEFT);
evadeCount++;
}
if ( testAnimMove( "evade_backright" ) ) {
SetArrayVal(evadeCount, ATTACK_EVADE_BACK_RIGHT);
evadeCount++;
}
}
if(evadeCount > 0) {
selection = sys.randomInt( evadeCount );
selectedEvade = GetArrayVal(selection);
if(selectedEvade == ATTACK_EVADE_LEFT) {
combat_evade_left();
return 1;
}
if(selectedEvade == ATTACK_EVADE_RIGHT) {
combat_evade_right();
return 1;
}
if(selectedEvade == ATTACK_EVADE_FRONT) {
combat_evade_front();
return 1;
}
if(selectedEvade == ATTACK_EVADE_FRONT_LEFT) {
combat_evade_front_left();
return 1;
}
if(selectedEvade == ATTACK_EVADE_FRONT_RIGHT) {
combat_evade_front_right();
return 1;
}
if(selectedEvade == ATTACK_EVADE_BACK) {
combat_evade_back();
return 1;
}
if(selectedEvade == ATTACK_EVADE_BACK_LEFT) {
combat_evade_back_left();
return 1;
}
if(selectedEvade == ATTACK_EVADE_BACK_RIGHT) {
combat_evade_back_right();
return 1;
}
}
return 0;
}
/*
=====================
monster_hunter_berserk::combat_range
=====================
*/
void monster_hunter_berserk::combat_range() {
faceEnemy();
// stopMove();
animState( ANIMCHANNEL_TORSO, "Torso_RangeAttack", 4 );
waitAction( "range_attack" );
// don't attack for a bit
nextAttack = DelayTime( BERSERK_RANGE_ATTACK_RATE );
}
/*
=====================
monster_hunter_berserk::combat_leap
=====================
*/
void monster_hunter_berserk::combat_leap() {
stopMove();
turnToPos( getOrigin() + jumpVelocity );
animState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
//While he is leaping check for damage to we know the player has knocked him out of berserk mode
setWaitState("leap_attack");
while(getWaitState() == "leap_attack") {
//Check for a hit so the torso will know which land animation to play
if(AI_HIT_ENEMY) {
hitEnemy = true;
}
checkDamage();
waitFrame();
}
}
/*
=====================
monster_hunter_berserk::combat_berserk_charge
=====================
*/
void monster_hunter_berserk::combat_berserk_charge() {
vector chargeBegin;
vector dir;
float dist;
float endtime;
float animD;
float animT;
entity curEnemy;
disablePain();
moveState = HUNTER_MOVE_CHARGE;
avoidObstacles(0);
float canMelee;
animState( ANIMCHANNEL_TORSO, "Torso_BerserkRage", 4);
waitAction("beginrage");
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
checkDamage();
//While raging make sure the player is not to close
faceEnemy();
canMelee = testMeleeAttack();
if ( canMelee ) {
combat_melee();
return;
}
waitFrame();
}
attackBegin( "melee_hunter_berserk_ChargeAttack" );
animState( ANIMCHANNEL_TORSO, "Torso_BerserkCharge", 4);
curEnemy = getEnemy();
stopMove();
while(1) {
//sys.print("Beginning Charge...\n");
turnToPos( curEnemy.getOrigin() );
chargeBegin = getOrigin();
chargeTarget = curEnemy.getOrigin();
dir = chargeTarget - chargeBegin;
moveToPosition(chargeTarget);
animD = animDistance( ANIMCHANNEL_LEGS, "berzerk_charge" );
animT = animLength( ANIMCHANNEL_LEGS, "berzerk_charge" );
dist = sys.vecLength(dir) + 256;
endtime = sys.getTime() + (dist / animD * animT);
while( !AI_HIT_ENEMY && !AI_MOVE_DONE && !AI_BLOCKED && ( sys.getTime() < endtime ) ) {
//while( !AI_HIT_ENEMY && !AI_MOVE_DONE && !AI_BLOCKED) {
kickObstacles(getObstacle(), 200);
checkDamage();
waitFrame();
}
if(!AI_HIT_ENEMY) {
//sys.print("Missed the enemy\n");
//Check to see if the enemy is still right in front of me
vector newDir;
float ang;
newDir = curEnemy.getOrigin() - chargeBegin;
ang = sys.DotProduct(sys.vecNormalize(dir), sys.vecNormalize(newDir));
ang = sys.acos(ang);
if(ang > CHARGE_CONTINUE_ANGLE || ang < -CHARGE_CONTINUE_ANGLE) {
//sys.print("Sliding...\n");
attackEnd();
//The player got out of the way so lets get pissed and end the charge
playAnim( ANIMCHANNEL_TORSO, "berzerk_miss" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
checkDamage();
waitFrame();
}
break;
}
if ( AI_BLOCKED ) {
faceEnemy();
waitFrame();
break;
}
} else {
//sys.print("Hit the enemy\n");
//We hit the player...do a quick melee attack
playAnim( ANIMCHANNEL_TORSO, "berzerk_hit" );
while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) {
checkDamage();
waitFrame();
}
stopMove();
attackEnd();
break;
}
waitFrame();
}
avoidObstacles(1);
moveState = HUNTER_MOVE_RUN;
enablePain();
animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4);
}
/*
=====================
monster_hunter_berserk::combat_melee
=====================
*/
void monster_hunter_berserk::combat_melee() {
lookAt( getEnemy(), 100 );
faceEnemy();
animState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
waitAction( "melee_attack" );
lookAt( getEnemy(), 1 );
stopMove();
combat_evade();
}
/*
=====================
monster_hunter_berserk::combat_melee_berserk
=====================
*/
void monster_hunter_berserk::combat_melee_berserk() {
lookAt( getEnemy(), 100 );
faceEnemy();
animState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 );
setWaitState("melee_attack");
while(getWaitState() == "melee_attack") {
checkDamage();
if(leavingBerserkDamage) {
//Go immediately into a berserk pain state
leavingBerserkDamage = false;
setState("state_BerserkPain");
}
waitFrame();
}
lookAt( getEnemy(), 1 );
stopMove();
}
/*
=====================
monster_hunter_berserk::combat_evade_left
=====================
*/
void monster_hunter_berserk::combat_evade_left() {
//sys.print("Evade Left\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeLeft", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_right
=====================
*/
void monster_hunter_berserk::combat_evade_right() {
//sys.print("Evade Right\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeRight", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_back
=====================
*/
void monster_hunter_berserk::combat_evade_back() {
//sys.print("Evade Back\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeBack", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_back_long
=====================
*/
void monster_hunter_berserk::combat_evade_back_long() {
//sys.print("Evade Back Long\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeBackLong", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_back_left
=====================
*/
void monster_hunter_berserk::combat_evade_back_left() {
//sys.print("Evade Back Left\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeBackLeft", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_back_right
=====================
*/
void monster_hunter_berserk::combat_evade_back_right() {
//sys.print("Evade Back Right\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeBackRight", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_front
=====================
*/
void monster_hunter_berserk::combat_evade_front() {
//sys.print("Evade Front\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeFront", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_front_left
=====================
*/
void monster_hunter_berserk::combat_evade_front_left() {
//sys.print("Evade Front Left\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeFrontLeft", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::combat_evade_front_right
=====================
*/
void monster_hunter_berserk::combat_evade_front_right() {
//sys.print("Evade Front Right\n");
faceEnemy();
stopMove();
animState( ANIMCHANNEL_LEGS, "Legs_EvadeFrontRight", 2 );
waitAction( "evade" );
nextEvade = DelayTime( HUNTER_EVADE_RATE );
}
/*
=====================
monster_hunter_berserk::path_jump
=====================
*/
void monster_hunter_berserk::path_jump() {
entity target;
vector dir;
vector jumpTarget;
// walk to the path entity first
path_corner();
if ( checkForEnemy( true ) ) {
return;
}
target = current_path.randomTarget( current_path.getName() );
if ( !target ) {
sys.error( "missing target for '" + current_path.getName() + "'" );
}
jumpTarget = target.getOrigin();
if ( !current_path.getKey( "up" ) ) {
jumpVelocity = getJumpVelocity( jumpTarget, BERSERK_LEAP_SPEED, 1024 );
if ( jumpVelocity == '0 0 0' ) {
sys.error( "Monster '" + getName() + "' couldn't make jump from '" + current_path.getName() + "' to '" + target.getName() + "'" );
}
} else {
float forward = current_path.getFloatKey( "forward" );
if ( forward <= 0 ) {
sys.error( "Invalid forward velocity on path_jump entity '" + current_path.getName() + "'\n" );
}
dir = jumpTarget - getOrigin();
dir_z = 0;
dir = sys.vecNormalize( dir );
dir = dir * forward;
dir_z = current_path.getFloatKey( "up" );
jumpVelocity = dir;
}
stopMove();
turnToPos( jumpTarget );
while( !facingIdeal() ) {
if ( checkForEnemy( true ) ) {
return;
}
waitFrame();
}
animState( ANIMCHANNEL_TORSO, "Torso_LeapAttack", 4 );
waitAction( "leap_attack" );
stopMove();
}
void monster_hunter_berserk::checkDamage() {
if (hunterState == BERSERK_STATE_BERSERK || hunterState == BERSERK_STATE_BERSERK_START) {
//Helltime Damage will knock the hunter out of berserk mode
//Need to check the scale to make sure the code has executed and the player is able to do the serious damage
//before knocking the monster out of berserk
float scale;
scale = getDamageGroupScale("heart");
if(hunterState == BERSERK_STATE_BERSERK && AI_DAMAGE && $player1.isPowerupActive(HELLTIME) && scale == BERSERK_DAMAGE_MULTIPLIER_HELLTIME) {
AI_DAMAGE = false;
if(!leavingBerserkDamage) {
//sys.print("!!!!!!!!!!!!!!!!!!!! Damage While In Berserk !!!!!!!!!!!!!!!!!\n");
leavingBerserkDamage = true;
//SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_NORMAL);
}
}
/*if($player1.isPowerupActive(HELLTIME)) {
//if(!leavingBerserkDamage) {
//You can only damage him while he is in berserk when you are in hell time
if(hunterState == BERSERK_STATE_BERSERK) {
SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_HELLTIME);
}
startEmitter("heartemitter", "heart1", "hunter_bezerk_heart_burn.prt");
//}
} else {
SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_NORMAL);
stopEmitter("heartemitter");
}*/
} /*else {
SetHeartDamage(BERSERK_DAMAGE_MULTIPLIER_NORMAL);
stopEmitter("heartemitter");
}*/
if(AI_DAMAGE) {
AI_DAMAGE = false;
}
}
void monster_hunter_berserk::SetHeartDamage(float damage) {
//sys.print("Heart Damage Scale = " + damage + "\n");
setDamageGroupScale("heart", damage);
if(damage == BERSERK_DAMAGE_MULTIPLIER_HELLTIME) {
setDamageCap(minBerserkHealth);
} else {
setDamageCap(-1);
}
}