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

484 lines
18 KiB
Plaintext

/*=============================================================================
PYRO - Originally from Rubicon2 by John Fitzgibbons
FUMIGATOR - Green/Poison version of the Pyro
==============================================================================*/
$cd id1/models/enforcer
$origin 0 -6 24
$base base
$skin skin
$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7
$frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8 walk9 walk10
$frame walk11 walk12 walk13 walk14 walk15 walk16
$frame run1 run2 run3 run4 run5 run6 run7 run8
$frame attack1 attack2 attack3 attack4 attack5 attack6
$frame attack7 attack8 attack9 attack10
$frame death1 death2 death3 death4 death5 death6 death7 death8
$frame death9 death10 death11 death12 death13 death14
$frame fdeath1 fdeath2 fdeath3 fdeath4 fdeath5 fdeath6 fdeath7 fdeath8
$frame fdeath9 fdeath10 fdeath11
$frame paina1 paina2 paina3 paina4
$frame painb1 painb2 painb3 painb4 painb5
$frame painc1 painc2 painc3 painc4 painc5 painc6 painc7 painc8
//----------------------------------------------------------------------
void() pyro_soundreset =
{
//turn off flame sound
if (self.attack_finished > time)
sound (self, CHAN_WEAPON, self.noise4, 1, ATTN_NORM);
//play sound anyway to be sure the channel is silent
else sound (self, CHAN_WEAPON, SOUND_EMPTY, 0, ATTN_NORM);
};
//============================================================================
void() pyro_stand1 =[ $stand1, pyro_stand2 ] {monster_idle_sound(); ai_stand();};
void() pyro_stand2 =[ $stand2, pyro_stand3 ] {ai_stand();};
void() pyro_stand3 =[ $stand3, pyro_stand4 ] {ai_stand();};
void() pyro_stand4 =[ $stand4, pyro_stand5 ] {ai_stand();};
void() pyro_stand5 =[ $stand5, pyro_stand6 ] {ai_stand();};
void() pyro_stand6 =[ $stand6, pyro_stand7 ] {ai_stand();};
void() pyro_stand7 =[ $stand7, pyro_stand1 ] {ai_stand();};
//============================================================================
void() pyro_walk1 =[ $walk1, pyro_walk2 ] {monster_idle_sound();ai_walk(2);};
void() pyro_walk2 =[ $walk2, pyro_walk3 ] {monster_footstep(FALSE); ai_walk(4);};
void() pyro_walk3 =[ $walk3, pyro_walk4 ] {ai_walk(4);};
void() pyro_walk4 =[ $walk4, pyro_walk5 ] {ai_walk(3);};
void() pyro_walk5 =[ $walk5, pyro_walk6 ] {ai_walk(1);};
void() pyro_walk6 =[ $walk6, pyro_walk7 ] {ai_walk(2);};
void() pyro_walk7 =[ $walk7, pyro_walk8 ] {ai_walk(2);};
void() pyro_walk8 =[ $walk8, pyro_walk9 ] {ai_walk(1);};
void() pyro_walk9 =[ $walk9, pyro_walk10 ] {ai_walk(2);};
void() pyro_walk10 =[ $walk10, pyro_walk11 ] {monster_footstep(FALSE); ai_walk(4);};
void() pyro_walk11 =[ $walk11, pyro_walk12 ] {ai_walk(4);};
void() pyro_walk12 =[ $walk12, pyro_walk13 ] {ai_walk(1);};
void() pyro_walk13 =[ $walk13, pyro_walk14 ] {ai_walk(2);};
void() pyro_walk14 =[ $walk14, pyro_walk15 ] {ai_walk(3);};
void() pyro_walk15 =[ $walk15, pyro_walk16 ] {ai_walk(4);};
void() pyro_walk16 =[ $walk16, pyro_walk1 ] {ai_walk(2);};
//============================================================================
float pyro_RUN_SPEED = 1.5;
void() pyro_run1 =[ $run1, pyro_run2 ] {monster_idle_sound(); ai_run(18 * pyro_RUN_SPEED);};
void() pyro_run2 =[ $run2, pyro_run3 ] {ai_run(14 * pyro_RUN_SPEED);};
void() pyro_run3 =[ $run3, pyro_run4 ] {ai_run(7 * pyro_RUN_SPEED);};
void() pyro_run4 =[ $run4, pyro_run5 ] {ai_run(12 * pyro_RUN_SPEED);};
void() pyro_run5 =[ $run5, pyro_run6 ] {ai_run(14 * pyro_RUN_SPEED);};
void() pyro_run6 =[ $run6, pyro_run7 ] {ai_run(14 * pyro_RUN_SPEED);};
void() pyro_run7 =[ $run7, pyro_run8 ] {ai_run(7 * pyro_RUN_SPEED);};
void() pyro_run8 =[ $run8, pyro_run1 ] {ai_run(11 * pyro_RUN_SPEED);};
//============================================================================
// - Pyro enforcer (FIRE FIRE FIRE!)
//----------------------------------------------------------------------
void() pyroflame_touch =
{
if (other == self.owner) return;
if (other.solid == SOLID_TRIGGER) return;
if (other.health < 1) return;
if (other.takedamage == DAMAGE_NO) return;
if (self.attack_finished > time) return;
if (other.classtype == self.owner.classtype) return; // Pyro proof!
// Stop still if hit world, carry on if burning something
if (other != world) self.velocity = self.movedir;
else self.velocity = '0 0 0';
// Block sprite burst from damage again
self.attack_finished = time + 1;
// Check owner is a Pyro?
if (self.owner.classtype == CT_MONPYRO) {
// damage and debuff are same function
ApplyFireDmg(other, DAMAGE_PYROFLAME, DAMAGE_MONFLAME);
}
else {
// Can only poison the player
if (other.flags & FL_CLIENT) {
// Check if player is posioned already?
if (other.poisoned) T_Damage (other, self, self, DAMAGE_FUMPOISON, DAMARMOR);
// Make sure player is posioned
else PoisonDeBuff(other);
}
// Poison anything else (very quick death, high damage)
else T_Damage (other, self, self, DAMAGE_MONFLAME, DAMARMOR);
}
};
//----------------------------------------------------------------------
void() pyroflame_think =
{
self.cnt = self.cnt + 1;
if (self.cnt > 6) remove(self);
else {
self.frame = self.cnt;
// Randomly drift upwards and slow down forward movement
self.velocity_z = self.velocity_z + 10 + (random() * 20);
self.velocity = self.velocity - (self.movedir * 0.125);
self.nextthink = time + 0.1;
}
}
//----------------------------------------------------------------------
void(vector org, vector dir) pyroflame_spawn =
{
local vector variance;
local float roll, pointc;
newmis = spawn();
newmis.classtype = CT_TEMPSTREAM;
newmis.classname = "PBurst";
newmis.owner = self;
newmis.frame = newmis.cnt = 0;
// Extra wide flame contact if fighting a monster
if (self.enemy.flags & FL_MONSTER) newmis.movetype = MOVETYPE_FLYMISSILE;
else newmis.movetype = MOVETYPE_FLY; // Standard flame size
newmis.solid = SOLID_TRIGGER; // collision, touch required
// The Pyro and Fumigator have different flame types
// Pyro = fire, Fum = Poison
//-----------------------------------------------
if (self.classtype == CT_MONPYRO) {
// Switch particles between fire and smoke based on contents
pointc = pointcontents(self.origin);
if (pointc <= CONTENT_WATER) {
if (ext_dppart) {
// Use special DP smoke and empty sprite for collision
pointparticles(particleeffectnum(DPP_PYROFLAME2), org, dir, 1);
setmodel (newmis, SBURST_FLAMEDP);
}
else setmodel (newmis, SBURST_SMOKE);
}
else {
if (ext_dppart) {
// Use special DP flames and empty sprite for collision
pointparticles(particleeffectnum(DPP_PYROFLAME1), org, dir, 1);
setmodel (newmis, SBURST_FLAMEDP);
}
else setmodel (newmis, SBURST_FLAME);
}
}
else {
if (ext_dppart) {
// Use special DP flames and empty sprite for collision
pointparticles(particleeffectnum(DPP_PYROFLAME3), org, dir, 1);
setmodel (newmis, SBURST_POISONDP);
}
else setmodel (newmis, SBURST_POISON);
}
setorigin (newmis, org);
setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
// Work out forward speed (tweak based on skill level)
variance = vecrand(0,10,TRUE); // +/- 10 on all axis
newmis.attack_speed = SPEED_PYROFLAME + skill * SPEED_PYROSKILL;
newmis.movedir = (dir * newmis.attack_speed) + '0 0 -50' + variance;
newmis.velocity = newmis.movedir;
// Roll the flames from side to side towards the target
roll = 350 + random() * 20;
newmis.angles_z = anglemod(roll);
// If DP engine active remove particle shadow
if (engine == ENG_DPEXT) newmis.effects = newmis.effects + EF_NOSHADOW;
newmis.nextthink = time + 0.1;
newmis.think = pyroflame_think;
newmis.touch = pyroflame_touch;
};
//----------------------------------------------------------------------
void() pyro_fire =
{
local vector org, dir, targ_org;
if (self.health < 1) return;
makevectors (self.angles);
org = self.origin + attack_vector(self.attack_offset);
// point the flames at the feet of the player
if (self.enemy.flags & FL_CLIENT) targ_org = self.enemy.origin - '0 0 20';
else targ_org = self.enemy.origin - self.enemy.view_ofs;
dir = normalize(targ_org - self.origin);
pyroflame_spawn(org, dir);
};
//----------------------------------------------------------------------
void() pyro_atk1 =[ $attack1, pyro_atk2 ] {ai_face();};
void() pyro_atk2 =[ $attack3, pyro_atk3 ] {ai_face();};
void() pyro_atk3 =[ $attack5, pyro_atk4 ] {ai_face();
sound (self, CHAN_WEAPON, self.noise3, 1, ATTN_NORM); // ON
pyro_fire();
self.attack_finished = time + 0.3;
};
void() pyro_atk4 =[ $attack5, pyro_atk5 ] {ai_face();
pyro_fire();
if (self.attack_finished > time ||
(range(self.enemy) == RANGE_MELEE && self.enemy.health > 0) )
self.think = pyro_atk4;
};
void() pyro_atk5 =[ $attack10, pyro_run1 ] {ai_face();
sound (self, CHAN_WEAPON, self.noise4, 1, ATTN_NORM); // OFF
SUB_CheckRefire (pyro_atk1);
};
//============================================================================
void() pyro_paina1 =[ $paina1, pyro_paina2 ] {};
void() pyro_paina2 =[ $paina2, pyro_paina3 ] {};
void() pyro_paina3 =[ $paina3, pyro_paina4 ] {};
void() pyro_paina4 =[ $paina4, pyro_run1 ] {};
//----------------------------------------------------------------------
void() pyro_painb1 =[ $painb1, pyro_painb2 ] {};
void() pyro_painb2 =[ $painb2, pyro_painb3 ] {};
void() pyro_painb3 =[ $painb3, pyro_painb4 ] {};
void() pyro_painb4 =[ $painb4, pyro_painb5 ] {};
void() pyro_painb5 =[ $painb5, pyro_run1 ] {};
//----------------------------------------------------------------------
void() pyro_painc1 =[ $painc1, pyro_painc2 ] {};
void() pyro_painc2 =[ $painc2, pyro_painc3 ] {};
void() pyro_painc3 =[ $painc3, pyro_painc4 ] {};
void() pyro_painc4 =[ $painc4, pyro_painc5 ] {};
void() pyro_painc5 =[ $painc5, pyro_painc6 ] {};
void() pyro_painc6 =[ $painc6, pyro_painc7 ] {};
void() pyro_painc7 =[ $painc7, pyro_painc8 ] {};
void() pyro_painc8 =[ $painc8, pyro_run1 ] {};
//----------------------------------------------------------------------
void(entity inflictor, entity attacker, float damage) pyro_pain =
{
// Check all pain conditions and set up what to do next
monster_pain_check(attacker, damage);
pyro_soundreset();
// 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);
// Not using long pain animation (need to be agile)
if (self.pain_check == 1) {
// Randomly pick which pain animation to play
if (self.lip < 0.3) pyro_paina1 ();
else if (self.lip < 0.6) pyro_painb1 ();
else pyro_painc1 ();
}
else if (self.pain_check == 2) {
// reset axe hit and setup short pain recovery
self.pain_finished = time + 0.8;
self.axhitme = 0;
pyro_painc1 ();
}
}
};
//============================================================================
void() pyro_die1 =[ $death1, pyro_die2 ] {};
void() pyro_die2 =[ $death2, pyro_die3 ] {monster_check_gib();};
void() pyro_die3 =[ $death3, pyro_die4 ] {monster_check_gib();
self.solid = SOLID_NOT; if (!self.gibbed) DropBackpack();};
void() pyro_die4 =[ $death4, pyro_die5 ] {ai_forward(14);};
void() pyro_die5 =[ $death5, pyro_die6 ] {ai_forward(2);};
void() pyro_die6 =[ $death6, pyro_die7 ] {};
void() pyro_die7 =[ $death7, pyro_die8 ] {};
void() pyro_die8 =[ $death8, pyro_die9 ] {};
void() pyro_die9 =[ $death9, pyro_die10 ] {ai_forward(3);};
void() pyro_die10 =[ $death10, pyro_die11 ] {ai_forward(5);};
void() pyro_die11 =[ $death11, pyro_die12 ] {ai_forward(5);};
void() pyro_die12 =[ $death12, pyro_die13 ] {ai_forward(5);};
void() pyro_die13 =[ $death13, pyro_die14 ] {monster_death_postcheck();};
void() pyro_die14 =[ $death14, pyro_die14 ] {monster_deadbody_check();};
//----------------------------------------------------------------------
void() pyro_fdie1 =[ $fdeath1, pyro_fdie2 ] {};
void() pyro_fdie2 =[ $fdeath2, pyro_fdie3 ] {monster_check_gib();};
void() pyro_fdie3 =[ $fdeath3, pyro_fdie4 ] {monster_check_gib();
self.solid = SOLID_NOT; if (!self.gibbed) DropBackpack();};
void() pyro_fdie4 =[ $fdeath4, pyro_fdie5 ] {};
void() pyro_fdie5 =[ $fdeath5, pyro_fdie6 ] {};
void() pyro_fdie6 =[ $fdeath6, pyro_fdie7 ] {};
void() pyro_fdie7 =[ $fdeath7, pyro_fdie8 ] {};
void() pyro_fdie8 =[ $fdeath8, pyro_fdie9 ] {};
void() pyro_fdie9 =[ $fdeath9, pyro_fdie10 ] {};
void() pyro_fdie10 =[ $fdeath10, pyro_fdie11 ] {monster_death_postcheck();};
void() pyro_fdie11 =[ $fdeath11, pyro_fdie11 ] {monster_deadbody_check();};
//----------------------------------------------------------------------
void() pyro_die =
{
// Pre-check routine to tidy up extra entities
monster_death_precheck();
pyro_soundreset();
// regular death
if (!self.gibbed) {
sound (self, CHAN_VOICE, "enforcer/death1.wav", 1, ATTN_NORM);
if (random() > 0.5) pyro_die1 ();
else pyro_fdie1 ();
}
};
/*======================================================================
QUAKED monster_pyro (1 0 0) (-16 -16 -24) (16 16 40) Ambush
======================================================================*/
void() monster_pyro =
{
if (deathmatch) { remove(self); return; }
self.mdl = "progs/mon_pyro.mdl";
self.headmdl = "progs/h_pyro.mdl";
self.gib1mdl = "progs/w_enforcergun.mdl"; // Unique weapon
precache_model (self.mdl);
precache_model (self.headmdl);
precache_model (self.gib1mdl);
self.gib1skin = 3;
precache_sound ("enforcer/death1.wav");
self.pain_sound = "enforcer/pain1.wav";
self.pain_sound2 = "enforcer/pain2.wav";
precache_sound (self.pain_sound);
precache_sound (self.pain_sound2);
self.idle_sound = "enforcer/idle1.wav";
precache_sound (self.idle_sound);
self.sight_sound = "enforcer/sight1.wav";
precache_sound (self.sight_sound);
precache_sound ("enforcer/sight2.wav");
precache_sound ("enforcer/sight3.wav");
precache_sound ("enforcer/sight4.wav");
// Extra sound/sprite assets (from Rubicon2 MOD)
self.noise3 = "enforcer/pyro_flameon.wav";
self.noise4 = "enforcer/pyro_flameoff.wav";
precache_sound (self.noise3);
precache_sound (self.noise4);
precache_model (SBURST_FLAME); // Flame on
precache_model (SBURST_SMOKE); // Underwater
precache_model (SBURST_FLAMEDP); // DP Collision
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_NONE;
if (self.bboxtype < 1) self.bboxtype = BBOX_TALL;
if (self.health < 1) self.health = 100;
self.gibhealth = -35;
self.gibbed = FALSE;
self.yaw_speed = 35; // Can turn really fast
self.pain_flinch = 100; // extremely high pain tolerance
self.pain_longanim = TRUE; // can be chopped with shadow axe
self.steptype = FS_TYPEHEAVY; // heavy feet
self.attack_offset = '24 8.5 16'; // End of Gun
self.ammo_shells = 4; // drops armor shards instead
if (!self.exactskin) self.randomskin = 4;
self.poisonous = FALSE; // Use Fumigator instead
self.deathstring = " was toasted by a Pyro\n";
// Always reset Ammo Resistance to be consistent
self.resist_shells = 0; self.resist_nails = 0.5;
self.resist_rockets = self.resist_cells = 0;
self.th_checkattack = PyroCheckAttack;
self.th_stand = pyro_stand1;
self.th_walk = pyro_walk1;
self.th_run = pyro_run1;
self.th_pain = pyro_pain;
self.th_die = pyro_die;
self.th_melee = pyro_atk1;
self.th_missile = pyro_atk1; // For monster_turret function
self.classtype = CT_MONPYRO;
self.classgroup = CG_ENFORCER;
self.classmove = MON_MOVEWALK;
monster_start();
};
/*======================================================================
QUAKED monster_fumigator (1 0 0) (-16 -16 -24) (16 16 40) Ambush
======================================================================*/
void() monster_fumigator =
{
if (deathmatch) { remove(self); return; }
self.mdl = "progs/mon_fumigator.mdl";
self.headmdl = "progs/h_pyro.mdl";
self.gib1mdl = "progs/w_enforcergun.mdl"; // Unique weapon
precache_model (self.mdl);
precache_model (self.headmdl);
precache_model (self.gib1mdl);
self.gib1sound = GIB_IMPACT_WOOD;
self.gib1skin = 4;
precache_sound ("enforcer/death1.wav");
self.pain_sound = "enforcer/pain1.wav";
self.pain_sound2 = "enforcer/pain2.wav";
precache_sound (self.pain_sound);
precache_sound (self.pain_sound2);
self.idle_sound = "enforcer/idle1.wav";
precache_sound (self.idle_sound);
self.sight_sound = "enforcer/sight1.wav";
precache_sound (self.sight_sound);
precache_sound ("enforcer/sight2.wav");
precache_sound ("enforcer/sight3.wav");
precache_sound ("enforcer/sight4.wav");
// Extra sound/sprite assets (from Rubicon2 MOD)
self.noise3 = "enforcer/fum_poisonon.wav";
self.noise4 = "enforcer/fum_poisonoff.wav";
precache_sound (self.noise3);
precache_sound (self.noise4);
precache_model (SBURST_POISON); // Poison on
precache_model (SBURST_POISONDP); // DP Collision
precache_poisongibs(); // precache gibs
self.gibtype = GIBTYPE_POISON; // Poison gibs!
self.poisonous = TRUE; // Oh yeah! Always!
self.solid = SOLID_NOT;
self.movetype = MOVETYPE_NONE;
if (self.bboxtype < 1) self.bboxtype = BBOX_TALL;
if (self.health < 1) self.health = 100;
self.gibhealth = -35;
self.gibbed = FALSE;
self.yaw_speed = 35; // Can turn really fast
self.pain_flinch = 100; // extremely high pain tolerance
self.pain_longanim = TRUE; // can be chopped with shadow axe
self.steptype = FS_TYPEHEAVY; // heavy feet
self.attack_offset = '24 8.5 16'; // End of Gun
self.ammo_shells = 4; // drops armor shards instead
self.no_liquiddmg = TRUE; // Immune to slime/lava
if (!self.exactskin) self.randomskin = 4;
self.deathstring = " was choked by a Fumigator\n";
// Always reset Ammo Resistance to be consistent
self.resist_shells = 0; self.resist_nails = 0.5;
self.resist_rockets = self.resist_cells = 0;
self.reflectnails = TRUE; // Reflect nail projectiles
self.th_checkattack = PyroCheckAttack;
self.th_stand = pyro_stand1;
self.th_walk = pyro_walk1;
self.th_run = pyro_run1;
self.th_pain = pyro_pain;
self.th_die = pyro_die;
self.th_melee = pyro_atk1;
self.th_missile = pyro_atk1; // For monster_turret function
self.classtype = CT_MONFUMIGATOR;
self.classgroup = CG_ENFORCER;
self.classmove = MON_MOVEWALK;
monster_start();
};