Files
quakemapping/mod_xj18/my_progs/mon_wizard.qc
2020-01-07 11:54:38 +01:00

392 lines
14 KiB
Plaintext

/*==============================================================================
WIZARD
==============================================================================*/
$cd id1/models/a_wizard
$origin 0 0 24
$base wizbase
$skin wizbase
$frame hover1 hover2 hover3 hover4 hover5 hover6 hover7 hover8
$frame hover9 hover10 hover11 hover12 hover13 hover14 hover15
$frame fly1 fly2 fly3 fly4 fly5 fly6 fly7 fly8 fly9 fly10
$frame fly11 fly12 fly13 fly14
$frame magatt1 magatt2 magatt3 magatt4 magatt5 magatt6 magatt7
$frame magatt8 magatt9 magatt10 magatt11 magatt12 magatt13
$frame pain1 pain2 pain3 pain4
$frame death1 death2 death3 death4 death5 death6 death7 death8
//============================================================================
void() wiz_stand1 =[ $hover1, wiz_stand2 ] {monster_idle_sound(); ai_stand();};
void() wiz_stand2 =[ $hover2, wiz_stand3 ] {ai_stand();};
void() wiz_stand3 =[ $hover3, wiz_stand4 ] {ai_stand();};
void() wiz_stand4 =[ $hover4, wiz_stand5 ] {ai_stand();};
void() wiz_stand5 =[ $hover5, wiz_stand6 ] {ai_stand();};
void() wiz_stand6 =[ $hover6, wiz_stand7 ] {ai_stand();};
void() wiz_stand7 =[ $hover7, wiz_stand8 ] {ai_stand();};
void() wiz_stand8 =[ $hover8, wiz_stand1 ] {ai_stand();};
//============================================================================
void() wiz_walk1 =[ $hover1, wiz_walk2 ] {monster_idle_sound(); ai_walk(8);};
void() wiz_walk2 =[ $hover2, wiz_walk3 ] {ai_walk(8);};
void() wiz_walk3 =[ $hover3, wiz_walk4 ] {ai_walk(8);};
void() wiz_walk4 =[ $hover4, wiz_walk5 ] {ai_walk(8);};
void() wiz_walk5 =[ $hover5, wiz_walk6 ] {ai_walk(8);};
void() wiz_walk6 =[ $hover6, wiz_walk7 ] {ai_walk(8);};
void() wiz_walk7 =[ $hover7, wiz_walk8 ] {ai_walk(8);};
void() wiz_walk8 =[ $hover8, wiz_walk1 ] {ai_walk(8);};
//============================================================================
void() wiz_side1 =[ $hover1, wiz_side2 ] {monster_idle_sound(); ai_run(8);};
void() wiz_side2 =[ $hover2, wiz_side3 ] {ai_run(8);};
void() wiz_side3 =[ $hover3, wiz_side4 ] {ai_run(8);};
void() wiz_side4 =[ $hover4, wiz_side5 ] {ai_run(8);};
void() wiz_side5 =[ $hover5, wiz_side6 ] {ai_run(8);};
void() wiz_side6 =[ $hover6, wiz_side7 ] {ai_run(8);};
void() wiz_side7 =[ $hover7, wiz_side8 ] {ai_run(8);};
void() wiz_side8 =[ $hover8, wiz_side1 ] {ai_run(8);};
//============================================================================
void() wiz_run1 =[ $fly1, wiz_run2 ] {monster_idle_sound(); ai_run(16);};
void() wiz_run2 =[ $fly2, wiz_run3 ] {ai_run(16);};
void() wiz_run3 =[ $fly3, wiz_run4 ] {ai_run(16);};
void() wiz_run4 =[ $fly4, wiz_run5 ] {ai_run(16);};
void() wiz_run5 =[ $fly5, wiz_run6 ] {ai_run(16);};
void() wiz_run6 =[ $fly6, wiz_run7 ] {ai_run(16);};
void() wiz_run7 =[ $fly7, wiz_run8 ] {ai_run(16);};
void() wiz_run8 =[ $fly8, wiz_run9 ] {ai_run(16);};
void() wiz_run9 =[ $fly9, wiz_run10 ] {ai_run(16);};
void() wiz_run10 =[ $fly10, wiz_run11 ] {ai_run(16);};
void() wiz_run11 =[ $fly11, wiz_run12 ] {ai_run(16);};
void() wiz_run12 =[ $fly12, wiz_run13 ] {ai_run(16);};
void() wiz_run13 =[ $fly13, wiz_run14 ] {ai_run(16);};
void() wiz_run14 =[ $fly14, wiz_run1 ] {ai_run(16);};
//============================================================================
// Range attack
//============================================================================
void() WizardAttackFinished =
{
SUB_AttackFinished(2);
if (enemy_range >= RANGE_MID || !enemy_vis) {
self.attack_state = AS_STRAIGHT;
self.think = wiz_run1;
}
else {
self.attack_state = AS_SLIDING;
self.think = wiz_side1;
}
};
//----------------------------------------------------------------------
// Fire spit missile towards player position
// The missile is setup and defined beforehand
//----------------------------------------------------------------------
void() Wiz_FastFire =
{
local vector vec, dst;
local entity twizard, tmissile;
// Is the wizard still alive?
if (self.owner.health > 0) {
// Setup correct entities
tmissile = self;
twizard = self.owner;
// Switch to the actual wizard
self = twizard;
// Add glow effect to wizard while firing
self.effects = self.effects | EF_MUZZLEFLASH;
// Check for any enemytarget setup (above spawnflag)
makevectors (SUB_angEnemyTarget());
// Work out origin and offset from missile, not wizard
dst = SUB_orgEnemyTarget() - 13 * tmissile.movedir;
vec = normalize(dst - tmissile.origin);
self.attack_speed = SPEED_WIZSPIKE + (skill * SPEED_WIZSKILL);
// make sure sound is played on the wizard, not the missile
sound (self, CHAN_WEAPON, "wizard/wattack.wav", 1, ATTN_NORM);
// Launch projectile expects self = firing monster
launch_projectile (tmissile.origin, vec, CT_PROJ_WIZ, self.attack_speed);
// Switch back to missile entity for removal
self = tmissile;
}
remove (self);
};
//----------------------------------------------------------------------
// Create two spit missiles ready for firing later (delay 0.6, 1.0)
// This code is hoping the player will strafe into the second shot
//----------------------------------------------------------------------
void() Wiz_StartFast =
{
local entity missile;
sound (self, CHAN_WEAPON, "wizard/wattack.wav", 1, ATTN_NORM);
self.v_angle = self.angles;
makevectors (self.angles);
missile = spawn ();
missile.owner = self;
missile.nextthink = time + 0.6;
setsize (missile, '0 0 0', '0 0 0');
setorigin (missile, self.origin + self.attack_offset + v_forward*14 + v_right*14);
missile.enemy = SUB_entEnemyTarget();
missile.nextthink = time + 0.8;
missile.think = Wiz_FastFire;
missile.movedir = v_right;
missile = spawn ();
missile.owner = self;
missile.nextthink = time + 1;
setsize (missile, '0 0 0', '0 0 0');
setorigin (missile, self.origin + self.attack_offset + v_forward*14 + v_right* -14);
missile.enemy = SUB_entEnemyTarget();
missile.nextthink = time + 0.3;
missile.think = Wiz_FastFire;
missile.movedir = VEC_ORIGIN - v_right;
};
//----------------------------------------------------------------------
void() wiz_fast1 =[ $magatt1, wiz_fast2 ] {ai_face();Wiz_StartFast();};
void() wiz_fast2 =[ $magatt2, wiz_fast3 ] {ai_face();};
void() wiz_fast3 =[ $magatt3, wiz_fast4 ] {ai_face();};
void() wiz_fast4 =[ $magatt4, wiz_fast5 ] {ai_face();};
void() wiz_fast5 =[ $magatt5, wiz_fast6 ] {ai_face();};
void() wiz_fast6 =[ $magatt6, wiz_fast7 ] {ai_face();};
void() wiz_fast7 =[ $magatt5, wiz_fast8 ] {ai_face();};
void() wiz_fast8 =[ $magatt4, wiz_fast9 ] {ai_face();};
void() wiz_fast9 =[ $magatt3, wiz_fast10] {ai_face();};
void() wiz_fast10 =[ $magatt2, wiz_run1 ] {WizardAttackFinished (); ai_face();};
//============================================================================
void() wiz_wakeup =[ $hover1, wiz_run1 ] {
// Is the wizard stuck? cannot move?
if (pointcontents(self.origin) == CONTENT_SOLID) {
// Time to die!
self.health = self.gibhealth;
Killed(self, self);
}
else {
monster_sightsound();
// Setup goals and warn other monsters
FoundHuntTarget(TRUE);
// Restore all think state functions
self.th_stand = wiz_stand1;
self.th_walk = wiz_walk1;
self.th_run = wiz_run1;
self.th_missile = wiz_fast1;
self.th_slide = wiz_side1;
}
};
//============================================================================
void() wiz_pain1 =[ $pain1, wiz_pain2] {};
void() wiz_pain2 =[ $pain2, wiz_pain3] {};
void() wiz_pain3 =[ $pain3, wiz_pain4] {};
void() wiz_pain4 =[ $pain4, wiz_run1 ] {};
//----------------------------------------------------------------------
void(entity inflictor, entity attacker, float damage) wiz_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) wiz_pain1 ();
else if (self.pain_check == 2) {
// reset axe hit and setup short pain recovery
self.pain_finished = time + 0.4;
self.axhitme = 0;
wiz_pain1 ();
}
}
};
//============================================================================
void() wiz_death1 =[ $death1, wiz_death2 ] {};
void() wiz_death2 =[ $death2, wiz_death3 ] {monster_check_gib();};
void() wiz_death3 =[ $death3, wiz_death4 ] {monster_check_gib();
self.solid = SOLID_NOT;};
void() wiz_death4 =[ $death4, wiz_death5 ] {};
void() wiz_death5 =[ $death5, wiz_death6 ] {};
void() wiz_death6 =[ $death6, wiz_death7 ] {};
void() wiz_death7 =[ $death7, wiz_death8 ] {monster_death_postcheck();};
void() wiz_death8 =[ $death8, wiz_death8 ] {monster_deadbody_check();};
//----------------------------------------------------------------------
void() wiz_die =
{
// Pre-check routine to tidy up extra entities
monster_death_precheck();
if (!self.gibbed) {
sound (self, CHAN_VOICE, "wizard/wdeath.wav", 1, ATTN_NORM);
self.velocity_x = -200 + 400*random();
self.velocity_y = -200 + 400*random();
self.velocity_z = 100 + 100*random();
self.flags = self.flags - (self.flags & FL_ONGROUND);
wiz_death1 ();
}
};
/*======================================================================
QUAKED monster_wizard (1 0 0) (-16 -16 -24) (16 16 40) Ambush
======================================================================*/
void() setup_wizard;
void() monster_wizard =
{
if (deathmatch) { remove(self); return; }
self.mdl = "progs/mon_wizard.mdl";
self.headmdl = "progs/h_wizard.mdl";
self.gib1mdl = "progs/gib_wzarm1.mdl"; // Left arm/stump
self.gib2mdl = "progs/gib_wzarm2.mdl"; // Right arm/stump
self.gib3mdl = "progs/gib_wztail.mdl"; // Tail section
precache_model (self.mdl);
precache_model (self.headmdl);
precache_model (MODEL_PROJ_WIZ); // Originally progs/w_spike.mdl
precache_model (self.gib1mdl);
precache_model (self.gib2mdl);
precache_model (self.gib3mdl);
self.idle_sound = "wizard/widle1.wav";
self.idle_sound2 = "wizard/widle2.wav";
precache_sound (self.idle_sound);
precache_sound (self.idle_sound2);
precache_sound ("wizard/wdeath.wav");
self.pain_sound = "wizard/wpain.wav";
precache_sound (self.pain_sound);
precache_sound ("wizard/hit.wav"); // used by c code
precache_sound ("wizard/wattack.wav");
self.sight_sound = "wizard/wsight.wav";
precache_sound (self.sight_sound);
// Check for poisonous entity flag
if (self.poisonous) {
precache_poisongibs(); // precache gibs
self.gibtype = GIBTYPE_POISON; // Poisonous blood trails
self.exactskin = 1; // Greener version
self.gib1skin = self.gib2skin = self.gib3skin = 1;
}
// Cache gargoyle is a special class used for precache only
if (self.classtype != CT_CACHEWIZARD) setup_wizard();
};
//----------------------------------------------------------------------------
void() monster_nourminion = {
self.classtype = CT_CACHEWIZARD;
self.poisonous = TRUE; // Cache poison stuff (just in case)
monster_wizard();
};
//----------------------------------------------------------------------------
void() setup_wizard =
{
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 = 80;
self.gibhealth = -30;
self.pain_flinch = 70; // high pain threshold
self.pain_longanim = TRUE; // Has long pain animation for Shadow Axe
self.blockudeath = TRUE; // no humanoid death sound
if (self.height < 1) self.height = MONAI_ABOVEDIST; // Custom height
self.attack_offset = '0 0 30'; // Acid spit start location
self.deathstring = " was wrecked by a Wizard\n";
// Always reset Ammo Resistance to be consistent
self.resist_shells = self.resist_nails = 0;
self.resist_rockets = self.resist_cells = 0;
// Allow wizards to keep a certain distance above the enemy
if (self.spawnflags & MON_WIZARD_ABOVE) {
self.th_checkattack = GargoyleCheckAttack;
}
else {
// Default behaviour, really high distance checks
self.th_checkattack = WizardCheckAttack;
self.enemymaxdist = TRUE;
}
self.th_pain = wiz_pain;
self.th_die = wiz_die;
if(!self.classtype) self.classtype = CT_MONWIZARD;
if (!self.classgroup) self.classgroup = CG_WIZARD;
self.classmove = MON_MOVEFLY;
if (self.classtype == CT_MINIONWIZARD) {
self.th_stand = self.th_walk = self.th_run = wiz_wakeup;
self.th_missile = self.th_slide = wiz_wakeup;
}
else {
self.th_stand = wiz_stand1;
self.th_walk = wiz_walk1;
self.th_run = wiz_run1;
self.th_missile = wiz_fast1;
self.th_slide = wiz_side1;
}
monster_start();
};
//----------------------------------------------------------------------------
// A code way to spawn wizards (requires monster_nourminion entity)
//----------------------------------------------------------------------------
void(vector minion_org, entity minion_targ, float minion_sflag) minion_wizard =
{
local entity minion;
// Check if there is space to spawn entity
if (entity_pcontent(minion_org)) return;
// Self = minotaur, there is no egg
update_minioncount(self, 1); // Update spawn counters
minion = spawn();
minion.classname = "monster_wizard"; // For function searching
setorigin(minion, minion_org); // Move to new location
minion.owner = self; // Spawner Parent Link
minion.effects = minion.flags = 0; // make sure are blank
minion.gibondeath = 1; // Always gib on death
minion.classtype = CT_MINIONWIZARD; // Special minion class
minion.enemy = minion_targ; // Target to attack
minion.spawnflags = minion_sflag; // Spawnflags
minion.classgroup = CG_WIZARD; // Don't turn on master
minion.minion_active = TRUE; // Minion flag
minion.bodyfadeaway = TRUE; // Get rid of body
// Check for poison gibs being cached first
if (gibpoison && self.poisonous == TRUE) {
minion.poisonous = self.poisonous; // carry over from host spawner
minion.gibtype = GIBTYPE_POISON; // Poisonous blood trails
}
minion.mdl = "progs/mon_wizard.mdl";
minion.headmdl = "progs/h_wizard.mdl";
minion.gib1mdl = "progs/gib_wzarm1.mdl"; // Left arm/stump
minion.gib2mdl = "progs/gib_wzarm2.mdl"; // Right arm/stump
minion.gib3mdl = "progs/gib_wztail.mdl"; // Tail section
minion.pain_sound = "wizard/wpain.wav";
minion.idle_sound = "wizard/widle1.wav";
minion.idle_sound2 = "wizard/widle2.wav";
minion.sight_sound = "wizard/wsight.wav";
minion.nextthink = time + 0.01;
minion.think = setup_wizard;
};