842 lines
37 KiB
Plaintext
842 lines
37 KiB
Plaintext
/*==============================================================================
|
|
KNIGHT
|
|
==============================================================================*/
|
|
$cd id1/models/knight2
|
|
$origin 0 0 24
|
|
$base base
|
|
$skin skin
|
|
|
|
// 000-008
|
|
$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9
|
|
|
|
// 009-028
|
|
$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9
|
|
$frame walk10 walk11 walk12 walk13 walk14 walk15 walk16 walk17
|
|
$frame walk18 walk19 walk20
|
|
|
|
// 029-036
|
|
$frame run1 run2 run3 run4 run5 run6 run7 run8
|
|
|
|
// 037-041
|
|
$frame pain1 pain2 pain3 pain4 pain5
|
|
|
|
// 042-053
|
|
$frame death1 death2 death3 death4 death5 death6 death7 death8
|
|
$frame death9 death10 death11 death12
|
|
|
|
// 054-062
|
|
$frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8
|
|
$frame deathb9
|
|
|
|
// 063-078
|
|
$frame char_a1 char_a2 char_a3 char_a4 char_a5 char_a6 char_a7 char_a8
|
|
$frame char_a9 char_a10 char_a11 char_a12 char_a13 char_a14 char_a15 char_a16
|
|
|
|
// 079-092
|
|
$frame magica1 magica2 magica3 magica4 magica5 magica6 magica7 magica8
|
|
$frame magica9 magica10 magica11 magica12 magica13 magica14
|
|
|
|
// 093-105
|
|
$frame magicb1 magicb2 magicb3 magicb4 magicb5 magicb6 magicb7 magicb8
|
|
$frame magicb9 magicb10 magicb11 magicb12 magicb13
|
|
|
|
// 106-111
|
|
$frame char_b1 char_b2 char_b3 char_b4 char_b5 char_b6
|
|
|
|
// 112-121
|
|
$frame slice1 slice2 slice3 slice4 slice5 slice6 slice7 slice8 slice9 slice10
|
|
|
|
// 122-132
|
|
$frame smash1 smash2 smash3 smash4 smash5 smash6 smash7 smash8 smash9 smash10
|
|
$frame smash11
|
|
|
|
// 133-154
|
|
$frame w_attack1 w_attack2 w_attack3 w_attack4 w_attack5 w_attack6 w_attack7
|
|
$frame w_attack8 w_attack9 w_attack10 w_attack11 w_attack12 w_attack13 w_attack14
|
|
$frame w_attack15 w_attack16 w_attack17 w_attack18 w_attack19 w_attack20
|
|
$frame w_attack21 w_attack22
|
|
|
|
// 155-165
|
|
$frame magicc1 magicc2 magicc3 magicc4 magicc5 magicc6 magicc7 magicc8
|
|
$frame magicc9 magicc10 magicc11
|
|
|
|
void() hknight_sword_sound;
|
|
void() knight_statue;
|
|
|
|
//===========================================================================
|
|
void() hknight_stand1 =[ $stand1, hknight_stand2 ] {monster_idle_sound(); ai_stand();};
|
|
void() hknight_stand2 =[ $stand2, hknight_stand3 ] {ai_stand();};
|
|
void() hknight_stand3 =[ $stand3, hknight_stand4 ] {ai_stand();};
|
|
void() hknight_stand4 =[ $stand4, hknight_stand5 ] {ai_stand();};
|
|
void() hknight_stand5 =[ $stand5, hknight_stand6 ] {ai_stand();};
|
|
void() hknight_stand6 =[ $stand6, hknight_stand7 ] {ai_stand();};
|
|
void() hknight_stand7 =[ $stand7, hknight_stand8 ] {ai_stand();};
|
|
void() hknight_stand8 =[ $stand8, hknight_stand9 ] {ai_stand();};
|
|
void() hknight_stand9 =[ $stand9, hknight_stand1 ] {ai_stand();};
|
|
|
|
//===========================================================================
|
|
void() hknight_walk1 =[ $walk1, hknight_walk2 ] {monster_idle_sound(); ai_walk(2);};
|
|
void() hknight_walk2 =[ $walk2, hknight_walk3 ] {monster_footstep(FALSE);ai_walk(5);};
|
|
void() hknight_walk3 =[ $walk3, hknight_walk4 ] {ai_walk(5);};
|
|
void() hknight_walk4 =[ $walk4, hknight_walk5 ] {ai_walk(4);};
|
|
void() hknight_walk5 =[ $walk5, hknight_walk6 ] {ai_walk(4);};
|
|
void() hknight_walk6 =[ $walk6, hknight_walk7 ] {ai_walk(2);};
|
|
void() hknight_walk7 =[ $walk7, hknight_walk8 ] {ai_walk(2);};
|
|
void() hknight_walk8 =[ $walk8, hknight_walk9 ] {ai_walk(3);};
|
|
void() hknight_walk9 =[ $walk9, hknight_walk10 ] {ai_walk(3);};
|
|
void() hknight_walk10 =[ $walk10, hknight_walk11 ] {ai_walk(4);};
|
|
void() hknight_walk11 =[ $walk11, hknight_walk12 ] {monster_footstep(FALSE); ai_walk(3);};
|
|
void() hknight_walk12 =[ $walk12, hknight_walk13 ] {ai_walk(4);};
|
|
void() hknight_walk13 =[ $walk13, hknight_walk14 ] {ai_walk(6);};
|
|
void() hknight_walk14 =[ $walk14, hknight_walk15 ] {ai_walk(2);};
|
|
void() hknight_walk15 =[ $walk15, hknight_walk16 ] {ai_walk(2);};
|
|
void() hknight_walk16 =[ $walk16, hknight_walk17 ] {ai_walk(4);};
|
|
void() hknight_walk17 =[ $walk17, hknight_walk18 ] {ai_walk(3);};
|
|
void() hknight_walk18 =[ $walk18, hknight_walk19 ] {ai_walk(3);};
|
|
void() hknight_walk19 =[ $walk19, hknight_walk20 ] {ai_walk(3);};
|
|
void() hknight_walk20 =[ $walk20, hknight_walk1 ] {ai_walk(2);};
|
|
|
|
//===========================================================================
|
|
void() CheckForCharge =
|
|
{
|
|
// There are 4 conditions to keep charging
|
|
// Can see player, no attack timer, similar Z axis and outside melee range
|
|
// Nightmare skill level can lock HK in constant charging (no attack timer)
|
|
enemy_vis = visible(self.enemy);
|
|
if (!enemy_vis) return;
|
|
if (time < self.attack_finished) return;
|
|
if (self.movespeed < 0) return;
|
|
if (self.tetherlock) return;
|
|
// Only charge at player if ground height < 20
|
|
if ( fabs(self.origin_z - self.enemy.origin_z) > MONAI_CHARGEZAXIS) return;
|
|
if (ai_checkmelee(MONAI_MELEEKNIGHT)) return;
|
|
|
|
SUB_AttackFinished (2);
|
|
|
|
// Used a pre-defined charge routine (th_charge) so that
|
|
// hell and death knights can share the same function
|
|
if (self.th_charge) self.th_charge();
|
|
else self.th_run();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_run1 =[ $run1, hknight_run2 ] {monster_idle_sound();
|
|
ai_run (20); CheckForCharge(); };
|
|
void() hknight_run2 =[ $run2, hknight_run3 ] {monster_footstep(FALSE); ai_run(25);};
|
|
void() hknight_run3 =[ $run3, hknight_run4 ] {ai_run(18);};
|
|
void() hknight_run4 =[ $run4, hknight_run5 ] {ai_run(16);};
|
|
void() hknight_run5 =[ $run5, hknight_run6 ] {ai_run(14);};
|
|
void() hknight_run6 =[ $run6, hknight_run7 ] {monster_footstep(FALSE); ai_run(25);};
|
|
void() hknight_run7 =[ $run7, hknight_run8 ] {ai_run(21);};
|
|
void() hknight_run8 =[ $run8, hknight_run1 ] {ai_run(13);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_char_a1 =[ $char_a1, hknight_char_a2 ] {ai_charge(20);};
|
|
void() hknight_char_a2 =[ $char_a2, hknight_char_a3 ] {monster_footstep(FALSE); ai_charge(25);};
|
|
void() hknight_char_a3 =[ $char_a3, hknight_char_a4 ] {ai_charge(18);};
|
|
void() hknight_char_a4 =[ $char_a4, hknight_char_a5 ] {ai_charge(16);hknight_sword_sound();};
|
|
void() hknight_char_a5 =[ $char_a5, hknight_char_a6 ] {ai_charge(14);};
|
|
void() hknight_char_a6 =[ $char_a6, hknight_char_a7 ] {monster_footstep(FALSE); ai_charge(20); ai_melee();};
|
|
void() hknight_char_a7 =[ $char_a7, hknight_char_a8 ] {ai_charge(21); ai_melee();};
|
|
void() hknight_char_a8 =[ $char_a8, hknight_char_a9 ] {ai_charge(13); ai_melee();};
|
|
void() hknight_char_a9 =[ $char_a9, hknight_char_a10 ] {ai_charge(20); ai_melee();};
|
|
void() hknight_char_a10=[ $char_a10, hknight_char_a11 ] {monster_footstep(FALSE); ai_charge(20); ai_melee();};
|
|
void() hknight_char_a11=[ $char_a11, hknight_char_a12 ] {ai_charge(18); ai_melee();};
|
|
void() hknight_char_a12=[ $char_a12, hknight_char_a13 ] {ai_charge(16);};
|
|
void() hknight_char_a13=[ $char_a13, hknight_char_a14 ] {ai_charge(14);};
|
|
void() hknight_char_a14=[ $char_a14, hknight_char_a15 ] {monster_footstep(FALSE);ai_charge(25);};
|
|
void() hknight_char_a15=[ $char_a15, hknight_char_a16 ] {ai_charge(21);};
|
|
void() hknight_char_a16=[ $char_a16, hknight_run1 ] {ai_charge(13);};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Never used, only called from hknight_char_b
|
|
//----------------------------------------------------------------------
|
|
void() CheckContinueCharge =
|
|
{
|
|
if (time > self.attack_finished) {
|
|
SUB_AttackFinished (3);
|
|
self.th_run();
|
|
return; // done charging
|
|
}
|
|
};
|
|
|
|
void() hknight_char_b1 =[ $char_b1, hknight_char_b2 ]
|
|
{CheckContinueCharge (); ai_charge(23); ai_melee();};
|
|
void() hknight_char_b2 =[ $char_b2, hknight_char_b3 ] {hknight_sword_sound(); ai_charge(17); ai_melee();};
|
|
void() hknight_char_b3 =[ $char_b3, hknight_char_b4 ] {ai_charge(12); ai_melee();};
|
|
void() hknight_char_b4 =[ $char_b4, hknight_char_b5 ] {monster_footstep(FALSE); ai_charge(22); ai_melee();};
|
|
void() hknight_char_b5 =[ $char_b5, hknight_char_b6 ] {ai_charge(18); ai_melee();};
|
|
void() hknight_char_b6 =[ $char_b6, hknight_char_b1 ] {ai_charge(8); ai_melee();};
|
|
|
|
//===========================================================================
|
|
void() hknight_slice1 =[ $slice1, hknight_slice2 ] {ai_charge(9);};
|
|
void() hknight_slice2 =[ $slice2, hknight_slice3 ] {ai_charge(6);};
|
|
void() hknight_slice3 =[ $slice3, hknight_slice4 ] {monster_footstep(FALSE); ai_charge(13);};
|
|
void() hknight_slice4 =[ $slice4, hknight_slice5 ] {hknight_sword_sound(); ai_charge(4);};
|
|
void() hknight_slice5 =[ $slice5, hknight_slice6 ] {ai_charge(7); ai_melee();};
|
|
void() hknight_slice6 =[ $slice6, hknight_slice7 ] {ai_charge(15); ai_melee();};
|
|
void() hknight_slice7 =[ $slice7, hknight_slice8 ] {monster_footstep(FALSE); ai_charge(8); ai_melee();};
|
|
void() hknight_slice8 =[ $slice8, hknight_slice9 ] {ai_charge(2); ai_melee();};
|
|
void() hknight_slice9 =[ $slice9, hknight_slice10 ] {ai_melee();};
|
|
void() hknight_slice10 =[ $slice10, hknight_run1 ] {ai_charge(3);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_smash1 =[ $smash1, hknight_smash2 ] {ai_charge(1);};
|
|
void() hknight_smash2 =[ $smash2, hknight_smash3 ] {ai_charge(13);};
|
|
void() hknight_smash3 =[ $smash3, hknight_smash4 ] {hknight_sword_sound(); ai_charge(9);};
|
|
void() hknight_smash4 =[ $smash4, hknight_smash5 ] {monster_footstep(FALSE); ai_charge(11);};
|
|
void() hknight_smash5 =[ $smash5, hknight_smash6 ] {ai_charge(10); ai_melee();};
|
|
void() hknight_smash6 =[ $smash6, hknight_smash7 ] {ai_charge(7); ai_melee();};
|
|
void() hknight_smash7 =[ $smash7, hknight_smash8 ] {ai_charge(12); ai_melee();};
|
|
void() hknight_smash8 =[ $smash8, hknight_smash9 ] {ai_charge(2); ai_melee();};
|
|
void() hknight_smash9 =[ $smash9, hknight_smash10 ] {monster_footstep(FALSE); ai_charge(3); ai_melee();};
|
|
void() hknight_smash10 =[ $smash10, hknight_smash11 ] {ai_charge(0);};
|
|
void() hknight_smash11 =[ $smash11, hknight_run1 ] {ai_charge(0);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_watk1 =[ $w_attack1, hknight_watk2 ] {ai_charge(2);};
|
|
void() hknight_watk2 =[ $w_attack2, hknight_watk3 ] {ai_charge(0);};
|
|
void() hknight_watk3 =[ $w_attack3, hknight_watk4 ] {ai_charge(0);};
|
|
void() hknight_watk4 =[ $w_attack4, hknight_watk5 ] {hknight_sword_sound(); ai_melee();};
|
|
void() hknight_watk5 =[ $w_attack5, hknight_watk6 ] {ai_melee();};
|
|
void() hknight_watk6 =[ $w_attack6, hknight_watk7 ] {ai_melee();};
|
|
void() hknight_watk7 =[ $w_attack7, hknight_watk8 ] {ai_charge(1);};
|
|
void() hknight_watk8 =[ $w_attack8, hknight_watk9 ] {ai_charge(4);};
|
|
void() hknight_watk9 =[ $w_attack9, hknight_watk10 ] {ai_charge(5);};
|
|
void() hknight_watk10 =[ $w_attack10, hknight_watk11 ] {ai_charge(3); ai_melee();};
|
|
void() hknight_watk11 =[ $w_attack11, hknight_watk12 ] {monster_footstep(FALSE); ai_charge(2); ai_melee();};
|
|
void() hknight_watk12 =[ $w_attack12, hknight_watk13 ] {ai_charge(2); ai_melee();};
|
|
void() hknight_watk13 =[ $w_attack13, hknight_watk14 ] {ai_charge(0);};
|
|
void() hknight_watk14 =[ $w_attack14, hknight_watk15 ] {hknight_sword_sound(); ai_charge(0);};
|
|
void() hknight_watk15 =[ $w_attack15, hknight_watk16 ] {ai_charge(0);};
|
|
void() hknight_watk16 =[ $w_attack16, hknight_watk17 ] {ai_charge(1);};
|
|
void() hknight_watk17 =[ $w_attack17, hknight_watk18 ] {ai_charge(1); ai_melee();};
|
|
void() hknight_watk18 =[ $w_attack18, hknight_watk19 ] {ai_charge(3); ai_melee();};
|
|
void() hknight_watk19 =[ $w_attack19, hknight_watk20 ] {ai_charge(4); ai_melee();};
|
|
void() hknight_watk20 =[ $w_attack20, hknight_watk21 ] {ai_charge(6);};
|
|
void() hknight_watk21 =[ $w_attack21, hknight_watk22 ] {ai_charge(7);};
|
|
void() hknight_watk22 =[ $w_attack22, hknight_run1 ] {monster_footstep(FALSE); ai_charge(3);};
|
|
|
|
//============================================================================
|
|
// Cycle between 3 melee attacks
|
|
// Attack 1 - Slice (one hand slashing run attack)
|
|
// Attack 2 - Smash (chop overhead to ground)
|
|
// Attack 3 - W_Attack (two handed slashing attack)
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_melee =
|
|
{
|
|
self.meleeattack = self.meleeattack + 1;
|
|
|
|
if (self.meleeattack == 1) hknight_slice1 ();
|
|
else if (self.meleeattack == 2) hknight_smash1 ();
|
|
else {
|
|
hknight_watk1 ();
|
|
self.meleeattack = 0;
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
void(float offset) hknight_shot =
|
|
{
|
|
local vector offang;
|
|
local vector org, dir;
|
|
|
|
if (!self.enemy) return;
|
|
if (self.health < 1) return;
|
|
|
|
sound (self, CHAN_WEAPON, "hknight/attack1.wav", 1, ATTN_NORM);
|
|
|
|
self.attack_speed = SPEED_HKSPIKE + ( skill * SPEED_HKSKILL);
|
|
offang = vectoangles (self.enemy.origin - self.origin);
|
|
offang_y = offang_y + offset * 6;
|
|
makevectors (offang);
|
|
|
|
org = self.origin + self.mins + self.size*0.5 + v_forward * 20;
|
|
dir = normalize (v_forward);
|
|
dir_z = 0 - dir_z + (random() - 0.5)*0.1;
|
|
|
|
launch_projectile (org, dir, CT_PROJ_HKN, self.attack_speed);
|
|
};
|
|
//----------------------------------------------------------------------
|
|
// Magic attack A (over head attack) - not used
|
|
void() hknight_magica1 =[ $magica1, hknight_magica2 ] {ai_face();};
|
|
void() hknight_magica2 =[ $magica2, hknight_magica3 ] {ai_face();};
|
|
void() hknight_magica3 =[ $magica3, hknight_magica4 ] {ai_face();};
|
|
void() hknight_magica4 =[ $magica4, hknight_magica5 ] {ai_face();};
|
|
void() hknight_magica5 =[ $magica5, hknight_magica6 ] {ai_face();};
|
|
void() hknight_magica6 =[ $magica6, hknight_magica7 ] {ai_face();};
|
|
void() hknight_magica7 =[ $magica7, hknight_magica8 ] {hknight_shot(-2);};
|
|
void() hknight_magica8 =[ $magica8, hknight_magica9 ] {hknight_shot(-1);};
|
|
void() hknight_magica9 =[ $magica9, hknight_magica10] {hknight_shot(0);};
|
|
void() hknight_magica10 =[ $magica10, hknight_magica11] {hknight_shot(1);};
|
|
void() hknight_magica11 =[ $magica11, hknight_magica12] {hknight_shot(2);};
|
|
void() hknight_magica12 =[ $magica12, hknight_magica13] {hknight_shot(3);};
|
|
void() hknight_magica13 =[ $magica13, hknight_magica14] {ai_face();};
|
|
void() hknight_magica14 =[ $magica14, hknight_run1 ] {ai_face();};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Magic attack B (pointing sword) - not used
|
|
void() hknight_magicb1 =[ $magicb1, hknight_magicb2 ] {ai_face();};
|
|
void() hknight_magicb2 =[ $magicb2, hknight_magicb3 ] {ai_face();};
|
|
void() hknight_magicb3 =[ $magicb3, hknight_magicb4 ] {ai_face();};
|
|
void() hknight_magicb4 =[ $magicb4, hknight_magicb5 ] {ai_face();};
|
|
void() hknight_magicb5 =[ $magicb5, hknight_magicb6 ] {ai_face();};
|
|
void() hknight_magicb6 =[ $magicb6, hknight_magicb7 ] {ai_face();};
|
|
void() hknight_magicb7 =[ $magicb7, hknight_magicb8 ] {hknight_shot(-2);};
|
|
void() hknight_magicb8 =[ $magicb8, hknight_magicb9 ] {hknight_shot(-1);};
|
|
void() hknight_magicb9 =[ $magicb9, hknight_magicb10] {hknight_shot(0);};
|
|
void() hknight_magicb10 =[ $magicb10, hknight_magicb11] {hknight_shot(1);};
|
|
void() hknight_magicb11 =[ $magicb11, hknight_magicb12] {hknight_shot(2);};
|
|
void() hknight_magicb12 =[ $magicb12, hknight_magicb13] {hknight_shot(3);};
|
|
void() hknight_magicb13 =[ $magicb13, hknight_run1] {ai_face();};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Magic attack C (swing sword in arc)
|
|
void() hknight_magicc1 =[ $magicc1, hknight_magicc2 ] {ai_face();};
|
|
void() hknight_magicc2 =[ $magicc2, hknight_magicc3 ] {ai_face();};
|
|
void() hknight_magicc3 =[ $magicc3, hknight_magicc4 ] {ai_face();};
|
|
void() hknight_magicc4 =[ $magicc4, hknight_magicc5 ] {ai_face();};
|
|
void() hknight_magicc5 =[ $magicc5, hknight_magicc6 ] {ai_face();};
|
|
void() hknight_magicc6 =[ $magicc6, hknight_magicc7 ] {hknight_shot(-2);};
|
|
void() hknight_magicc7 =[ $magicc7, hknight_magicc8 ] {hknight_shot(-1);};
|
|
void() hknight_magicc8 =[ $magicc8, hknight_magicc9 ] {hknight_shot(0);};
|
|
void() hknight_magicc9 =[ $magicc9, hknight_magicc10] {hknight_shot(1);};
|
|
void() hknight_magicc10 =[ $magicc10, hknight_magicc11] {hknight_shot(2);};
|
|
void() hknight_magicc11 =[ $magicc11, hknight_run1] {hknight_shot(3);};
|
|
|
|
//============================================================================
|
|
// Special setup - Hell Knight firing electricity at target
|
|
//----------------------------------------------------------------------
|
|
void() hknight_create_attachment =
|
|
{
|
|
// Are the attachments setup yet?
|
|
if (!self.attachment) {
|
|
self.attachment = spawn();
|
|
self.attachment.owner = self;
|
|
self.attachment.classtype = CT_ATTACHMENT;
|
|
self.attachment.mdl = self.weaponglow;
|
|
setmodel(self.attachment, self.attachment.mdl);
|
|
setsize (self.attachment, VEC_ORIGIN, VEC_ORIGIN);
|
|
self.attachment.movetype = MOVETYPE_NONE;
|
|
self.attachment.solid = SOLID_NOT;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_remove_attachment =
|
|
{
|
|
if (self.attachment) {
|
|
self.attachment.think = SUB_Remove;
|
|
self.attachment.nextthink = time + 0.1;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_pointfire =
|
|
{
|
|
local vector hkp_start, hkp_rand, hkp_end;
|
|
|
|
// Make sure the knight is alive
|
|
if (self.health < 1) return;
|
|
|
|
// Time for a new LG hit sound?
|
|
if (self.t_width < time) {
|
|
// Lower volume and attenuation just in case of several together
|
|
sound (self, CHAN_WEAPON, "weapons/lhit.wav", 0.75, ATTN_IDLE);
|
|
self.t_width = time + 0.6;
|
|
}
|
|
|
|
// Update animation frames (default = MagicB6-11)
|
|
self.frame = self.frame + self.pos3_z;
|
|
if (self.frame < self.pos3_x) self.frame = self.pos3_y;
|
|
if (self.frame > self.pos3_y) self.frame = self.pos3_x;
|
|
|
|
// Turn the Knight towards lightning target
|
|
self.ideal_yaw = vectoyaw(self.movetarget.origin - self.origin);
|
|
ChangeYaw ();
|
|
// Work out start point of lightning bolt
|
|
makevectors(self.angles);
|
|
|
|
// Work out starting point for lightning on sword
|
|
hkp_start = v_forward*self.pos1_x + v_right*self.pos1_y + v_up*self.pos1_z;
|
|
hkp_rand = v_forward * (self.pos2_x * random());
|
|
hkp_rand = hkp_rand + v_right * (self.pos2_y * random());
|
|
hkp_rand = hkp_rand + v_up * (self.pos2_z * random());
|
|
hkp_start = hkp_start + self.origin + hkp_rand;
|
|
|
|
// Check for any random target origin variance
|
|
if (self.t_length > 0) {
|
|
hkp_end = vecrand(0,self.t_length,1);
|
|
hkp_end = hkp_end + self.movetarget.origin;
|
|
}
|
|
// Default (0) = exact target position
|
|
else hkp_end = self.movetarget.origin;
|
|
|
|
// Update sword with lightning visuals
|
|
if (self.attachment) {
|
|
setorigin(self.attachment, self.origin);
|
|
self.attachment.frame = self.frame;
|
|
self.attachment.angles = self.angles;
|
|
// Allow for no sword effect by going over max and reset
|
|
self.attachment.alpha = self.attachment.alpha + random();
|
|
if (self.attachment.alpha > 1) self.attachment.alpha = 0.01;
|
|
// The four skins are randomly setup already
|
|
self.lip = self.attachment.skin + 1;
|
|
if (self.lip > 3) self.lip = 0;
|
|
self.attachment.skin = self.lip;
|
|
// Draw paticles on sword where lightning starts
|
|
particle_explode(hkp_start, 2, 1, PARTICLE_BURST_BLUE, PARTICLE_BURST_LOSTUP);
|
|
}
|
|
|
|
// Create lightning bolt
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_LIGHTNING2);
|
|
WriteEntity (MSG_BROADCAST, self);
|
|
WriteCoord (MSG_BROADCAST, hkp_start_x);
|
|
WriteCoord (MSG_BROADCAST, hkp_start_y);
|
|
WriteCoord (MSG_BROADCAST, hkp_start_z);
|
|
WriteCoord (MSG_BROADCAST, hkp_end_x);
|
|
WriteCoord (MSG_BROADCAST, hkp_end_y);
|
|
WriteCoord (MSG_BROADCAST, hkp_end_z);
|
|
|
|
// Can the lightning damage things?
|
|
if (self.dmg > 0)
|
|
LightningDamage (hkp_start, hkp_end, self, self.dmg);
|
|
|
|
// Keep Looping
|
|
self.think = self.th_run;
|
|
self.nextthink = time + 0.1;
|
|
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_pointsetup =
|
|
{
|
|
// Find Lightning bolt target
|
|
self.movetarget = find(world, targetname, self.target);
|
|
if (!self.movetarget) {
|
|
dprint("\b[HKNIGHT]\b Pointing knight invalid target!\n");
|
|
spawn_marker(self.origin, SPNMARK_YELLOW);
|
|
remove(self);
|
|
return;
|
|
}
|
|
// Start pointing lightning sword!
|
|
else {
|
|
// Save pain threshold and timeout for later
|
|
self.finaldest_x = self.pain_flinch;
|
|
self.finaldest_y = self.pain_timeout;
|
|
// Reset all pain parameters so it always happens
|
|
self.pain_flinch = self.pain_timeout = 0;
|
|
self.pain_finished = self.pain_ignore = 0;
|
|
self.infightextra = self.noinfighting = 0;
|
|
// Stop other monsters attacking pointy knights
|
|
self.flags = self.flags - (self.flags & FL_MONSTER);
|
|
self.enemy = world;
|
|
// Create glowing sword and start pointing
|
|
hknight_create_attachment();
|
|
self.th_run();
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
void() hknight_pain1 =[ $pain1, hknight_pain2 ] {};
|
|
void() hknight_pain2 =[ $pain2, hknight_pain3 ] {};
|
|
void() hknight_pain3 =[ $pain3, hknight_pain4 ] {};
|
|
void() hknight_pain4 =[ $pain4, hknight_pain5 ] {};
|
|
void() hknight_pain5 =[ $pain5, hknight_run1 ] {};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity inflictor, entity attacker, float damage) hknight_pain =
|
|
{
|
|
// Check all pain conditions and set up what to do next
|
|
monster_pain_check(attacker, damage);
|
|
|
|
// Any pain animation/sound required?
|
|
if (self.pain_check > 0) {
|
|
sound (self, CHAN_VOICE, self.pain_sound, 1, ATTN_NORM);
|
|
if (self.pain_check == 1) hknight_pain1 ();
|
|
else if (self.pain_check == 2) {
|
|
// reset axe hit and setup short pain recovery
|
|
self.pain_finished = time + 0.5;
|
|
self.axhitme = 0;
|
|
hknight_pain1 ();
|
|
}
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
void() hknight_die1 =[ $death1, hknight_die2 ] {ai_forward(10);};
|
|
void() hknight_die2 =[ $death2, hknight_die3 ] {monster_check_gib();ai_forward(8);};
|
|
void() hknight_die3 =[ $death3, hknight_die4 ] {monster_check_gib();
|
|
self.solid = SOLID_NOT; ai_forward(7);};
|
|
void() hknight_die4 =[ $death4, hknight_die5 ] {};
|
|
void() hknight_die5 =[ $death5, hknight_die6 ] {};
|
|
void() hknight_die6 =[ $death6, hknight_die7 ] {};
|
|
void() hknight_die7 =[ $death7, hknight_die8 ] {};
|
|
void() hknight_die8 =[ $death8, hknight_die9 ] {ai_forward(10);};
|
|
void() hknight_die9 =[ $death9, hknight_die10 ] {ai_forward(11);};
|
|
void() hknight_die10 =[ $death10, hknight_die11 ] {};
|
|
void() hknight_die11 =[ $death11, hknight_die12 ] {monster_death_postcheck();};
|
|
void() hknight_die12 =[ $death12, hknight_die12 ] {monster_deadbody_check();};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_dieb1 =[ $deathb1, hknight_dieb2 ] {};
|
|
void() hknight_dieb2 =[ $deathb2, hknight_dieb3 ] {monster_check_gib();};
|
|
void() hknight_dieb3 =[ $deathb3, hknight_dieb4 ] {monster_check_gib();
|
|
self.solid = SOLID_NOT;};
|
|
void() hknight_dieb4 =[ $deathb4, hknight_dieb5 ] {};
|
|
void() hknight_dieb5 =[ $deathb5, hknight_dieb6 ] {};
|
|
void() hknight_dieb6 =[ $deathb6, hknight_dieb7 ] {};
|
|
void() hknight_dieb7 =[ $deathb7, hknight_dieb8 ] {};
|
|
void() hknight_dieb8 =[ $deathb8, hknight_dieb9 ] {monster_death_postcheck();};
|
|
void() hknight_dieb9 =[ $deathb9, hknight_dieb9 ] {monster_deadbody_check();};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_die =
|
|
{
|
|
// Pre-check routine to tidy up extra entities
|
|
monster_death_precheck();
|
|
// Make sure attachments are removed
|
|
hknight_remove_attachment();
|
|
|
|
// regular death
|
|
if (!self.gibbed) {
|
|
sound (self, CHAN_VOICE, self.death_sound, 1, ATTN_NORM);
|
|
if (random() > 0.5) hknight_die1 (); // Forward - standard
|
|
else hknight_dieb1 (); // Backward - standard
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
void() hknight_sword_sound =
|
|
{
|
|
self.lip = random();
|
|
if (self.lip < 0.25) sound (self, CHAN_WEAPON, "weapons/sword1a.wav", 1, ATTN_NORM);
|
|
else if (self.lip < 0.5) sound (self, CHAN_WEAPON, "weapons/sword2a.wav", 1, ATTN_NORM);
|
|
else if (self.lip < 0.75) sound (self, CHAN_WEAPON, "weapons/sword1b.wav", 1, ATTN_NORM);
|
|
else sound (self, CHAN_WEAPON, "weapons/sword2b.wav", 1, ATTN_NORM);
|
|
};
|
|
|
|
//============================================================================
|
|
// Used by stone knights to wakup and attack the player
|
|
//----------------------------------------------------------------------
|
|
void() hknight_aistates =
|
|
{
|
|
self.th_stand = hknight_stand1; // Idling on feet
|
|
self.th_walk = hknight_walk1; // Walking
|
|
self.th_run = hknight_run1; // Running
|
|
self.th_melee = hknight_melee; // Sword attack
|
|
self.th_missile = hknight_magicc1; // Fire spikes
|
|
self.th_charge = hknight_char_a1; // Run + melee
|
|
self.th_pain = hknight_pain; // Stumble and pain
|
|
self.th_die = hknight_die; // Face meets floor
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() hknight_wakeup =
|
|
{
|
|
if (self.health < 0) return; // Check for sudden death!
|
|
|
|
self.use = SUB_Null; // no re-trigger
|
|
self.takedamage = DAMAGE_AIM; // Can receive damage
|
|
self.spawnstatue = FALSE; // No longer stationary
|
|
|
|
hknight_aistates(); // Restore AI state functions
|
|
monster_targets(); // Check for targets
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Restore pointy lightning knights to normal hell knights
|
|
//----------------------------------------------------------------------
|
|
void() hknight_pointwakeup =
|
|
{
|
|
// Check for sudden death!
|
|
if (self.health < 0) return;
|
|
// Always reset health+max back to default
|
|
self.health = self.max_health = self.reset_health;
|
|
// Fire any trigger event when breaking pointy pose
|
|
if (self.target2 != "") {
|
|
trigger_strs(self.target2, self.enemy);
|
|
self.target2 = ""; // Trigger once
|
|
}
|
|
|
|
// Restore pain threshold and timeout
|
|
self.pain_flinch = self.finaldest_x;
|
|
self.pain_timeout = self.finaldest_y;
|
|
self.pain_finished = time + 2;
|
|
self.flags = self.flags | FL_MONSTER;
|
|
|
|
// Restore the nail resistance if setup to be a statue skin
|
|
if (self.spawnflags & MON_STATUE) self.resist_nails = 0.2;
|
|
|
|
hknight_remove_attachment(); // Remove glowing sword
|
|
hknight_aistates(); // Restore AI state functions
|
|
// Check for any enemy target?
|
|
if (self.enemy) {
|
|
monster_sightsound(); // Sword sounds!
|
|
FoundHuntTarget(TRUE); // Setup goals and targets
|
|
self.think = self.th_run; // Move to running
|
|
}
|
|
else self.think = self.th_stand; // Stand around
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Special pain function for pointy lightning knights
|
|
//----------------------------------------------------------------------
|
|
void(entity inflictor, entity attacker, float damage) hknight_pointpain =
|
|
{
|
|
self.pain_finished = 0;
|
|
if (!(attacker.flags & FL_CLIENT)) return;
|
|
self.enemy = attacker;
|
|
hknight_pointwakeup();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Special use/trigger function for pointy lightning knights
|
|
//----------------------------------------------------------------------
|
|
void() hknight_pointuse =
|
|
{
|
|
// Check for enemy/health/player exceptions
|
|
if (self.enemy) return;
|
|
if (self.health < 1) return;
|
|
if (activator.items & IT_INVISIBILITY) return;
|
|
if (activator.flags & FL_NOTARGET) return;
|
|
|
|
// Trigger once
|
|
self.use = SUB_Null;
|
|
// Is the trigger the player?
|
|
if (activator.flags & FL_CLIENT) self.enemy = activator;
|
|
else self.enemy = world;
|
|
hknight_pointwakeup();
|
|
};
|
|
|
|
/*======================================================================
|
|
QUAKED monster_hell_knight (1 0 0) (-16 -16 -24) (16 16 40) Ambush
|
|
======================================================================*/
|
|
void() monster_hell_knight =
|
|
{
|
|
if (deathmatch) { remove(self); return; }
|
|
|
|
self.mdl = "progs/mon_hknight.mdl"; // Original ID Knight + extra skins
|
|
self.headmdl = "progs/h_hellkn.mdl";
|
|
self.gib1mdl = "progs/w_hknightsword.mdl"; // Unique sword
|
|
self.gib2mdl = "progs/gib_knfoot_l.mdl"; // left foot
|
|
self.gib3mdl = "progs/gib_knfoot_r.mdl"; // right foot
|
|
|
|
precache_model (self.mdl);
|
|
precache_model (self.headmdl);
|
|
precache_model (MODEL_PROJ_HKN);
|
|
precache_model (self.gib1mdl);
|
|
precache_model (self.gib2mdl);
|
|
precache_model (self.gib3mdl);
|
|
|
|
self.gib1sound = GIB_IMPACT_METALA;
|
|
if (random() < 0.5) self.gib2mdl = string_null;
|
|
if (random() < 0.5) self.gib3mdl = string_null;
|
|
|
|
// Firing of magic spikes (first 0.1s must loop)
|
|
precache_sound ("hknight/attack1.wav");
|
|
|
|
// TE_KNIGHTSPIKE = Magic spike hitting the wall
|
|
// Has got to be in this location because the
|
|
// filename is hardcoded (engine.exe)
|
|
precache_sound ("hknight/hit.wav");
|
|
|
|
//----------------------------------------------------------------------
|
|
if (self.spawnflags & MON_STATUE) {
|
|
self.idle_sound = "statue/idle1.wav";
|
|
self.idle_sound2 = "statue/idle2.wav";
|
|
self.idle_soundcom = "statue/comidle.wav";
|
|
precache_sound (self.idle_sound);
|
|
precache_sound (self.idle_sound2);
|
|
precache_sound (self.idle_soundcom);
|
|
|
|
self.sight_sound = "statue/sight.wav";
|
|
self.pain_sound = "statue/hurt.wav";
|
|
self.death_sound = "statue/death.wav";
|
|
self.gib1skin = 1; // Stoney sword
|
|
precache_stonegibs(); // Cache gib models
|
|
self.gibtype = GIBTYPE_STONE; // Stoney gibs!
|
|
self.gib2mdl = self.gib3mdl = string_null;
|
|
}
|
|
else {
|
|
self.idle_sound = "hknight/idle.wav";
|
|
precache_sound (self.idle_sound);
|
|
|
|
self.sight_sound = "hknight/sight1.wav";
|
|
self.pain_sound = "hknight/pain1.wav";
|
|
self.death_sound = "hknight/death1.wav";
|
|
|
|
// Check for poisonous entity flag
|
|
if (self.poisonous) {
|
|
precache_poisongibs(); // precache gibs
|
|
self.gibtype = GIBTYPE_POISON; // Poisonous blood trails
|
|
precache_model (MODEL_PROJ_HKNGRN);
|
|
}
|
|
}
|
|
// precache sight, pain and death (changes on type)
|
|
precache_sound (self.sight_sound);
|
|
precache_sound (self.pain_sound);
|
|
precache_sound (self.death_sound);
|
|
|
|
// New sword sweep/slash sounds
|
|
precache_sound ("weapons/sword1a.wav");
|
|
precache_sound ("weapons/sword1b.wav");
|
|
precache_sound ("weapons/sword2a.wav");
|
|
precache_sound ("weapons/sword2b.wav");
|
|
|
|
self.solid = SOLID_NOT; // No interaction with world
|
|
self.movetype = MOVETYPE_NONE; // Static item, no movement
|
|
if (self.bboxtype < 1) self.bboxtype = BBOX_TALL;
|
|
if (self.health < 1) self.health = 250;
|
|
self.gibhealth = -40; // Not easy to gib
|
|
self.gibbed = FALSE; // Still in one piece
|
|
self.pain_flinch = 40; // Medium pain threshold
|
|
self.pain_timeout = 1.5; // Higher than a knight
|
|
self.steptype = FS_TYPEHEAVY; // Heavy player
|
|
self.meleeattack = rint(random()*3); // randomize last melee attack
|
|
self.th_die = hknight_die; // Face meets floor
|
|
self.deathstring = " was slain by a Hell Knight\n";
|
|
|
|
// Always reset Ammo Resistance to be consistent
|
|
self.resist_shells = self.resist_nails = 0;
|
|
self.resist_rockets = self.resist_cells = 0;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Special lightning point knight (metal/stone skins)
|
|
//----------------------------------------------------------------------
|
|
if (self.spawnflags & MON_POINT_KNIGHT) {
|
|
self.weaponglow = "progs/mon_hknight_glow.mdl";
|
|
precache_model (self.weaponglow); // Glowing magical swords
|
|
if (self.spawnflags & MON_STATUE) {
|
|
self.classgroup = CG_STONE; // Stone hates metal!
|
|
self.skin = self.exactskin = 1; // Stone skin
|
|
self.pain_flinch = 60; // Higher resistance
|
|
self.pain_longanim = FALSE; // resistant to shadow axe
|
|
self.pain_timeout = 2; // high pain threshold
|
|
self.no_liquiddmg = TRUE; // Immune to slime/lava
|
|
}
|
|
self.th_stand = hknight_pointsetup; // Find target before shoot
|
|
self.th_run = hknight_pointfire; // Keep Looping
|
|
self.th_pain = hknight_pointpain; // Release from pointing
|
|
self.think1 = hknight_pointuse; // Special trigger event
|
|
self.reset_health = self.health; // Save for later
|
|
self.health = MEGADEATH; // Should be impossible to kill
|
|
if (self.t_length < 0) self.t_length = 5; // Random target factor
|
|
if (self.dmg < 0) self.dmg = 5; // Contact damage
|
|
|
|
// Setup default pose start pos + random modifier
|
|
if (CheckZeroVector(self.pos1)) self.pos1 = '20 0 24';
|
|
if (CheckZeroVector(self.pos2)) self.pos2 = '30 0 2';
|
|
// Setup default animation pose (magicB6 - magicB11)
|
|
if (CheckZeroVector(self.pos3)) self.pos3 = '98 103 1';
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Detect statues and setup different parameters
|
|
// Tougher skin, less pain, heavy feet etc
|
|
//----------------------------------------------------------------------
|
|
else if (self.spawnflags & MON_STATUE) {
|
|
self.classgroup = CG_STONE; // Stone hates metal!
|
|
self.poisonous = FALSE; // Cannot be poisonous and stone!
|
|
self.resist_nails = 0.5; // Tough skin for nails
|
|
self.reflectnails = TRUE; // Reflect nail projectiles
|
|
self.skin = self.exactskin = 1; // Stone skin
|
|
self.pain_flinch = 60; // Higher resistance
|
|
self.pain_longanim = FALSE; // resistant to shadow axe
|
|
self.pain_timeout = 2; // high pain threshold
|
|
self.no_liquiddmg = TRUE; // Immune to slime/lava
|
|
self.deathstring = " was smashed by a Stone Hell Knight\n";
|
|
|
|
// Allow knights to start with a stone skin, but not be frozen!
|
|
if (self.spawnflags & MON_NOTFROZEN) {
|
|
self.spawnflags = self.spawnflags - MON_STATUE;
|
|
hknight_aistates(); // setup AI state functions
|
|
}
|
|
else {
|
|
self.spawnstatue = TRUE; // Start as a statue
|
|
self.th_stand = knight_statue; // Stone idle sound
|
|
self.think1 = hknight_wakeup; // Wakeup function when triggered
|
|
|
|
// statues can change position when the player is not looking
|
|
// Specify a start/finish frame using pos1 or use frame
|
|
if (!self.pos1_x) {
|
|
self.pos1_x = $w_attack1; // Default pose (attacking)
|
|
self.pos1_y = $w_attack15;
|
|
}
|
|
// A frame has been specified, create a small range from that
|
|
if (self.frame > 0) {
|
|
self.pos1_x = self.pos1_y = self.frame;
|
|
}
|
|
// Work out range, make sure no negative values
|
|
self.pos1_z = fabs(self.pos1_y - self.pos1_x);
|
|
self.frame = self.pos1_x; // Setup frame / pose
|
|
}
|
|
}
|
|
else {
|
|
// Default Hell knight (like a death knight really)
|
|
self.pain_longanim = TRUE; // can be chopped with shadow axe
|
|
self.classgroup = CG_DKNIGHT; // Classic tincan!
|
|
hknight_aistates(); // setup AI state functions
|
|
}
|
|
|
|
self.classtype = CT_MONHELLK;
|
|
self.classmove = MON_MOVEWALK;
|
|
monster_start();
|
|
};
|
|
|
|
//======================================================================
|
|
/*QUAKED monster_hell_pointknight (0.75 0.5 0) (-16 -16 -24) (16 16 40) AMBUSH STATUE x NOSIGHT NOIDLE NOGFX STARTOFF x Not_Easy Not_Normal Not_Hard Not_DM
|
|
{ model(":progs/mon_hknight.mdl"); }
|
|
Special Electricity Pointing Hell Knight
|
|
-------- KEYS --------
|
|
targetname : monster/trigger name
|
|
target : target for electricity
|
|
target2 : trigger to fire when electricity stopped
|
|
pos1 : XYZ offset to start lightning (uses v_forward/v_right/v_up)
|
|
pos2 : XYZ random offset to start lightning (uses v_forward/v_right/v_up)
|
|
pos3 : Animation lightning frame set (Start/Finish/Direction)
|
|
t_length : random wobble to electricity target origin (0=def 5)
|
|
dmg : amount of contact damage with electricity (0=def 5)
|
|
deathtarget : entity to trigger upon death (useful if target field already in use)
|
|
health : Override default health settings
|
|
upgrade_ssg : 1 = will only spawn if shotgun upgrade active on server
|
|
upgrade_axe : 1 = will only spawn if axe upgrade active on server
|
|
upgrade_lg : 1 = will only spawn if lightning gun upgrade active on server
|
|
nomonstercount : will not be included in any monster count functionality
|
|
no_zaware : All Z Aware projectiles will be disabled
|
|
bboxtype : Change bbox 1=Tiny,4=Short,5=Tall,7=Wide,8=Giant,10=Massive
|
|
gibondeath : 1 = always explode in a shower of gibs on death
|
|
bodyfadeaway : Time (secs) before body/head will fade away (default=0)
|
|
-------- SPAWNFLAGS --------
|
|
AMBUSH : the monster will only wake up on seeing the player, not by another monster
|
|
STATUE : Stone statue skin
|
|
NOSIGHT : No sight sound
|
|
NOIDLE : No idle sound
|
|
NOGFX : No spawn effect or sound when triggered
|
|
STARTOFF : Trigger Spawn
|
|
-------- NOTES --------
|
|
Special Electricity Pointing Hell Knight*/
|
|
//======================================================================
|
|
void() monster_hell_pointknight =
|
|
{
|
|
// Setup with new special spawnflag (not mapper controlled)
|
|
self.spawnflags = self.spawnflags | MON_POINT_KNIGHT;
|
|
// remove notfrozen and angry, always start/spawn firing lightning
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & MON_NOTFROZEN);
|
|
self.spawnflags = self.spawnflags - (self.spawnflags & MON_SPAWN_ANGRY);
|
|
|
|
// Override early to prevent unnecesaary cache of assets
|
|
self.poisonous = FALSE; // Never poisonous
|
|
|
|
// If not target setup then stop!
|
|
if (self.target == "") {
|
|
dprint("\b[HKNIGHT]\b Pointing knight needs target!\n");
|
|
spawn_marker(self.origin, SPNMARK_YELLOW);
|
|
remove(self);
|
|
return;
|
|
}
|
|
// Loop back through existing hell knight function
|
|
else monster_hell_knight();
|
|
}; |