792 lines
33 KiB
Plaintext
792 lines
33 KiB
Plaintext
/*==============================================================================
|
|
MINOTAUR (Hexen2 model by Raven Software) (The Shuffler from Marcher MOD)
|
|
also known as "The Nethergoat: Spawnmaster of the Elder World"
|
|
==============================================================================*/
|
|
|
|
// Slap RIGHT - 7, 9, 11, 13, 15, 17
|
|
// Slap LEFT - 19, 21, 23, 1, 3, 5
|
|
$frame bwalk1 bwalk2 bwalk3 bwalk4 bwalk5 bwalk6 bwalk7 bwalk8
|
|
$frame bwalk9 bwalk10 bwalk11 bwalk12 bwalk13 bwalk14 bwalk15 bwalk16
|
|
$frame bwalk17 bwalk18 bwalk19 bwalk20 bwalk21 bwalk22 bwalk23 bwalk24
|
|
|
|
// (024) Charging - Head down
|
|
$frame charge1 charge2 charge3 charge4 charge5 charge6 charge7 charge8
|
|
$frame charge9 charge10 charge11 charge12
|
|
|
|
// (036) Fall forward onto knees
|
|
$frame death1 death2 death3 death4 death5 death6 death7 death8
|
|
$frame death9 death10 death11 death12 death13 death14 death15 death16
|
|
$frame death17 death18 death19 death20 death21 death22 death23 death24
|
|
$frame death25
|
|
|
|
// (061) Charging - swiping arm and biting
|
|
$frame gore1 gore2 gore3 gore4 gore5 gore6 gore7 gore8
|
|
$frame gore9 gore10 gore11 gore12
|
|
|
|
// (073) Stationary - howl at the moon
|
|
$frame howl1 howl2 howl3 howl4 howl5 howl6 howl7 howl8
|
|
$frame howl9 howl10 howl11 howl12 howl13 howl14 howl15 howl16
|
|
$frame howl17 howl18 howl19 howl20 howl21 howl22 howl23 howl24
|
|
$frame howl25 howl26 howl27 howl28 howl29 howl30 howl31 howl32
|
|
$frame howl33 howl34 howl35 howl36
|
|
|
|
// (109) Small leap/jump
|
|
$frame jump1 jump2 jump3 jump4 jump5 jump6 jump7 jump8
|
|
$frame jump9 jump10 jump11 jump12 jump13 jump14 jump15 jump16
|
|
$frame jump17 jump18 jump19 jump20 jump21 jump22 jump23 jump24
|
|
|
|
// (133) Quick pain flinch backward
|
|
$frame pain1 pain2 pain3 pain4 pain5 pain6 pain7 pain8
|
|
|
|
// (141) Stationary - shake head/arms
|
|
$frame shake1 shake2 shake3 shake4 shake5 shake6 shake7 shake8
|
|
$frame shake9 shake10 shake11 shake12 shake13 shake14 shake15 shake16
|
|
$frame shake17 shake18 shake19 shake20
|
|
|
|
// (161) Stationary - fire projectile
|
|
$frame magic1 magic2 magic3 magic4 magic5 magic6 magic7 magic8
|
|
$frame magic9 magic10 magic11 magic12 magic13 magic14 magic15 magic16
|
|
$frame magic17 magic18 magic19 magic20 magic21 magic22 magic23 magic24
|
|
$frame magic25 magic26 magic27 magic28 magic29 magic30 magic31 magic32
|
|
|
|
// Looks terrible, not used
|
|
// (193) Slide sideways (LEFT)
|
|
// (198) Slide sideways (RIGHT)
|
|
$frame slideL1 slideL2 slideL3 slideL4 slideL5
|
|
$frame slideR1 slideR2 slideR3 slideR4 slideR5
|
|
|
|
// (203) Idle/wait
|
|
$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8
|
|
$frame stand9 stand10 stand11 stand12 stand13 stand14 stand15 stand16
|
|
$frame stand17 stand18 stand19 stand20 stand21 stand22 stand23 stand24
|
|
|
|
// (227) Walking
|
|
$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8
|
|
$frame walk9 walk10 walk11 walk12 walk13 walk14 walk15 walk16
|
|
$frame walk17 walk18 walk19 walk20 walk21 walk22 walk23 walk24
|
|
|
|
//============================================================================
|
|
void() mino_stand1 =[ $stand1, mino_stand2 ] {monster_idle_sound();ai_stand();};
|
|
void() mino_stand2 =[ $stand3, mino_stand3 ] {ai_stand();};
|
|
void() mino_stand3 =[ $stand5, mino_stand4 ] {ai_stand();};
|
|
void() mino_stand4 =[ $stand7, mino_stand5 ] {ai_stand();};
|
|
void() mino_stand5 =[ $stand9, mino_stand6 ] {ai_stand();};
|
|
void() mino_stand6 =[ $stand11, mino_stand7 ] {ai_stand();};
|
|
void() mino_stand7 =[ $stand13, mino_stand8 ] {ai_stand();};
|
|
void() mino_stand8 =[ $stand15, mino_stand9 ] {ai_stand();};
|
|
void() mino_stand9 =[ $stand17, mino_stand10 ] {ai_stand();};
|
|
void() mino_stand10 =[ $stand19, mino_stand11 ] {ai_stand();};
|
|
void() mino_stand11 =[ $stand21, mino_stand12 ] {ai_stand();};
|
|
void() mino_stand12 =[ $stand23, mino_stand1 ] {ai_stand();};
|
|
|
|
//============================================================================
|
|
void() mino_walk1 =[ $walk1, mino_walk2 ] {monster_idle_sound();ai_walk(12);};
|
|
void() mino_walk2 =[ $walk3, mino_walk3 ] {ai_walk(12);};
|
|
void() mino_walk3 =[ $walk5, mino_walk4 ] {ai_walk(9);};
|
|
void() mino_walk4 =[ $walk7, mino_walk5 ] {ai_walk(10);monster_footstep(FALSE);};
|
|
void() mino_walk5 =[ $walk9, mino_walk6 ] {ai_walk(4);};
|
|
void() mino_walk6 =[ $walk11, mino_walk7 ] {ai_walk(9);};
|
|
void() mino_walk7 =[ $walk13, mino_walk8 ] {ai_walk(12);};
|
|
void() mino_walk8 =[ $walk15, mino_walk9 ] {ai_walk(12);};
|
|
void() mino_walk9 =[ $walk17, mino_walk10 ] {ai_walk(9);};
|
|
void() mino_walk10 =[ $walk19, mino_walk11 ] {ai_walk(10);monster_footstep(FALSE);};
|
|
void() mino_walk11 =[ $walk21, mino_walk12 ] {ai_walk(4);};
|
|
void() mino_walk12 =[ $walk23, mino_walk1 ] {ai_walk(9);};
|
|
|
|
//============================================================================
|
|
// Charge/run/gore!
|
|
//============================================================================
|
|
void() mino_run;
|
|
|
|
void() mino_rage1 =[ $charge2, mino_rage2 ] {monster_idle_sound();ai_run(34);};
|
|
void() mino_rage2 =[ $charge4, mino_rage3 ] {ai_run(28);monster_footstep(FALSE);};
|
|
void() mino_rage3 =[ $charge6, mino_rage4 ] {ai_run(26);};
|
|
void() mino_rage4 =[ $charge8, mino_rage5 ] {ai_run(34);};
|
|
void() mino_rage5 =[ $charge10, mino_rage6 ] {ai_run(28);monster_footstep(FALSE);};
|
|
void() mino_rage6 =[ $charge12, mino_run ] {ai_run(26);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_run1 =[ $walk1, mino_run2 ] {monster_idle_sound();ai_run(12);};
|
|
void() mino_run2 =[ $walk3, mino_run3 ] {ai_run(12);};
|
|
void() mino_run3 =[ $walk5, mino_run4 ] {ai_run(9);};
|
|
void() mino_run4 =[ $walk7, mino_run5 ] {ai_run(10);monster_footstep(FALSE);};
|
|
void() mino_run5 =[ $walk9, mino_run6 ] {ai_run(4);};
|
|
void() mino_run6 =[ $walk11, mino_run7 ] {ai_run(9);};
|
|
void() mino_run7 =[ $walk13, mino_run8 ] {ai_run(12);};
|
|
void() mino_run8 =[ $walk15, mino_run9 ] {ai_run(12);};
|
|
void() mino_run9 =[ $walk17, mino_run10 ] {ai_run(9);};
|
|
void() mino_run10 =[ $walk19, mino_run11 ] {ai_run(10);monster_footstep(FALSE);};
|
|
void() mino_run11 =[ $walk21, mino_run12 ] {ai_run(4);};
|
|
void() mino_run12 =[ $walk23, mino_run ] {ai_run(9);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_run =
|
|
{
|
|
if (self.attack_rage) mino_rage1();
|
|
else mino_run1();
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Short howl animation to switch to rage mode
|
|
//----------------------------------------------------------------------
|
|
void() mino_howl1 =[ $howl1, mino_howl2 ] {
|
|
sound (self, CHAN_WEAPON, "minotaur/attack1long.wav", 1, ATTN_NORM);};
|
|
void() mino_howl2 =[ $howl3, mino_howl3 ] {};
|
|
void() mino_howl3 =[ $howl5, mino_howl4 ] {};
|
|
void() mino_howl4 =[ $howl7, mino_howl5 ] {};
|
|
void() mino_howl5 =[ $howl9, mino_howl6 ] {};
|
|
void() mino_howl6 =[ $howl19, mino_howl7 ] {};
|
|
void() mino_howl7 =[ $howl21, mino_howl8 ] {};
|
|
void() mino_howl8 =[ $howl23, mino_howl9 ] {};
|
|
void() mino_howl9 =[ $howl25, mino_howl10 ] {};
|
|
void() mino_howl10 =[ $howl27, mino_howl11 ] {};
|
|
void() mino_howl11 =[ $howl29, mino_howl12 ] {};
|
|
void() mino_howl12 =[ $howl31, mino_run ] {};
|
|
|
|
//============================================================================
|
|
// MELEE ATTACK (claws)
|
|
//============================================================================
|
|
void(float side) mino_claw =
|
|
{
|
|
local float ldmg;
|
|
|
|
if (!self.enemy) return;
|
|
if (self.health < 1) return;
|
|
ai_face (); // Turn towards enemy target
|
|
ai_damagebreakable(50); // Damage any breakables
|
|
|
|
if (!ai_checkmelee(MONAI_MELEEMINOTAUR) || !self.enemy.takedamage) {
|
|
// Melee claw miss sound
|
|
sound (self, CHAN_WEAPON, "minotaur/swipe.wav", 1, ATTN_NORM);
|
|
}
|
|
else {
|
|
// Melee claw hit sound
|
|
if (self.lefty) sound (self, CHAN_WEAPON, "minotaur/strike1.wav", 1, ATTN_NORM);
|
|
else sound (self, CHAN_WEAPON, "minotaur/strike2.wav", 1, ATTN_NORM);
|
|
self.lefty = 1 - self.lefty;
|
|
ldmg = 12 + 6*random();
|
|
// Rage attack is once per animation frame, more deadly
|
|
if (self.attack_rage) ldmg = ldmg*2;
|
|
T_Damage (self.enemy, self, self, ldmg, DAMARMOR);
|
|
SpawnMeatSpray (self, self.enemy, side);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Slap LEFT - 19, 21, 23, 1, 3, 5
|
|
// Slap RIGHT - 7, 9, 11, 13, 15, 17
|
|
void() mino_slapL1 =[ $bwalk19, mino_slapL2 ] {ai_charge(10);monster_footstep(FALSE);};
|
|
void() mino_slapL2 =[ $bwalk21, mino_slapL3 ] {ai_charge(4);};
|
|
void() mino_slapL3 =[ $bwalk23, mino_slapL4 ] {ai_charge(9);};
|
|
void() mino_slapL4 =[ $bwalk1, mino_slapL5 ] {ai_charge(12);mino_claw(-250);};
|
|
void() mino_slapL5 =[ $bwalk3, mino_slapL6 ] {ai_charge(12);};
|
|
void() mino_slapL6 =[ $bwalk5, mino_slapR1 ] {ai_charge(9);};
|
|
|
|
void() mino_slapR1 =[ $bwalk7, mino_slapR2 ] {ai_charge(10);monster_footstep(FALSE);};
|
|
void() mino_slapR2 =[ $bwalk9, mino_slapR3 ] {ai_charge(4);};
|
|
void() mino_slapR3 =[ $bwalk11, mino_slapR4 ] {ai_charge(9);};
|
|
void() mino_slapR4 =[ $bwalk13, mino_slapR5 ] {ai_charge(12);mino_claw(250);};
|
|
void() mino_slapR5 =[ $bwalk15, mino_slapR6 ] {ai_charge(12);};
|
|
void() mino_slapR6 =[ $bwalk17, mino_run ] {ai_charge(9);
|
|
// Check if enemy is close, alive and visible before doing more melee
|
|
if (random() < 0.5 && ai_checkmelee(MONAI_MELEEMINOTAUR) &&
|
|
self.enemy.health > 0 && visible(self.enemy)) self.think = mino_slapL1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Rage melee attack
|
|
void() mino_gore1 =[ $gore2, mino_gore2 ] {ai_charge(34);};
|
|
void() mino_gore2 =[ $gore4, mino_gore3 ] {ai_charge(28);monster_footstep(FALSE);};
|
|
void() mino_gore3 =[ $gore6, mino_gore4 ] {ai_charge(26);mino_claw(-250);};
|
|
void() mino_gore4 =[ $gore8, mino_gore5 ] {ai_charge(34);};
|
|
void() mino_gore5 =[ $gore10, mino_gore6 ] {ai_charge(28);monster_footstep(FALSE);};
|
|
void() mino_gore6 =[ $gore12, mino_run ] {ai_charge(26);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_melee =
|
|
{
|
|
// Switch to a faster more deadly melee attack if in rage mode
|
|
if (self.attack_rage) mino_gore1();
|
|
else {
|
|
// Randomly start left or right and then loop
|
|
if (random() < 0.5) mino_slapR1();
|
|
else mino_slapL1();
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
// JUMP ATTACK - pre-rage only
|
|
//============================================================================
|
|
void() mino_JumpTouch =
|
|
{
|
|
local float ldmg;
|
|
|
|
if (self.health < 1) return;
|
|
ai_jumpbreakable(30); // Damage any breakables
|
|
self.touch = SUB_Null; // No more touching
|
|
self.count = self.count + 1; // Total amount of touch jumps
|
|
self.think = self.th_jumpexit; // Exit frame
|
|
|
|
// Keep track of how many times touched the same object
|
|
if (self.jumptouch == other) self.jump_flag = time + MONAI_JUMPTIMEOUT;
|
|
self.jumptouch = other; // Keep track of touch target
|
|
|
|
if ( CanDamage(other, self) ) {
|
|
if ( vlen(self.velocity) > 300 ) {
|
|
ldmg = 20 + 10*random();
|
|
T_Damage (other, self, self, ldmg, DAMARMOR);
|
|
spawn_touchblood (self, self.enemy, ldmg*3);
|
|
}
|
|
}
|
|
|
|
// Is the minotaur floating in the air?
|
|
if (!checkbottom(self)) {
|
|
// Is the minotaur standing on something?
|
|
if (self.flags & FL_ONGROUND) {
|
|
// Do an extra jump if got the count
|
|
if (self.count < 2) self.think = self.th_jump;
|
|
}
|
|
}
|
|
|
|
// Next timer
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_jump1 =[ $jump1, mino_jump2 ] {
|
|
self.jump_flag = time + MONAI_JUMPMINOTIME;
|
|
// Use combat idle grunt sounds (can get annoying otherwise)
|
|
if (random() < 0.5) sound (self, CHAN_VOICE, self.idle_soundcom, 1, ATTN_NORM);
|
|
else sound (self, CHAN_VOICE, self.idle_soundcom2, 1, ATTN_NORM);
|
|
};
|
|
void() mino_jump2 =[ $jump3, mino_jump3 ] {ai_face();};
|
|
void() mino_jump3 =[ $jump5, mino_jump4 ] {ai_face();};
|
|
void() mino_jump4 =[ $jump7, mino_jump5 ] {
|
|
ai_face();
|
|
self.jump_flag = time + MONAI_JUMPMINOTIME;
|
|
self.touch = mino_JumpTouch;
|
|
makevectors (self.angles);
|
|
self.velocity = v_forward * 450 + '0 0 250';
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
self.oldorigin = self.origin;
|
|
};
|
|
|
|
// Flying through the air waiting to touch something!
|
|
void() mino_jump5 =[ $jump9, mino_jump6 ] {ai_face();};
|
|
void() mino_jump6 =[ $jump11, mino_jump7 ] {};
|
|
void() mino_jump7 =[ $jump13, mino_jump8 ] {ai_face();};
|
|
void() mino_jump8 =[ $jump15, mino_jump8 ] {
|
|
// Double check monster is still falling?
|
|
if (CheckZeroVector(self.velocity) || self.oldorigin == self.origin) {
|
|
self.ideal_yaw = random() * 360; //random jump angle
|
|
self.think = mino_jump4; // Keep jumping
|
|
}
|
|
self.oldorigin = self.origin;
|
|
};
|
|
//----------------------------------------------------------------------
|
|
void() mino_jump10 =[ $jump19, mino_jump12 ] {ai_charge(8);monster_footstep(FALSE);};
|
|
void() mino_jump12 =[ $jump23, mino_run ] {ai_charge(10);ai_resetangles();
|
|
// If close enough start melee ELSE return to run cycle
|
|
if (ai_checkmelee(MONAI_MELEEMINOTAUR) && self.enemy.health > 0)
|
|
self.think = mino_melee;
|
|
};
|
|
|
|
//============================================================================
|
|
// RANGE ATTACK - Plasma Bolts (rocket)
|
|
//============================================================================
|
|
void() minotaur_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 = MODEL_PROJ_MBALL;
|
|
}
|
|
if (!self.attachment2) {
|
|
self.attachment2 = spawn();
|
|
self.attachment2.owner = self;
|
|
self.attachment2.classtype = CT_ATTACHMENT;
|
|
self.attachment2.mdl = MODEL_PROJ_MBALL;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() minotaur_finish_attachment =
|
|
{
|
|
if (self.attachment) {
|
|
setmodel(self.attachment, "");
|
|
self.attachment.state = STATE_OFF;
|
|
}
|
|
if (self.attachment2) {
|
|
setmodel(self.attachment2, "");
|
|
self.attachment2.state = STATE_OFF;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() minotaur_remove_attachment =
|
|
{
|
|
if (self.attachment) {
|
|
self.attachment.think = SUB_Remove;
|
|
self.attachment.nextthink = time + 0.1;
|
|
}
|
|
if (self.attachment2) {
|
|
self.attachment2.think = SUB_Remove;
|
|
self.attachment2.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity handent, vector orgofs, float dbframe) minotaur_handball =
|
|
{
|
|
local vector org;
|
|
|
|
// Frame 0 is start of the sequence (move everything into place)
|
|
if (dbframe == 0) {
|
|
handent.state = STATE_ON;
|
|
setorigin(handent, self.origin);
|
|
setmodel(handent, handent.mdl);
|
|
setsize (handent, VEC_ORIGIN, VEC_ORIGIN);
|
|
handent.movetype = MOVETYPE_NONE;
|
|
handent.solid = SOLID_NOT;
|
|
handent.skin = self.exactskin;
|
|
handent.alpha = 0.85;
|
|
}
|
|
|
|
// Generate attachment in hand (left)
|
|
// makevectors set in previous function (minotaur_hands)
|
|
org = self.origin + attack_vector(orgofs);
|
|
setorigin(handent, org);
|
|
handent.angles_y = rint(random()*359);
|
|
handent.frame = dbframe;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// There is a ball for each hand with different origins
|
|
//----------------------------------------------------------------------
|
|
void(vector leftofs, vector rightofs, float dbframe) minotaur_hands =
|
|
{
|
|
if (self.health < 1) return;
|
|
|
|
// Keep turning towards enemy
|
|
ai_face();
|
|
makevectors(self.angles);
|
|
|
|
// Generate a ball in each hand
|
|
minotaur_handball(self.attachment, leftofs, dbframe);
|
|
minotaur_handball(self.attachment2, rightofs, dbframe);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Fire a volley of plasma/poison bolts at the enemy
|
|
//----------------------------------------------------------------------
|
|
void(vector orgofs, float angofs) minotaur_fire =
|
|
{
|
|
local vector org, angvec, dir;
|
|
|
|
if (!self.enemy) return;
|
|
if (self.health < 1) return;
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
self.attack_speed = SPEED_MINOBOLT + (skill * SPEED_MINOSKILL);
|
|
makevectors(self.angles);
|
|
org = self.origin + attack_vector(orgofs);
|
|
dir = (self.enemy.origin - org) + (v_right*(angofs*30));
|
|
dir = normalize (dir);
|
|
|
|
// The spawning minotaur has poison spikes instead
|
|
if (self.spawnflags & MON_MINOTAUR_MINIONS)
|
|
launch_projectile(org, dir, CT_PROJ_MPOISON, self.attack_speed);
|
|
else {
|
|
// Projectile behaves like plasma (light/trail/impact effects)
|
|
// The correct projectile model is switched in launch function
|
|
launch_plasma(org, dir, CT_MONMINOTAUR, self.attack_speed);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Main projectile plasma/poison attack of both types of minotaur
|
|
//----------------------------------------------------------------------
|
|
void() mino_magic1 =[ $magic1, mino_magic2 ] {ai_face();
|
|
if (random() < 3) sound (self, CHAN_BODY, "minotaur/attack1start.wav", 1, ATTN_NORM);};
|
|
void() mino_magic2 =[ $magic3, mino_magic3 ] {minotaur_hands('34 0 44','-20 30 20',0);};
|
|
void() mino_magic3 =[ $magic5, mino_magic4 ] {minotaur_hands('30 -12 64','-20 44 28',1);};
|
|
void() mino_magic4 =[ $magic7, mino_magic5 ] {minotaur_hands('15 -16 88','-10 56 48',2);};
|
|
void() mino_magic5 =[ $magic9, mino_magic6 ] {minotaur_hands('0 -40 80','-10 48 76',3);};
|
|
void() mino_magic6 =[ $magic11, mino_magic7 ] {minotaur_hands('16 -36 72','-12 44 80',4);};
|
|
void() mino_magic7 =[ $magic13, mino_magic8 ] {minotaur_hands('36 -20 56','24 32 68',5);};
|
|
|
|
void() mino_magic8 =[ $magic15, mino_magic9 ] {
|
|
sound (self, CHAN_WEAPON, "minotaur/bolt_fire.wav", 1, ATTN_NORM);
|
|
minotaur_hands('40 -3 46','40 1 44',6);minotaur_fire('40 0 45',-2);};
|
|
void() mino_magic9 =[ $magic17, mino_magic10 ] {ai_back(8);
|
|
minotaur_hands('32 -2 44','32 2 44',5);minotaur_fire('32 0 44',-1);};
|
|
void() mino_magic10 =[ $magic19, mino_magic11 ] {ai_back(4);
|
|
minotaur_hands('20 0 48','20 2 46',4);minotaur_fire('20 0 47',0);};
|
|
void() mino_magic11 =[ $magic21, mino_magic12 ] {ai_forward(4);monster_footstep(FALSE);
|
|
minotaur_hands('28 0 50','28 2 48',3);minotaur_fire('28 0 49',1);};
|
|
void() mino_magic12 =[ $magic23, mino_magic13 ] {ai_forward(8);
|
|
minotaur_hands('44 0 46','44 2 44',2);minotaur_fire('44 0 45',2);};
|
|
|
|
void() mino_magic13 =[ $magic25, mino_magic14 ] {
|
|
// Remove hand attachments and setup particle explosion
|
|
// Create a cloud of particles which slowly raise upwards
|
|
minotaur_finish_attachment();
|
|
ai_face();
|
|
makevectors(self.angles);
|
|
self.finalangle = self.origin + (v_forward * 44) + (v_up * 32);
|
|
// Spawn a mist of blue/green particles where projectiles came from
|
|
particle_explode(self.finalangle, 50, 3, self.part_style, PARTICLE_BURST_MINOTAUR);
|
|
// Random chance of final growl at player
|
|
if (random() < 0.3) sound (self, CHAN_BODY, "minotaur/attack1end.wav", 1, ATTN_NORM);
|
|
};
|
|
void() mino_magic14 =[ $magic27, mino_magic15 ] {};
|
|
void() mino_magic15 =[ $magic29, mino_magic16 ] {ai_face();};
|
|
void() mino_magic16 =[ $magic31, mino_run ] {ai_face();};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Spawn particles from body that float upwards during summons
|
|
//----------------------------------------------------------------------
|
|
void() mino_sumeffect =
|
|
{ particle_debuff(self.origin+'0 0 64', 16, rint(4+random()*4), PARTICLE_BURST_GREEN);};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Display a green ball where the gargoyle is spawning
|
|
//----------------------------------------------------------------------
|
|
void(float dbframe) mino_sumball =
|
|
{
|
|
// Double check the space checks worked
|
|
if (self.aflag == FALSE) return;
|
|
|
|
// Frame 0 is start of the sequence (move everything into place)
|
|
if (dbframe == 0) {
|
|
self.attachment.state = STATE_ON;
|
|
setorigin(self.attachment, self.pos2);
|
|
setmodel(self.attachment, self.attachment.mdl);
|
|
setsize (self.attachment, VEC_ORIGIN, VEC_ORIGIN);
|
|
self.attachment.movetype = MOVETYPE_NONE;
|
|
self.attachment.solid = SOLID_NOT;
|
|
self.attachment.skin = self.exactskin;
|
|
}
|
|
|
|
// rotate and fade the ball as it grows
|
|
self.attachment.angles_y = rint(random()*359);
|
|
self.attachment.frame = dbframe;
|
|
self.attachment.lip = 0.3 + ((7-dbframe) * 0.1);
|
|
self.attachment.alpha = self.attachment.lip;
|
|
particle_explode(self.pos2, 5+random()*10, 3, self.part_style, PARTICLE_BURST_MINOTAUR);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Raise up head and howl at the sky and spawn green particles from body
|
|
// Always check if there is space to spawn a gargoyle first
|
|
// Block monster pain function so that the summons is completed
|
|
//----------------------------------------------------------------------
|
|
void() mino_summon1 =[ $howl1, mino_summon2 ] {ai_face();self.pain_finished = time + 2;};
|
|
void() mino_summon2 =[ $howl3, mino_summon3 ] {ai_face();};
|
|
void() mino_summon3 =[ $howl5, mino_summon4 ] {ai_face();mino_sumeffect();
|
|
sound (self, CHAN_BODY, "minotaur/attack1long.wav", 1, ATTN_NORM);};
|
|
void() mino_summon4 =[ $howl7, mino_summon5 ] {mino_sumeffect();};
|
|
void() mino_summon5 =[ $howl9, mino_summon6 ] {mino_sumeffect();};
|
|
void() mino_summon6 =[ $howl11, mino_summon7 ] {mino_sumeffect();};
|
|
void() mino_summon7 =[ $howl13, mino_summon8 ] {mino_sumeffect();
|
|
// Find out if there is enough space to spawn a gargoyle
|
|
// Findradius version
|
|
makevectors(self.angles);
|
|
self.pos1 = self.origin + self.view_ofs + v_forward*64 + v_up*32;
|
|
self.aflag = find_minionspace(self.pos1);
|
|
|
|
// If the spawn locaiton all clear, spawn something!
|
|
if (self.aflag == TRUE) {
|
|
minion_gargoyle(self.pos1, self.enemy);
|
|
self.pos2 = self.pos1 - '0 0 16'; // Origin of gargoyle
|
|
mino_sumball(0);
|
|
}
|
|
};
|
|
void() mino_summon8 =[ $howl15, mino_summon9 ] {mino_sumeffect();mino_sumball(1);};
|
|
void() mino_summon9 =[ $howl17, mino_summon10 ] {mino_sumeffect();mino_sumball(2);};
|
|
void() mino_summon10 =[ $howl19, mino_summon11 ] {mino_sumeffect();mino_sumball(3);};
|
|
void() mino_summon11 =[ $howl21, mino_summon12 ] {mino_sumeffect();mino_sumball(4);};
|
|
void() mino_summon12 =[ $howl23, mino_summon13 ] {mino_sumeffect();mino_sumball(5);};
|
|
void() mino_summon13 =[ $howl25, mino_summon14 ] {mino_sumeffect();mino_sumball(6);};
|
|
void() mino_summon14 =[ $howl27, mino_summon15 ] {mino_sumeffect();mino_sumball(7);};
|
|
void() mino_summon15 =[ $howl29, mino_summon16 ] {minotaur_finish_attachment();
|
|
if (self.aflag == TRUE) particle_explode(self.pos1, 50, 3, self.part_style, PARTICLE_BURST_MINOTAUR);};
|
|
void() mino_summon16 =[ $howl31, mino_summon17 ] {};
|
|
void() mino_summon17 =[ $howl33, mino_summon18 ] {ai_face();};
|
|
void() mino_summon18 =[ $howl35, mino_run ] {ai_face();SUB_AttackFinished (1 + 2*random());};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Both type of minotaurs go through the same magic function
|
|
//----------------------------------------------------------------------
|
|
void() mino_magic =
|
|
{
|
|
local entity miniondef;
|
|
|
|
// Make sure the attachments are setup ready
|
|
minotaur_create_attachment();
|
|
|
|
if (self.spawnflags & MON_MINOTAUR_MINIONS && !self.minion_active) {
|
|
// Check for minion template first
|
|
miniondef = find(world,classname,"monster_minotaurminion");
|
|
if (miniondef.classtype == CT_CACHEGARGOYLE) setup_minionsupport();
|
|
else {
|
|
// If template no available, warn and remove feature
|
|
dprint("\b[MINOTAUR]\b Cannot find minion template!\n");
|
|
self.spawnflags = self.spawnflags - MON_MINOTAUR_MINIONS;
|
|
}
|
|
}
|
|
|
|
// Is the target the player or monster?
|
|
if (self.enemy.flags & FL_CLIENT) {
|
|
if (self.spawnflags & MON_MINOTAUR_MINIONS) {
|
|
// has the gargoyle limit been reached?
|
|
if (query_minionactive(self) == TRUE) mino_summon1();
|
|
else {
|
|
if (self.minion_baseattack > 0 ) mino_magic1();
|
|
else mino_run();
|
|
}
|
|
}
|
|
// White Minotaur attacks with plasma
|
|
else mino_magic1();
|
|
|
|
}
|
|
// Always attack monsters with plasma/poison projectiles
|
|
else mino_magic1();
|
|
};
|
|
|
|
//============================================================================
|
|
// CHARGE ATTACK - just been hit with a long pain animation (pre-rage only)
|
|
//============================================================================
|
|
void(float dist) mino_chargecheck =
|
|
{
|
|
// turn, face and pursue enemy
|
|
ai_charge(dist);
|
|
// Close enough for melee combat?
|
|
if (ai_checkmelee(MONAI_MELEEMINOTAUR)) self.th_melee();
|
|
// Enemy dead? run out of stamina or enemy not infront?
|
|
if (self.enemy.health < 1) self.think = mino_run;
|
|
else if (self.attack_finished < time) self.think = mino_run;
|
|
else if (!infront(self.enemy)) self.think = mino_run;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_charge1 =[ $charge1, mino_charge2 ] {mino_chargecheck(34);};
|
|
void() mino_charge2 =[ $charge3, mino_charge3 ] {mino_chargecheck(28);monster_footstep(FALSE);};
|
|
void() mino_charge3 =[ $charge5, mino_charge4 ] {mino_chargecheck(26);};
|
|
void() mino_charge4 =[ $charge7, mino_charge5 ] {mino_chargecheck(34);};
|
|
void() mino_charge5 =[ $charge9, mino_charge6 ] {mino_chargecheck(28);monster_footstep(FALSE);};
|
|
void() mino_charge6 =[ $charge11, mino_charge1 ] {mino_chargecheck(26);};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_charge =
|
|
{
|
|
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
|
self.attack_finished = time + 2 + random();
|
|
mino_charge1();
|
|
};
|
|
|
|
//============================================================================
|
|
// (133) Quick pain flinch backward
|
|
void() mino_pain1 = [ $pain1, mino_pain2 ] {};
|
|
void() mino_pain2 = [ $pain2, mino_pain3 ] {};
|
|
void() mino_pain3 = [ $pain3, mino_pain4 ] {};
|
|
void() mino_pain4 = [ $pain4, mino_pain5 ] {};
|
|
void() mino_pain5 = [ $pain5, mino_pain6 ] {};
|
|
void() mino_pain6 = [ $pain6, mino_pain7 ] {};
|
|
void() mino_pain7 = [ $pain7, mino_pain8 ] {};
|
|
void() mino_pain8 = [ $pain8, mino_run ] {};
|
|
|
|
//----------------------------------------------------------------------
|
|
// (141) Stationary - shake head/arms
|
|
// Block the pain and then start charging like crazy
|
|
void() mino_shake1 = [ $shake1, mino_shake2 ] {self.pain_finished = time + 2;};
|
|
void() mino_shake2 = [ $shake3, mino_shake3 ] {};
|
|
void() mino_shake3 = [ $shake5, mino_shake4 ] {};
|
|
void() mino_shake4 = [ $shake7, mino_shake5 ] {};
|
|
void() mino_shake5 = [ $shake9, mino_shake6 ] {};
|
|
void() mino_shake6 = [ $shake11, mino_shake7 ] {};
|
|
void() mino_shake7 = [ $shake13, mino_shake8 ] {};
|
|
void() mino_shake8 = [ $shake15, mino_shake9 ] {};
|
|
void() mino_shake9 = [ $shake17, mino_shake10 ] {};
|
|
void() mino_shake10 = [ $shake19, mino_run ] {
|
|
// If pre-rage and can see enemy, start chasing
|
|
if (self.attack_rage == FALSE && infront(self.enemy)) self.think = mino_charge;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity inflictor, entity attacker, float damage) mino_pain =
|
|
{
|
|
// Check all pain conditions and set up what to do next
|
|
monster_pain_check(attacker, damage);
|
|
minotaur_finish_attachment();
|
|
|
|
// Any pain animation/sound required?
|
|
if (self.pain_check > 0) {
|
|
self.lip = random();
|
|
if (self.lip < 0.5) sound (self, CHAN_VOICE, self.pain_sound, 1, ATTN_NORM);
|
|
else sound (self, CHAN_VOICE, self.pain_sound2, 1, ATTN_NORM);
|
|
|
|
if (self.pain_check == 1) {
|
|
// Minion spawning minotaur always goes short pain cycle
|
|
if (self.spawnflags & MON_MINOTAUR_MINIONS) mino_pain1();
|
|
else {
|
|
if (self.lip < 0.4) mino_pain1();
|
|
else mino_shake1();
|
|
}
|
|
}
|
|
else if (self.pain_check == 2) {
|
|
// reset axe hit and setup short pain recovery
|
|
self.pain_finished = time + 0.8;
|
|
self.axhitme = 0;
|
|
mino_pain1();
|
|
}
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
void() mino_death1 = [ $death1, mino_death2 ] {};
|
|
void() mino_death2 = [ $death3, mino_death3 ] {ai_forward(2);monster_check_gib();};
|
|
void() mino_death3 = [ $death5, mino_death4 ] {ai_forward(4);monster_check_gib();
|
|
self.solid = SOLID_NOT;};
|
|
void() mino_death4 = [ $death7, mino_death5 ] {ai_forward(8);};
|
|
void() mino_death5 = [ $death9, mino_death6 ] {ai_forward(10);};
|
|
void() mino_death6 = [ $death11, mino_death7 ] {ai_forward(11);};
|
|
void() mino_death7 = [ $death13, mino_death8 ] {ai_forward(11);};
|
|
void() mino_death8 = [ $death15, mino_death9 ] {ai_forward(10);};
|
|
void() mino_death9 = [ $death17, mino_death10 ] {ai_forward(8);};
|
|
void() mino_death10 = [ $death19, mino_death11 ] {ai_forward(4);};
|
|
void() mino_death11 = [ $death21, mino_death12 ] {ai_forward(7);};
|
|
void() mino_death12 = [ $death23, mino_death13 ] {ai_forward(4);};
|
|
void() mino_death13 = [ $death25, mino_death14 ] {monster_death_postcheck();};
|
|
void() mino_death14 = [ $death25, mino_death14 ] {monster_deadbody_check();};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() mino_die =
|
|
{
|
|
// Pre-check routine to tidy up extra entities
|
|
monster_death_precheck();
|
|
minotaur_remove_attachment();
|
|
//delete_minionspace();
|
|
|
|
// regular death
|
|
if (!self.gibbed) {
|
|
sound (self, CHAN_VOICE, "minotaur/death.wav", 1, ATTN_NORM);
|
|
mino_death1 ();
|
|
}
|
|
};
|
|
|
|
/*======================================================================
|
|
QUAKED monster_minotaur (1 0 0) (-32 -32 -24) (32 32 64) Ambush
|
|
======================================================================*/
|
|
void() monster_minotaur =
|
|
{
|
|
if (deathmatch) { remove(self); return; }
|
|
|
|
self.mdl = "progs/mon_minotaur.mdl";
|
|
self.headmdl = "progs/h_minotaur.mdl"; // Large head
|
|
self.gib1mdl = "progs/gib_minoclaw1.mdl"; // Left claw
|
|
self.gib2mdl = "progs/gib_minoclaw2.mdl"; // Right claw
|
|
self.gib3mdl = "progs/gib_minoleg1.mdl"; // foot
|
|
self.gib4mdl = "progs/gib_minoleg2.mdl"; // foot
|
|
|
|
precache_model (self.mdl);
|
|
precache_model (self.headmdl);
|
|
precache_model (MODEL_PROJ_MBALL); // Creating attachment
|
|
precache_model (MODEL_PROJ_MPLASMA); // Plasma bolt
|
|
precache_model (MODEL_PROJ_MPOISON); // Poison bolt
|
|
precache_model (self.gib1mdl);
|
|
precache_model (self.gib2mdl);
|
|
precache_model (self.gib3mdl);
|
|
precache_model (self.gib4mdl);
|
|
|
|
// Randomly swap claws and legs around
|
|
self.lip = random();
|
|
if (self.lip < 0.25) self.gib1mdl = self.gib4mdl;
|
|
else if (self.lip < 0.5) self.gib2mdl = self.gib4mdl;
|
|
else if (self.lip < 0.75) self.gib3mdl = self.gib4mdl;
|
|
|
|
self.idle_sound = "minotaur/idle1.wav";
|
|
self.idle_sound2 = "minotaur/idle2.wav";
|
|
self.idle_soundcom = "minotaur/idle1com.wav";
|
|
self.idle_soundcom2 = "minotaur/idle2com.wav";
|
|
precache_sound (self.idle_sound);
|
|
precache_sound (self.idle_sound2);
|
|
precache_sound (self.idle_soundcom);
|
|
precache_sound (self.idle_soundcom2);
|
|
|
|
precache_sound ("minotaur/swipe.wav"); // claw misses
|
|
precache_sound ("minotaur/strike1.wav"); // claw hit1
|
|
precache_sound ("minotaur/strike2.wav"); // claw hit2
|
|
|
|
precache_sound ("minotaur/attack1start.wav"); // Short roar
|
|
precache_sound ("minotaur/attack1end.wav"); // Short roar
|
|
precache_sound ("minotaur/attack1long.wav"); // Enter rage mode
|
|
precache_sound ("minotaur/bolt_fire.wav"); // fire projectile
|
|
precache_sound (SOUND_PLASMA_HIT); // impact sounds
|
|
|
|
precache_sound ("minotaur/death.wav");
|
|
self.pain_sound = "minotaur/pain1.wav";
|
|
self.pain_sound2 = "minotaur/pain2.wav";
|
|
precache_sound (self.pain_sound);
|
|
precache_sound (self.pain_sound2);
|
|
|
|
self.sight_sound = "minotaur/sight1.wav";
|
|
precache_sound (self.sight_sound);
|
|
|
|
self.solid = SOLID_NOT; // No interaction with world
|
|
self.movetype = MOVETYPE_NONE; // Static item, no movement
|
|
if (self.bboxtype < 1) self.bboxtype = BBOX_GIANT;
|
|
if (self.health < 1) self.health = 500;
|
|
self.gibhealth = -60; // Same as Shambler
|
|
self.gibbed = FALSE; // Still in one piece
|
|
self.pain_flinch = 200; // Demon level
|
|
self.pain_longanim = TRUE; // can be chopped with shadow axe
|
|
self.pain_timeout = 2; // Does not repeat pain
|
|
// Double damage to other monsters
|
|
if (!self.infightextra) self.infightextra = 2;
|
|
self.steptype = FS_TYPELARGE; // Giant sound
|
|
self.blockudeath = TRUE; // No humanoid death
|
|
self.part_style = PARTICLE_BURST_BLUE;
|
|
self.poisonous = FALSE; // blue ver = Always poison free
|
|
self.deathstring = " was gored by a Minotaur\n";
|
|
|
|
// Always reset Ammo Resistance to be consistent
|
|
self.resist_shells = self.resist_nails = 0;
|
|
self.resist_rockets = 0; self.resist_cells = 0.5;
|
|
self.reflectlightning = TRUE; // Reflect lightning strikes
|
|
self.reflectplasma = TRUE; // Reflect plasma projectiles
|
|
|
|
self.th_checkattack = MinotaurCheckAttack;
|
|
self.th_stand = mino_stand1;
|
|
self.th_walk = mino_walk1;
|
|
self.th_run = mino_run;
|
|
self.th_melee = mino_melee;
|
|
self.th_missile = mino_magic;
|
|
self.th_pain = mino_pain;
|
|
self.th_die = mino_die;
|
|
self.th_jump = mino_jump1;
|
|
self.th_jumpexit = mino_jump10;
|
|
self.th_charge = mino_howl1;
|
|
|
|
self.classtype = CT_MONMINOTAUR;
|
|
self.classgroup = CG_DEMON;
|
|
self.classmove = MON_MOVEWALK;
|
|
|
|
// Special darker version for gargoyle spawner
|
|
// Update all gib models and set poison debuff
|
|
if (self.spawnflags & MON_MINOTAUR_MINIONS) {
|
|
self.poisonous = TRUE; // darker ver = Always poisonous
|
|
precache_poisongibs(); // precache gibs
|
|
self.gibtype = GIBTYPE_POISON; // Poisonous blood trails
|
|
self.exactskin = 1; // Black skin + yellow eyes
|
|
self.gib1skin = self.gib2skin = self.gib3skin = 1;
|
|
self.part_style = PARTICLE_BURST_GREEN;
|
|
// Reduce minions down to 3, gargoyles are tougher to kill
|
|
if (!self.minion_maxcount) self.minion_maxcount = 3;
|
|
}
|
|
|
|
monster_start();
|
|
};
|