908 lines
34 KiB
Plaintext
908 lines
34 KiB
Plaintext
/*==============================================================================
|
|
WRAITH from Rogue Expansion Pack
|
|
==============================================================================*/
|
|
$skin wrthskin
|
|
|
|
// Base animation pose
|
|
$frame basepose
|
|
|
|
// Walk/Idle pose (strange lerp at the end of frame set)
|
|
$frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8
|
|
$frame stand9 stand10 stand11 stand12
|
|
|
|
// Magic Attack A - Swing arms forward with hand meeting together
|
|
$frame magicA1 magicA2 magicA3 magicA4 magicA5 magicA6 magicA7 magicA8
|
|
$frame magicA9 magicA10 magicA11 magicA12 magicA13 magicA14
|
|
|
|
// Magic Attack B - Cup hands around a missile object infront and fire
|
|
$frame magicB1 magicB2 magicB3 magicB4 magicB5 magicB6 magicB7 magicB8
|
|
$frame magicB9 magicB10 magicB11 magicB12 magicB13
|
|
|
|
// Magic Attack C - Swing right arm forward firing a projectile
|
|
$frame magicC1 magicC2 magicC3 magicC4 magicC5 magicC6 magicC7 magicC8
|
|
$frame magicC9 magicC10 magicC11 magicC12 magicC13 magicC14 magicC15
|
|
|
|
// Pain A - Short flinch backwards
|
|
$frame painA1 painA2 painA3 painA4 painA5 painA6
|
|
|
|
// Pain B - Long motion of being hit several times
|
|
$frame painB1 painB2 painB3 painB4 painB5 painB6 painB7 painB8
|
|
$frame painB9 painB10 painB11
|
|
|
|
// Death - Shrink down and explode
|
|
$frame death1 death2 death3 death4 death5 death6 death7 death8
|
|
$frame death9 death10 death11 death12 death13 death14 death15
|
|
|
|
float BONEOFFSET = 8;
|
|
float WRAITH_ALPHA = 0.7;
|
|
float WRAITH_ALPHAFIRE = 1;
|
|
float BALL_NAIL_COL1 = 0; // Red
|
|
float BALL_NAIL_COL2 = 1; // Sickly yellow
|
|
float BALL_SPIDER_COL1 = 1; // Sickly yellow
|
|
float BALL_DEBUFF_COL1 = 3; // Glowing yellow/red
|
|
float BALL_DEBUFF_COL2 = 3; // Glowing yellow/red
|
|
float FADEDEBUFF_TIME = 0.01; // Small amount of time
|
|
float WRAITH_SKINBLUE = 0; // Fire wraith
|
|
float WRAITH_SKINGREEN = 2; // Spider version
|
|
float WRAITH_SKINPURPLE = 4; // Scorpion version
|
|
|
|
//======================================================================
|
|
void() wraith_stand1 =[ $stand4, wraith_stand2 ] { monster_idle_sound();ai_stand();};
|
|
void() wraith_stand2 =[ $stand5, wraith_stand3 ] { ai_stand();};
|
|
void() wraith_stand3 =[ $stand6, wraith_stand4 ] { ai_stand();};
|
|
void() wraith_stand4 =[ $stand7, wraith_stand5 ] { ai_stand();};
|
|
void() wraith_stand5 =[ $stand8, wraith_stand6 ] { ai_stand();};
|
|
void() wraith_stand6 =[ $stand9, wraith_stand7 ] { ai_stand();};
|
|
void() wraith_stand7 =[ $stand9, wraith_stand8 ] { ai_stand();};
|
|
void() wraith_stand8 =[ $stand8, wraith_stand9 ] { ai_stand();};
|
|
void() wraith_stand9 =[ $stand7, wraith_stand10 ] { ai_stand();};
|
|
void() wraith_stand10 =[ $stand6, wraith_stand11 ] { ai_stand();};
|
|
void() wraith_stand11 =[ $stand5, wraith_stand12 ] { ai_stand();};
|
|
void() wraith_stand12 =[ $stand4, wraith_stand1 ] { ai_stand();};
|
|
|
|
//======================================================================
|
|
void() wraith_walk1 =[ $stand3, wraith_walk2 ] { monster_idle_sound();ai_walk(8);};
|
|
void() wraith_walk2 =[ $stand4, wraith_walk3 ] { ai_walk(7);};
|
|
void() wraith_walk3 =[ $stand5, wraith_walk4 ] { ai_walk(7);};
|
|
void() wraith_walk4 =[ $stand6, wraith_walk5 ] { ai_walk(6);};
|
|
void() wraith_walk5 =[ $stand7, wraith_walk6 ] { ai_walk(6);};
|
|
void() wraith_walk6 =[ $stand8, wraith_walk7 ] { ai_walk(5);};
|
|
void() wraith_walk7 =[ $stand9, wraith_walk8 ] { ai_walk(5);};
|
|
void() wraith_walk8 =[ $stand10, wraith_walk9 ] { ai_walk(4);};
|
|
void() wraith_walk9 =[ $stand11, wraith_walk10 ] { ai_walk(8);};
|
|
void() wraith_walk10 =[ $stand12, wraith_walk1 ] { ai_walk(8);};
|
|
|
|
//======================================================================
|
|
void() wraith_run1 =[ $stand3, wraith_run2 ] {monster_idle_sound();
|
|
if (self.meleeattack < time) {
|
|
self.meleeattack = time + 4 + (random() + random() + random() * 4);
|
|
self.lefty = rint(1 - self.lefty);
|
|
}
|
|
ai_run(8);
|
|
};
|
|
void() wraith_run2 =[ $stand4, wraith_run3 ] {ai_run(7);};
|
|
void() wraith_run3 =[ $stand5, wraith_run3 ] {ai_run(7);};
|
|
void() wraith_run4 =[ $stand6, wraith_run3 ] {ai_run(6);};
|
|
void() wraith_run5 =[ $stand7, wraith_run3 ] {ai_run(6);};
|
|
void() wraith_run6 =[ $stand8, wraith_run3 ] {ai_run(5);};
|
|
void() wraith_run7 =[ $stand9, wraith_run3 ] {ai_run(5);};
|
|
void() wraith_run8 =[ $stand10, wraith_run3 ] {ai_run(4);};
|
|
void() wraith_run9 =[ $stand11, wraith_run3 ] {ai_run(8);};
|
|
void() wraith_run10 =[ $stand12, wraith_run3 ] {ai_run(8);};
|
|
|
|
//============================================================================
|
|
// Attachment management (create, finish and delete)
|
|
//============================================================================
|
|
void() wraith_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_WBALL;
|
|
self.attachment.alpha = 0.85;
|
|
}
|
|
if (!self.attachment2) {
|
|
self.attachment2 = spawn();
|
|
self.attachment2.owner = self;
|
|
self.attachment2.classtype = CT_ATTACHMENT;
|
|
self.attachment2.mdl = MODEL_PROJ_WBALL;
|
|
self.attachment2.alpha = 0.85;
|
|
}
|
|
if (!self.attachment3) {
|
|
self.attachment3 = spawn();
|
|
self.attachment3.owner = self;
|
|
self.attachment3.classtype = CT_ATTACHMENT;
|
|
self.attachment3.mdl = self.weaponglow;
|
|
self.attachment3.alpha = 0.5;
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() wraith_finish_attachment =
|
|
{
|
|
if (self.attachment) {
|
|
setmodel(self.attachment, "");
|
|
self.attachment.state = STATE_OFF;
|
|
}
|
|
if (self.attachment2) {
|
|
setmodel(self.attachment2, "");
|
|
self.attachment2.state = STATE_OFF;
|
|
}
|
|
if (self.attachment3) {
|
|
setmodel(self.attachment3, "");
|
|
self.attachment3.state = STATE_OFF;
|
|
self.attachment3.velocity = '0 0 0';
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() wraith_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;
|
|
}
|
|
if (self.attachment3) {
|
|
self.attachment3.think = SUB_Remove;
|
|
self.attachment3.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
//======================================================================
|
|
// FADE OUT TEMPORARY MODELS
|
|
// Very quick fade (0.5s) - modified version of model_fade
|
|
//======================================================================
|
|
void() debuff_fade =
|
|
{
|
|
self.alpha = 0.5 - ((time - self.ltime) / 0.5);
|
|
|
|
if (self.alpha < 0.2) {
|
|
self.alpha = 0;
|
|
self.modelindex = 0; // Make sure no model
|
|
self.model = ""; // hide model
|
|
self.think = SUB_Remove;
|
|
self.nextthink = time + 0.1;
|
|
return;
|
|
}
|
|
self.nextthink = time + FADEDEBUFF_TIME;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Start fading out (very quick, over 0.5s)
|
|
//----------------------------------------------------------------------
|
|
void() wraith_debuff_chunk =
|
|
{
|
|
self.think = debuff_fade;
|
|
self.nextthink = time + FADEDEBUFF_TIME;
|
|
self.ltime = self.nextthink;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Manual animation of ball at end of the debuff particle trail
|
|
//----------------------------------------------------------------------
|
|
void() wraith_debuff_shrink =
|
|
{
|
|
// Count downwards getting smaller
|
|
self.lip = self.frame - 1;
|
|
if (self.lip < 0) wraith_debuff_chunk();
|
|
else {
|
|
// Check frame number before assigning
|
|
// Cannot set models with negative frame numbers
|
|
self.frame = self.lip;
|
|
self.think = wraith_debuff_shrink;
|
|
self.nextthink = time + 0.1;
|
|
}
|
|
};
|
|
|
|
//======================================================================
|
|
// RANGE ATTACK 1 - Apply debuff to player (hitscan!)
|
|
//
|
|
// Generate an animated particle trail from 32 unit segments
|
|
// Slowly remove segments from source to target
|
|
// Generate a ball at target location slowly shrinking
|
|
//======================================================================
|
|
void() wraith_fire_touch =
|
|
{
|
|
if (other.solid == SOLID_TRIGGER) return;
|
|
if (other.classtype == CT_TEMPSTREAM) return;
|
|
if (other.health > 0) return;
|
|
if (self.attack_finished > time) return;
|
|
|
|
self.attack_finished = time + 1;
|
|
self.modelindex = 0; // Make sure no model
|
|
self.model = ""; // hide model
|
|
self.think = SUB_Remove;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() wraith_fire_think =
|
|
{
|
|
// Randomly drift upwards and slow down forward movement
|
|
self.velocity_z = self.velocity_z + 10 + (random() * 20);
|
|
self.velocity = self.velocity - (self.velocity * 0.125);
|
|
|
|
self.frame = self.frame + 1;
|
|
if (self.frame > self.count) SUB_Remove();
|
|
else self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(vector start_org) wraith_debuff_explode =
|
|
{
|
|
local float fire_loop;
|
|
local vector fire_angles, org, dir;
|
|
|
|
fire_loop = 0;
|
|
fire_angles = '0 0 0';
|
|
|
|
while (fire_loop < 360) {
|
|
newmis = spawn();
|
|
newmis.classtype = CT_TEMPSTREAM;
|
|
newmis.movetype = MOVETYPE_FLY; // Fly, no gravity
|
|
newmis.solid = SOLID_TRIGGER;
|
|
setmodel(newmis, SBURST_FLAME);
|
|
|
|
org = start_org;
|
|
org_z = org_z + (crandom() * 24);
|
|
setorigin(newmis, org);
|
|
setsize (newmis, VEC_ORIGIN, VEC_ORIGIN);
|
|
|
|
fire_angles_y = fire_loop;
|
|
makevectors(fire_angles);
|
|
dir = vecrand(0,50,FALSE);
|
|
dir = dir + (v_forward * (400 + random() * 50));
|
|
newmis.velocity = dir;
|
|
newmis.count = 6;
|
|
newmis.frame = rint(random()*2);
|
|
|
|
// If DP engine active remove particle shadow
|
|
if (engine == ENG_DPEXT) newmis.effects = newmis.effects + EF_NOSHADOW;
|
|
|
|
newmis.think = wraith_fire_think;
|
|
newmis.touch = wraith_fire_touch;
|
|
newmis.nextthink = time + 0.1;
|
|
|
|
fire_loop = fire_loop + rint(random()*15);
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() wraith_debuff_trail =
|
|
{
|
|
local vector org, orgofs, dir, tvec, tang;
|
|
local float tdist, tloop, tcount, tpc;
|
|
local entity tent;
|
|
|
|
// Starting point of beam matched to wraith animation
|
|
makevectors (self.angles);
|
|
orgofs = '52 4 6';
|
|
|
|
// Calculate starting point and beam and vector direction
|
|
org = self.origin + attack_vector(orgofs);
|
|
tvec = SUB_orgEnemyTarget() - org;
|
|
// Create facing angle for segments
|
|
dir = normalize(tvec);
|
|
tang = vectoangles(dir);
|
|
|
|
// Check for solid wall very close
|
|
tpc = pointcontents(org);
|
|
if (tpc == CONTENT_SOLID || tpc == CONTENT_SKY) return;
|
|
tpc = pointcontents(org+(dir*16));
|
|
if (tpc == CONTENT_SOLID || tpc == CONTENT_SKY) return;
|
|
|
|
// Check for obstacles (geo and monsters)
|
|
traceline(org, SUB_orgEnemyTarget(), FALSE, self);
|
|
tdist = trace_fraction * vlen(tvec);
|
|
if (tdist < 32) return;
|
|
tloop = tdist / 32;
|
|
tcount = 0;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Originally - BleedDeBuff(SUB_entEnemyTarget());
|
|
// New version - Fire explosion + burning
|
|
if (trace_ent.flags & FL_CLIENT) {
|
|
self.owner = self; // Fix for T_damage inside ApplyFireDmg
|
|
ApplyFireDmg(SUB_entEnemyTarget(), DAMAGE_PYROWRAITH, DAMAGE_PYROWRAITH);
|
|
}
|
|
else if (ai_foundbreakable(self, trace_ent, FALSE))
|
|
T_Damage (trace_ent, self, self, 100, DAMARMOR);
|
|
|
|
//----------------------------------------------------------------------
|
|
// Generate particle beam in segments
|
|
while(tcount < tloop) {
|
|
// Create segment in the middle of 32 segment
|
|
tvec = org + (dir* (16 + tcount*32) );
|
|
|
|
// Spawn temporary entity
|
|
tent = spawn();
|
|
tent.owner = self;
|
|
tent.classtype = CT_PROJ_WBOLT;
|
|
setsize (tent, VEC_ORIGIN, VEC_ORIGIN);
|
|
tent.movetype = MOVETYPE_FLYMISSILE; // Needs to float up
|
|
tent.solid = SOLID_NOT;
|
|
|
|
// Special animated particle model (still, slow rotate, fast rotate)
|
|
setmodel(tent, MODEL_PROJ_WTRAIL);
|
|
setorigin(tent, tvec);
|
|
|
|
tent.velocity = -v_up*5; // Slowly drift up to create trail particles
|
|
tent.angles = tang; // Rotate segment into place
|
|
tent.frame = 2; // Fast rotate animation group
|
|
tent.skin = 3; // Flame yellow/red
|
|
tcount = tcount + 1; // Next segment
|
|
|
|
// Wait around for a while before fading out
|
|
tent.think = wraith_debuff_chunk;
|
|
tent.nextthink = time + (tcount *0.05);
|
|
}
|
|
|
|
// Workout final destination of explosion and effects
|
|
self.finaldest = tvec+(dir *16);
|
|
|
|
//----------------------------------------------------------------------
|
|
// Spawn a ball of plasma at the final destionation
|
|
// Shrinking downwards in size over 0.1s frames
|
|
tent = spawn();
|
|
tent.owner = self;
|
|
tent.classtype = CT_PROJ_WBOLT;
|
|
setsize (tent, VEC_ORIGIN, VEC_ORIGIN);
|
|
tent.movetype = MOVETYPE_FLYMISSILE;// Needs to float upward
|
|
tent.solid = SOLID_NOT;
|
|
setmodel(tent, MODEL_PROJ_WBALL); // Initial ball
|
|
setorigin(tent, self.finaldest); // Move ball to end of final segment
|
|
tent.velocity = -v_up*5; // Slowly drift upwards
|
|
tent.avelocity = '100 100 100'; // Slight rotation on all axis
|
|
tent.frame = 6; // Start really large
|
|
tent.skin = BALL_DEBUFF_COL2;
|
|
tent.think = wraith_debuff_shrink; // Animate manually
|
|
tent.nextthink = time + 0.1;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Generate particle explosion (+ sound effect)
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION); // hard coded explosion
|
|
WriteCoord (MSG_BROADCAST, self.finaldest_x);
|
|
WriteCoord (MSG_BROADCAST, self.finaldest_y);
|
|
WriteCoord (MSG_BROADCAST, self.finaldest_z);
|
|
// Play original explosion sound
|
|
sound(tent, CHAN_WEAPON, "drole/r_explode.wav", 1, ATTN_NORM);
|
|
|
|
//----------------------------------------------------------------------
|
|
// Large circular flame explosion
|
|
wraith_debuff_explode(self.finaldest);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity handent, vector orgofs, vector orgside, float dbframe) wraith_debuffball =
|
|
{
|
|
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 = BALL_DEBUFF_COL1;
|
|
}
|
|
|
|
// Generate attachment in hand (left)
|
|
org = self.origin + v_forward*orgofs_x + orgside*orgofs_y + v_up*orgofs_z;
|
|
setorigin(handent, org);
|
|
handent.angles_y = rint(random()*359);
|
|
handent.frame = dbframe;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(vector orgofs, float dbframe) wraith_debuff =
|
|
{
|
|
if (self.health < 1) return;
|
|
|
|
if (dbframe == 0) {
|
|
self.pain_finished = time + 1.1;
|
|
sound (self, CHAN_WEAPON, "wraith/attack1.wav", 1, ATTN_NORM);
|
|
|
|
// Setup halo/glow models around wraith (5% larger)
|
|
setorigin(self.attachment3, self.origin);
|
|
setmodel(self.attachment3, self.attachment3.mdl);
|
|
setsize (self.attachment3, VEC_ORIGIN, VEC_ORIGIN);
|
|
self.attachment3.movetype = MOVETYPE_NONE;
|
|
self.attachment3.solid = SOLID_NOT;
|
|
}
|
|
|
|
// Should be visible during attack cycle
|
|
self.skin = self.exactskin + 1;
|
|
self.alpha = WRAITH_ALPHAFIRE;
|
|
// Particles drifting up slowly from body
|
|
particle_explode(self.origin, 20, 2, self.part_style, PARTICLE_BURST_UPWARD);
|
|
// Update halo/glow model
|
|
self.attachment3.angles = self.angles;
|
|
self.attachment3.frame = self.frame;
|
|
self.attachment3.velocity = self.velocity;
|
|
// Randomly change alpha to make glow flicker
|
|
self.attachment3.alpha = 0.1 + random()*0.5;
|
|
|
|
|
|
// Keep turning towards enemy
|
|
ai_face();
|
|
makevectors(self.angles);
|
|
|
|
// Generate a ball in each hand
|
|
wraith_debuffball(self.attachment, orgofs, v_right, dbframe);
|
|
wraith_debuffball(self.attachment2, orgofs, -v_right, dbframe);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// RANGE ATTACK 1 - Swing arms forward with hand meeting together
|
|
//
|
|
// The debuff attack is a hitscan attack but it does have 1s animation
|
|
// (backward frames) 30 32 8, 14 46 20, -6 46 28, -12 40 28
|
|
// (forward frames) 0 46 30, 16 42 24, 32 38 20, 48 26 10, 52 14 6, 52 4 6
|
|
void() wraith_debuff1 =[ $magicA1, wraith_magicA2 ] {ai_face();};
|
|
void() wraith_magicA2 =[ $magicA2, wraith_magicA3 ] {ai_face();};
|
|
void() wraith_magicA3 =[ $magicA3, wraith_magicA4 ] {wraith_debuff('30 32 8',0);};
|
|
void() wraith_magicA4 =[ $magicA4, wraith_magicA5 ] {wraith_debuff('14 46 20',1);};
|
|
void() wraith_magicA5 =[ $magicA5, wraith_magicA6 ] {wraith_debuff('-6 46 28',2);};
|
|
void() wraith_magicA6 =[ $magicA6, wraith_magicA7 ] {wraith_debuff('-12 40 28',3);};
|
|
void() wraith_magicA7 =[ $magicA7, wraith_magicA8 ] {wraith_debuff('0 46 30',3);};
|
|
void() wraith_magicA8 =[ $magicA8, wraith_magicA9 ] {wraith_debuff('16 42 24',4);};
|
|
void() wraith_magicA9 =[ $magicA9, wraith_magicA10 ] {wraith_debuff('32 38 20',4);};
|
|
void() wraith_magicA10 =[ $magicA10, wraith_magicA11 ] {wraith_debuff('48 26 10',5);};
|
|
void() wraith_magicA11 =[ $magicA11, wraith_magicA12 ] {wraith_debuff('52 14 6',5);};
|
|
void() wraith_magicA12 =[ $magicA12, wraith_magicA13 ] {
|
|
wraith_debuff('52 4 6',6);
|
|
wraith_debuff_trail();
|
|
self.skin = self.exactskin;
|
|
self.alpha = WRAITH_ALPHA;
|
|
};
|
|
void() wraith_magicA13 =[ $magicA13, wraith_magicA14 ] {wraith_finish_attachment();};
|
|
void() wraith_magicA14 =[ $magicA14, wraith_run1 ] {};
|
|
|
|
//======================================================================
|
|
// RANGE ATTACK 2 - Small bone/nail attack (low damage)
|
|
//
|
|
// Create a ball of blood and throw it towards the player
|
|
// Turning the ball into a pile of bones/nails
|
|
//======================================================================
|
|
void(vector orgofs, float nbframe) wraith_nailball =
|
|
{
|
|
local vector org;
|
|
if (self.health < 1) return;
|
|
|
|
// Frame 0 is start of the sequence (move everything into place)
|
|
if (nbframe == 0) {
|
|
self.attachment.state = STATE_ON;
|
|
setorigin(self.attachment, self.origin);
|
|
setmodel(self.attachment, self.attachment.mdl);
|
|
setsize (self.attachment, VEC_ORIGIN, VEC_ORIGIN);
|
|
self.attachment.movetype = MOVETYPE_NONE;
|
|
self.attachment.solid = SOLID_NOT;
|
|
// Setup regular/poison projectile
|
|
if (self.spawnflags & MON_WRAITH_SPIDERS) self.attachment.skin = BALL_NAIL_COL2;
|
|
else self.attachment.skin = BALL_NAIL_COL1;
|
|
|
|
self.pain_finished = time + 1;
|
|
sound (self, CHAN_WEAPON, "wraith/attack2.wav", 1, ATTN_NORM);
|
|
}
|
|
|
|
// Turn towards enemy and make vector
|
|
ai_face();
|
|
makevectors(self.angles);
|
|
|
|
// Generate attachment in hand (left)
|
|
org = self.origin + attack_vector(orgofs);
|
|
setorigin(self.attachment, org);
|
|
self.attachment.angles_y = rint(random()*359);
|
|
self.attachment.frame = nbframe;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(vector boneofs) wraith_firenails =
|
|
{
|
|
local vector org, dir;
|
|
if (self.health < 1) return;
|
|
|
|
self.attack_speed = SPEED_WRAITHBONE + (skill * SPEED_WBONESKILL);
|
|
makevectors(self.angles);
|
|
|
|
// Generate spike/bone at final animation position
|
|
org = self.origin + attack_vector(boneofs);
|
|
dir = SUB_orgEnemyTarget() + v_right*(BONEOFFSET*3);
|
|
dir = normalize(dir - org);
|
|
launch_projectile (org, dir, CT_PROJ_WBONE, self.attack_speed+random()*25);
|
|
dir = SUB_orgEnemyTarget() - v_right*(BONEOFFSET*3);
|
|
dir = normalize(dir - org);
|
|
launch_projectile (org, dir, CT_PROJ_WBONE, self.attack_speed+random()*25);
|
|
dir = SUB_orgEnemyTarget() + v_up*BONEOFFSET;
|
|
dir = normalize(dir - org);
|
|
launch_projectile (org, dir, CT_PROJ_WBONE, self.attack_speed+random()*25);
|
|
dir = SUB_orgEnemyTarget() - v_up*BONEOFFSET;
|
|
dir = normalize(dir - org);
|
|
launch_projectile (org, dir, CT_PROJ_WBONE, self.attack_speed+random()*25);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// RANGE ATTACK 2 - Small bone/nail attack (low damage)
|
|
//
|
|
// 32 22 0, 29 29 6, 15 33 16, 4 28 24, -6 20 24, -10 16 20, 6 16 14,
|
|
// 14 16 10, 54 5 6, 60 4 14
|
|
void() wraith_nails1 =[ $magicC1, wraith_magicC2 ] {ai_face();
|
|
SUB_AttackFinished(1 + random()); // Fire more frequent
|
|
};
|
|
void() wraith_magicC2 =[ $magicC2, wraith_magicC3 ] {ai_face();};
|
|
void() wraith_magicC3 =[ $magicC3, wraith_magicC4 ] {wraith_nailball('15 33 16',0);};
|
|
void() wraith_magicC4 =[ $magicC4, wraith_magicC5 ] {wraith_nailball('4 28 24',1);};
|
|
void() wraith_magicC5 =[ $magicC5, wraith_magicC6 ] {wraith_nailball('-6 20 24',2);};
|
|
void() wraith_magicC6 =[ $magicC6, wraith_magicC7 ] {wraith_nailball('-10 16 20',3);};
|
|
void() wraith_magicC7 =[ $magicC7, wraith_magicC8 ] {wraith_nailball('6 16 14',4);};
|
|
void() wraith_magicC8 =[ $magicC8, wraith_magicC9 ] {wraith_nailball('14 16 10',5);};
|
|
void() wraith_magicC9 =[ $magicC9, wraith_magicC10 ] {wraith_nailball('54 5 6',6);};
|
|
void() wraith_magicC10 =[ $magicC10, wraith_magicC11 ] {
|
|
wraith_finish_attachment();
|
|
wraith_firenails('60 4 14');
|
|
};
|
|
void() wraith_magicC11 =[ $magicC11, wraith_magicC12 ] {monster_idle_sound();};
|
|
void() wraith_magicC12 =[ $magicC12, wraith_magicC13 ] {};
|
|
void() wraith_magicC13 =[ $magicC13, wraith_magicC14 ] {};
|
|
void() wraith_magicC14 =[ $magicC14, wraith_magicC15 ] {ai_face();};
|
|
void() wraith_magicC15 =[ $magicC15, wraith_run1 ] {ai_face();};
|
|
|
|
//======================================================================
|
|
// RANGE ATTACK 3 - spawn spider minion (optional)
|
|
//
|
|
// Create a ball of green goo and throw it towards the player
|
|
// Once ball has landed, hatch/explode and create spider (brown/green)
|
|
//======================================================================
|
|
void(vector eggofs, float eggframe) Setup_WraithEgg =
|
|
{
|
|
local vector org;
|
|
if (self.health < 1) return;
|
|
|
|
// Frame 0 is start of the sequence (move everything into place)
|
|
if (eggframe == 0) {
|
|
self.attachment.state = STATE_ON;
|
|
setorigin(self.attachment, self.origin);
|
|
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 = BALL_SPIDER_COL1;
|
|
|
|
// Present wraith from going into pain during egg cycle
|
|
self.pain_finished = time + 1;
|
|
sound (self, CHAN_WEAPON, "wraith/attack3.wav", 1, ATTN_NORM);
|
|
// Should be visible during spawn minion cycle
|
|
self.skin = self.exactskin + 1;
|
|
self.alpha = WRAITH_ALPHAFIRE;
|
|
}
|
|
|
|
// Turn towards enemy and update model attachment
|
|
// The offset is based on the wraith facing forward
|
|
ai_face();
|
|
makevectors(self.angles);
|
|
org = self.origin + attack_vector(eggofs);
|
|
setorigin(self.attachment, org);
|
|
self.attachment.angles = self.angles;
|
|
self.attachment.frame = eggframe;
|
|
};
|
|
|
|
//======================================================================
|
|
// RANGE ATTACK 3 - Cup hands around a spider egg infront and fire
|
|
//
|
|
// Spawn a yellow/green egg ball shape and then drop it towards the player
|
|
// The egg is spawned in the middle and moved up/down to generate particles
|
|
// 20 2 12, 18 2 12
|
|
void() wraith_minion1 =[ $magicB1, wraith_magicB2 ] {ai_face();};
|
|
void() wraith_magicB2 =[ $magicB2, wraith_magicB3 ] {Setup_WraithEgg('20 2 12',0);};
|
|
void() wraith_magicB3 =[ $magicB3, wraith_magicB4 ] {Setup_WraithEgg('18 2 12',1);};
|
|
void() wraith_magicB4 =[ $magicB4, wraith_magicB5 ] {Setup_WraithEgg('20 2 12',2);};
|
|
void() wraith_magicB5 =[ $magicB5, wraith_magicB6 ] {Setup_WraithEgg('18 2 12',3);};
|
|
void() wraith_magicB6 =[ $magicB6, wraith_magicB7 ] {Setup_WraithEgg('20 2 12',4);};
|
|
void() wraith_magicB7 =[ $magicB7, wraith_magicB8 ] {Setup_WraithEgg('18 2 12',5);};
|
|
void() wraith_magicB8 =[ $magicB8, wraith_magicB9 ] {Setup_WraithEgg('20 2 12',6);};
|
|
void() wraith_magicB9 =[ $magicB9, wraith_magicB10 ] {
|
|
self.skin = self.exactskin; // Not glowing anymore
|
|
self.alpha = WRAITH_ALPHA; // Fade out
|
|
wraith_finish_attachment();
|
|
Create_Egg('20 2 12');
|
|
// Particles drifting up slowly from body
|
|
particle_explode(self.origin, 30, 2, self.part_style, PARTICLE_BURST_UPWARD);
|
|
|
|
};
|
|
void() wraith_magicB10 =[ $magicB10, wraith_magicB11 ] {};
|
|
void() wraith_magicB11 =[ $magicB11, wraith_magicB12 ] {};
|
|
void() wraith_magicB12 =[ $magicB12, wraith_magicB13 ] {ai_face();};
|
|
void() wraith_magicB13 =[ $magicB13, wraith_run1 ] {ai_face();};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() wraith_magic =
|
|
{
|
|
local entity truetarg, miniondef;
|
|
|
|
// Make sure the attachments are setup ready
|
|
wraith_create_attachment();
|
|
truetarg = SUB_entEnemyTarget();
|
|
|
|
if (self.spawnflags & MON_WRAITH_SPIDERS && !self.minion_active) {
|
|
// Check for minion template first
|
|
miniondef = find(world,classname,"monster_wraithminion");
|
|
if (miniondef.classtype == CT_CACHESPIDER) setup_minionsupport();
|
|
else {
|
|
// If template no available, warn and remove feature
|
|
dprint("\b[WRAITH]\b Cannot find spider template!\n");
|
|
self.spawnflags = self.spawnflags - MON_WRAITH_SPIDERS;
|
|
self.poisonous = FALSE;
|
|
}
|
|
}
|
|
if (self.spawnflags & MON_WRAITH_SCORPIONS && !self.minion_active) {
|
|
// Check for minion template first
|
|
miniondef = find(world,classname,"monster_wraithminion2");
|
|
if (miniondef.classtype == CT_CACHESCORPION) setup_minionsupport();
|
|
else {
|
|
// If template no available, warn and remove feature
|
|
dprint("\b[WRAITH]\b Cannot find scorpion template!\n");
|
|
self.spawnflags = self.spawnflags - MON_WRAITH_SCORPIONS;
|
|
}
|
|
}
|
|
|
|
// Is the target the player or monster?
|
|
if (truetarg.flags & FL_CLIENT) {
|
|
if (self.spawnflags & (MON_WRAITH_SPIDERS | MON_WRAITH_SCORPIONS)) {
|
|
// Calculate a flat vector to ignore Z axis difference
|
|
self.enemydist = range_distance(self.enemy, TRUE);
|
|
// If the player is too close (melee distance) try old base attack
|
|
if (self.minion_baseattack > 0 && self.enemydist < MON_RANGE_MELEE) wraith_nails1();
|
|
else {
|
|
// has the minion limit been reached?
|
|
if (query_minionactive(self) == TRUE) wraith_minion1();
|
|
else wraith_nails1();
|
|
}
|
|
}
|
|
// First ever attack is the fire debuff
|
|
else if (self.attack_speed < time) wraith_debuff1();
|
|
// Does the player have the debuff already?
|
|
else if (truetarg.burning) wraith_nails1();
|
|
else {
|
|
// Greater chance of debuff attack first
|
|
if (random() < 0.7) wraith_debuff1();
|
|
else wraith_nails1();
|
|
}
|
|
self.attack_speed = LARGE_TIMER;
|
|
}
|
|
// Always attack monsters with nails
|
|
else wraith_nails1();
|
|
};
|
|
|
|
//============================================================================
|
|
// Pain A - Short flinch backwards
|
|
void() wraith_painA1 = [ $painA1, wraith_painA2 ] {};
|
|
void() wraith_painA2 = [ $painA2, wraith_painA3 ] {};
|
|
void() wraith_painA3 = [ $painA3, wraith_painA4 ] {};
|
|
void() wraith_painA4 = [ $painA4, wraith_painA5 ] {};
|
|
void() wraith_painA5 = [ $painA5, wraith_painA6 ] {};
|
|
void() wraith_painA6 = [ $painA6, wraith_run1 ] {};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Pain B - Long motion of being hit several times
|
|
void() wraith_painB1 = [ $painB1, wraith_painB2 ] {};
|
|
void() wraith_painB2 = [ $painB2, wraith_painB3 ] {};
|
|
void() wraith_painB3 = [ $painB3, wraith_painB4 ] {};
|
|
void() wraith_painB4 = [ $painB4, wraith_painB5 ] {};
|
|
void() wraith_painB5 = [ $painB5, wraith_painB6 ] {};
|
|
void() wraith_painB6 = [ $painB6, wraith_painB7 ] {};
|
|
void() wraith_painB7 = [ $painB7, wraith_painB8 ] {};
|
|
void() wraith_painB8 = [ $painB8, wraith_painB9 ] {};
|
|
void() wraith_painB9 = [ $painB9, wraith_painB10 ] {};
|
|
void() wraith_painB10 = [ $painB10, wraith_painB11 ] {};
|
|
void() wraith_painB11 = [ $painB11, wraith_run1 ] {};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity inflictor, entity attacker, float damage) wraith_pain =
|
|
{
|
|
// Check all pain conditions and set up what to do next
|
|
monster_pain_check(attacker, damage);
|
|
|
|
// Make sure fire wraith skin/alpha is reset on pain/death
|
|
self.skin = self.exactskin;
|
|
self.alpha = WRAITH_ALPHA;
|
|
|
|
// Hide any attachments
|
|
wraith_finish_attachment();
|
|
|
|
// 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) {
|
|
// Randomly pick which pain animation to play
|
|
if (random() < 0.85) wraith_painA1 ();
|
|
else {
|
|
self.pain_finished = time + 2;
|
|
wraith_painB1 ();
|
|
}
|
|
}
|
|
// The new axe forces monsters into long pain animations
|
|
else if (self.pain_check == 2) {
|
|
self.axhitme = 0;
|
|
self.pain_finished = time + 2;
|
|
wraith_painB1 ();
|
|
}
|
|
}
|
|
};
|
|
|
|
//============================================================================
|
|
void() wraith_death2 =[ $death2, wraith_death3] {};
|
|
void() wraith_death3 =[ $death3, wraith_death4] {};
|
|
void() wraith_death4 =[ $death4, wraith_death5] {};
|
|
void() wraith_death5 =[ $death5, wraith_death7] {};
|
|
void() wraith_death7 =[ $death7, wraith_death9] {};
|
|
void() wraith_death9 =[ $death9, wraith_death11] {};
|
|
void() wraith_death11 =[ $death11, wraith_death13] {};
|
|
void() wraith_death13 =[ $death13, wraith_death15] {};
|
|
void() wraith_death15 =[ $death15, wraith_death15]
|
|
{
|
|
// Throw gibs, but create more XY outward velocity
|
|
self.max_health = MON_XYGIBVELOCITY;
|
|
monster_ThrowGib();
|
|
// Move the explosion up to the middle/top of the body
|
|
// Use to be a regular explosion, switched to tarbaby instead
|
|
self.origin = self.origin + '0 0 24';
|
|
T_RadiusDamage (self, self, DAMAGE_WRAITH, world, IGNORECLASS);
|
|
|
|
// Big purple explosion
|
|
if (ext_dppart)
|
|
pointparticles(particleeffectnum(DPP_WRAITHEXPLODE), self.origin, '0 0 0', 1);
|
|
else {
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_TAREXPLOSION); // Hard coded explosion
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
}
|
|
|
|
// Play original explosion sound or replacement
|
|
sound(self, CHAN_WEAPON, SOUND_REXP3, 1, ATTN_NORM);
|
|
// Stop thinking and hide entity (prevent wraith_death15 loop)
|
|
self.nextthink = LARGE_TIMER;
|
|
self.think = SUB_Null;
|
|
entity_hide(self);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() wraith_die =
|
|
{
|
|
// Pre-check routine to tidy up extra entities
|
|
monster_death_precheck();
|
|
self.solid = SOLID_NOT;
|
|
sound (self, CHAN_VOICE, "wraith/death1.wav", 1, ATTN_NORM);
|
|
|
|
// Make sure fire wraith skin/alpha is reset on pain/death
|
|
self.skin = self.exactskin;
|
|
self.alpha = WRAITH_ALPHA;
|
|
|
|
// Make sure any attachments are removed
|
|
wraith_remove_attachment();
|
|
|
|
// Make sure body stays floating up where it finally dies
|
|
// and do not drop to the ground (reset by monster_death_use func)
|
|
self.flags = self.flags | FL_FLY;
|
|
self.movetype = MOVETYPE_FLY;
|
|
wraith_death2();
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED monster_wraith (1 0 0) (-16 -16 -24) (16 16 32) Ambush
|
|
======================================================================*/
|
|
void() monster_wraith =
|
|
{
|
|
if (deathmatch) { remove(self); return; }
|
|
|
|
self.mdl = "progs/mon_wraith.mdl";
|
|
self.weaponglow = "progs/mon_wraith_glow.mdl";
|
|
self.gib1mdl = "progs/gib_wraith1.mdl"; // Forearm
|
|
self.gib2mdl = "progs/gib_wraith2.mdl"; // Hand/Claw
|
|
self.gib3mdl = "progs/gib_wraith3.mdl"; // stump?
|
|
|
|
precache_model (self.mdl);
|
|
precache_model (self.weaponglow); // Glowing halo effect
|
|
precache_model (MODEL_PROJ_WTRAIL); // Hitscan blood bolt
|
|
precache_model (MODEL_PROJ_WBALL); // ball growing 8 frames
|
|
precache_model (MODEL_PROJ_WEGG); // ball turned into egg
|
|
precache_model (MODEL_PROJ_WSHELL); // Gibs from Egg
|
|
|
|
precache_model (MODEL_PROJ_WBONE); // Bone nails (fire/scorpion)
|
|
precache_model (MODEL_PROJ_WPOISON); // Poison nails (spider)
|
|
|
|
precache_model (self.gib1mdl);
|
|
precache_model (self.gib2mdl);
|
|
precache_model (self.gib3mdl);
|
|
|
|
self.idle_sound = "wraith/idle1.wav";
|
|
self.idle_sound2 = "wraith/idle2.wav";
|
|
self.idle_soundcom = "wraith/idle3.wav";
|
|
precache_sound (self.idle_sound);
|
|
precache_sound (self.idle_sound2);
|
|
precache_sound (self.idle_soundcom);
|
|
|
|
precache_sound ("wraith/death1.wav");
|
|
self.pain_sound = "wraith/pain1.wav";
|
|
precache_sound (self.pain_sound);
|
|
|
|
precache_sound ("wraith/attack1.wav"); // HITSCAN bolt
|
|
precache_sound ("drole/r_explode.wav");
|
|
precache_model (SBURST_FLAME); // Flames
|
|
|
|
precache_sound ("wraith/attack2.wav"); // Nail/Bones
|
|
precache_sound ("wraith/attack3.wav"); // Minions
|
|
precache_sound ("wraith/bounce.wav"); // Egg bounce
|
|
|
|
self.sight_sound = "wraith/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_WIDE;
|
|
if (self.health < 1) self.health = 350;
|
|
self.alpha = WRAITH_ALPHA; // Semi transparent
|
|
self.yaw_speed = 35; // Can turn really fast
|
|
self.gibhealth = MON_NEVERGIB; // Cannot be gibbed by weapons
|
|
self.gibbed = FALSE; // not falling apart
|
|
self.pain_flinch = 70; // High pain threshold
|
|
self.blockudeath = TRUE; // no humanoid death sound
|
|
if (self.height < 1) self.height = MONAI_ABOVEDIST; // Custom height
|
|
self.frame = 1; // First frame is never used!
|
|
self.deathstring = " was shattered by a Wraith\n";
|
|
|
|
// Hitscan = 52 4 6; Nails = 60 4 14; Egg = 20 2 12;
|
|
self.attack_offset = '30 2 8'; // Half way point
|
|
|
|
// Always reset Ammo Resistance to be consistent
|
|
self.resist_shells = self.resist_nails = 0;
|
|
self.resist_rockets = self.resist_cells = 0;
|
|
|
|
self.th_checkattack = WraithCheckAttack;
|
|
self.th_stand = wraith_stand1;
|
|
self.th_walk = wraith_walk1;
|
|
self.th_run = wraith_run1;
|
|
self.th_missile = wraith_magic;
|
|
self.th_pain = wraith_pain;
|
|
self.th_die = wraith_die;
|
|
|
|
// Cannot be both scorpion and spider minion spawner, def=spider
|
|
if (self.spawnflags & MON_WRAITH_SCORPIONS && self.spawnflags & MON_WRAITH_SPIDERS)
|
|
self.spawnflags = self.spawnflags - MON_WRAITH_SCORPIONS;
|
|
|
|
// Setup each wraith type with different skins/particles
|
|
if (self.spawnflags & MON_WRAITH_SCORPIONS) {
|
|
// Check for override first, default should be 3 not 5 (spiders)
|
|
if (self.minion_maxcount < 1) self.minion_maxcount = 3;
|
|
self.exactskin = WRAITH_SKINPURPLE; // Purple/Blood skin
|
|
self.poisonous = FALSE; // Must be off for scorpion wraith
|
|
self.part_style = PARTICLE_BURST_PURPLE; // Purple cloud
|
|
}
|
|
else if (self.spawnflags & MON_WRAITH_SPIDERS) {
|
|
self.exactskin = WRAITH_SKINGREEN; // Poison/Green skin
|
|
self.poisonous = TRUE; // Fires Poison needles
|
|
self.part_style = PARTICLE_BURST_GREEN; // Poison cloud
|
|
}
|
|
else {
|
|
self.exactskin = WRAITH_SKINBLUE; // Blue/Red skin
|
|
self.poisonous = FALSE; // Must be off for fire wraith
|
|
self.part_style = PARTICLE_BURST_FIRE;
|
|
}
|
|
|
|
self.classtype = CT_MONWRAITH;
|
|
self.classgroup = CG_SPIDER;
|
|
self.classmove = MON_MOVEFLY;
|
|
|
|
monster_start();
|
|
};
|