Files
quakemapping/mod_ad_dev/my_progs/mon_minotaur.qc
2019-12-30 22:24:44 +01:00

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();
};