/*********************************************************************** ai_monster_zombie_commando_tentacle.script monster_zombie_commando_tentacle ***********************************************************************/ #define ZCT_RUNDISTANCE 128 #define ZCT_WALKTURN 65 #define ZCT_ATTACK_RATE 5 #define ZCT_TENTACLE_RANGE_MAX 200 #define ZCT_NOFOVTIME 4 #define ATTACK_TENTACLE ATTACK_SPECIAL1 object monster_zombie_commando_tentacle : monster_base { float nextAttack; float nextNoFOVAttack; boolean tentacleDamage; // // States // void state_Begin(); void state_Idle(); // attacks float check_attacks(); void do_attack( float attack_flags ); void combat_tentacle(); void combat_melee(); void init(); // torso anim states void Torso_Idle(); void Torso_Pain(); void Torso_MeleeAttack(); void Torso_TentacleAttack(); // legs anim states void Legs_Idle(); void Legs_Walk(); void Legs_Run(); }; /*********************************************************************** Torso animation control ***********************************************************************/ void monster_zombie_commando_tentacle::Torso_Idle() { idleAnim( ANIMCHANNEL_TORSO, "idle" ); eachFrame { if ( AI_PAIN ) { animState( ANIMCHANNEL_TORSO, "Torso_Pain", 0 ); } } } void monster_zombie_commando_tentacle::Torso_Pain() { string animname; float nextpain; float currenttime; animname = getPainAnim(); playAnim( ANIMCHANNEL_TORSO, animname ); 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_zombie_commando_tentacle::Torso_MeleeAttack() { playAnim( ANIMCHANNEL_TORSO, "melee_attack" ); while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) { waitFrame(); } finishAction( "melee_attack" ); animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); } void monster_zombie_commando_tentacle::Torso_TentacleAttack() { disablePain(); faceEnemy(); playAnim( ANIMCHANNEL_TORSO, "range_attack" ); while( !animDone( ANIMCHANNEL_TORSO, 4 ) ) { waitFrame(); } finishAction( "tentacle_attack" ); animState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); } /*********************************************************************** Legs animation control ***********************************************************************/ void monster_zombie_commando_tentacle::Legs_Idle() { idleAnim( ANIMCHANNEL_LEGS, "idle" ); eachFrame { if ( run && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); } if ( AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); } } } void monster_zombie_commando_tentacle::Legs_Walk() { playCycle( ANIMCHANNEL_LEGS, "walk" ); eachFrame { if ( run && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Run", 4 ); } if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); } } } void monster_zombie_commando_tentacle::Legs_Run() { playCycle( ANIMCHANNEL_LEGS, "run" ); eachFrame { if ( !run && AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Walk", 4 ); } if ( !AI_FORWARD ) { animState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); } } } /*********************************************************************** AI ***********************************************************************/ /* ===================== monster_zombie_commando_tentacle::init ===================== */ void monster_zombie_commando_tentacle::init() { run_distance = ZCT_RUNDISTANCE; walk_turn = ZCT_WALKTURN; setState( "state_Begin" ); } /* ===================== monster_zombie_commando_tentacle::state_Begin ===================== */ void monster_zombie_commando_tentacle::state_Begin() { animState( ANIMCHANNEL_TORSO, "Torso_Idle", 0 ); animState( ANIMCHANNEL_LEGS, "Legs_Idle", 0 ); monster_begin(); setMoveType( MOVETYPE_ANIM ); setState( "state_Idle" ); } /* ===================== monster_zombie_commando_tentacle::state_Idle ===================== */ void monster_zombie_commando_tentacle::state_Idle() { wait_for_enemy(); tentacleDamage = false; nextAttack = 0; nextNoFOVAttack = 0; setState( "state_Combat" ); } /*********************************************************************** attacks ***********************************************************************/ /* ===================== monster_zombie_commando_tentacle::do_attack ===================== */ void monster_zombie_commando_tentacle::do_attack( float attack_flags ) { nextNoFOVAttack = sys.getTime() + ZCT_NOFOVTIME; if ( attack_flags & ATTACK_MELEE ) { combat_melee(); } else if ( attack_flags & ATTACK_TENTACLE ) { combat_tentacle(); } } /* ===================== monster_zombie_commando_tentacle::check_attacks ===================== */ float monster_zombie_commando_tentacle::check_attacks() { float range; float currentTime; float attack_flags; attack_flags = 0; if ( testMeleeAttack() ) { attack_flags |= ATTACK_MELEE; } if ( ( ( sys.getTime() > nextNoFOVAttack ) && AI_ENEMY_VISIBLE ) || AI_ENEMY_IN_FOV ) { range = enemyRange(); currentTime = sys.getTime(); if ( ( range < ZCT_TENTACLE_RANGE_MAX ) && ( !canReachEnemy() || ( currentTime >= nextAttack ) ) ) { if ( canHitEnemy() ) { attack_flags |= ATTACK_TENTACLE; } } } return attack_flags; } /* ===================== monster_zombie_commando_tentacle::tentacle_attack_start ===================== */ void monster_zombie_commando_tentacle::tentacle_attack_start() { tentacleDamage = true; } /* ===================== monster_zombie_commando_tentacle::tentacle_attack_end ===================== */ void monster_zombie_commando_tentacle::tentacle_attack_end() { tentacleDamage = false; } /*********************************************************************** Combat ***********************************************************************/ /* ===================== monster_zombie_commando_tentacle::combat_tentacle ===================== */ void monster_zombie_commando_tentacle::combat_tentacle() { tentacleDamage = false; stopMove(); animState( ANIMCHANNEL_TORSO, "Torso_TentacleAttack", 4 ); while( inAnimState( ANIMCHANNEL_TORSO, "Torso_TentacleAttack" ) ) { if ( tentacleDamage ) { if ( meleeAttackToJoint( "joint13", "melee_commandoTentacle" ) ) { tentacleDamage = false; } } waitFrame(); } tentacleDamage = false; // don't attack for a bit nextAttack = DelayTime( ZCT_ATTACK_RATE ); nextNoFOVAttack = sys.getTime() + ZCT_NOFOVTIME; } /* ===================== monster_zombie_commando_tentacle::combat_melee ===================== */ void monster_zombie_commando_tentacle::combat_melee() { tentacleDamage = false; faceEnemy(); animState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack", 5 ); while( inAnimState( ANIMCHANNEL_TORSO, "Torso_MeleeAttack" ) ) { lookAtEnemy( 1 ); if ( tentacleDamage ) { if ( meleeAttackToJoint( "joint13", "melee_commandoTentacle" ) ) { tentacleDamage = false; } } waitFrame(); } tentacleDamage = false; }