392 lines
14 KiB
Plaintext
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;
|
|
}; |