648 lines
22 KiB
Plaintext
648 lines
22 KiB
Plaintext
/*==============================================================================
|
|
LOST SOUL
|
|
==============================================================================*/
|
|
|
|
// moving jaw up and down (talking)
|
|
$frame idle1 idle2 idle3 idle4 idle5 idle6
|
|
|
|
// Tilt head backwards to look up
|
|
$frame idleB1 idleB2 idleB3 idleB4 idleB5 idleB6
|
|
|
|
// Tilt head downwards to look below
|
|
$frame idleF1 idleF2 idleF3 idleF4 idleF5 idleF6
|
|
|
|
// Keep looking left or right
|
|
$frame idleleft1 idleleft2 idleleft3 idleleft4 idleleft5 idleleft6
|
|
$frame idleright1 idleright2 idleright3 idleright4 idleright5 idleright6
|
|
|
|
// Turn head left or right 30 degrees
|
|
$frame idleturnl1 idleturnl2 idleturnl3 idleturnl4 idleturnl5 idleturnl6
|
|
$frame idleturnr1 idleturnr2 idleturnr3 idleturnr4 idleturnr5 idleturnr6
|
|
|
|
// mouth wide open and head tilted back
|
|
$frame charge1 charge2 charge3 charge4 charge5 charge6
|
|
|
|
// obvious chewing motion
|
|
$frame chew1 chew2 chew3 chew4 chew5 chew6
|
|
|
|
// mouth open and rotated Backward or Forward
|
|
$frame pain1 pain2 pain3 pain4 pain5 pain6
|
|
$frame pain7 pain8 pain9 pain10 pain11 pain12
|
|
|
|
// Spawning from nothing
|
|
$frame grow1 grow2 grow3 grow4 grow5 grow6 grow7 grow8 grow9 grow10
|
|
|
|
float LOST_IDLE = 0; // Default state
|
|
float LOST_IDLEB = 1; // Looking UP
|
|
float LOST_IDLEF = 2; // Looking DOWN
|
|
float LOST_IDLELEFT = 3; // Looking left
|
|
float LOST_IDLERIGHT = 4; // Looking right
|
|
float LOST_TURNLEFT = 5; // Turning left (not looped)
|
|
float LOST_TURNRIGHT = 6; // Turning right (not looped)
|
|
float LOST_CHARGE = 7; // Mouth wide open and charging
|
|
float LOST_MELEE = 8; // Mouth chomping open/close
|
|
float LOST_PAINB = 9; // Roll backwards
|
|
float LOST_PAINF = 10; // Roll forward
|
|
|
|
// Size of each animation block
|
|
float LOST_ANIMBLOCK = 6;
|
|
|
|
// Different states and fire particle quantity
|
|
float LOST_STATE_STAND = 2;
|
|
float LOST_STATE_WALK = 4;
|
|
float LOST_STATE_GUARD = 3;
|
|
float LOST_STATE_RUN = 5;
|
|
float LOST_STATE_PAIN = 6;
|
|
|
|
//======================================================================
|
|
// Update Lost Souls every frame with velocity movements and glow eyes!
|
|
// fstate (see above for animation block details)
|
|
//======================================================================
|
|
void(float fstate) lost_update =
|
|
{
|
|
// If Lost is dead, no more updates
|
|
if (self.health < 1) return;
|
|
|
|
// Time for an idle sound? (run only)
|
|
if (self.idletimer < time && self.state == LOST_STATE_RUN)
|
|
monster_idle_sound();
|
|
|
|
// Update glowing eyes! (skin types = 0,1,2,3,2,1)
|
|
if (self.t_length < time) {
|
|
// fire particle quantity linked to AI state
|
|
particle_explode(self.origin+'0 0 12', self.state, 1, PARTICLE_BURST_YELLOW, PARTICLE_BURST_LOSTUP);
|
|
self.t_length = time + 0.2;
|
|
self.exactskin = self.exactskin + 1;
|
|
if (self.exactskin > 5) self.exactskin = 0;
|
|
self.skin = self.exactskin;
|
|
}
|
|
|
|
// While standing still or guarding, gently float up and down
|
|
if (self.state == LOST_STATE_STAND || self.state == LOST_STATE_GUARD) {
|
|
// While standing around idle, gently move up and down
|
|
// using velocity, forced origin movement is really jerky!
|
|
if (self.velocity_x == 0 && self.velocity_y == 0) {
|
|
if (self.attack_timer < time) {
|
|
self.attack_timer = time + 1;
|
|
if (self.lip < 1) self.lip = 1;
|
|
else self.lip = -1;
|
|
self.velocity_z = 2 * self.lip;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lost sight of enemy during combat, wait and look around
|
|
if (self.state == LOST_STATE_GUARD) {
|
|
if (self.meleeattack < time) {
|
|
self.meleeattack = time + 4 + (random() + random() + random() * 4);
|
|
if (random() < 0.5) self.lefty = 1;
|
|
else self.lefty = -1;
|
|
self.avelocity = '0 0 0';
|
|
self.avelocity_y = self.lefty*50;
|
|
self.attack_sidestep = 0;
|
|
self.attack_sidedeny = rint(4 + random()*4);
|
|
}
|
|
|
|
// Keep increasing rotation angle velocity
|
|
self.attack_sidestep = self.attack_sidestep + 1;
|
|
if (self.attack_sidestep > self.attack_sidedeny) self.avelocity = '0 0 0';
|
|
else self.avelocity_y = self.avelocity_y * 1.25;
|
|
|
|
// Check lost idle timer after combat
|
|
if (self.lostenemy && self.losttimer < time) {
|
|
self.losttimer = LARGE_TIMER;
|
|
if (self.lostenemy.health < 0) self.lostenemy = world;
|
|
else {
|
|
// Restore previous enemy and start hunting
|
|
self.enemy = self.lostenemy;
|
|
self.think = self.th_run;
|
|
self.state = LOST_STATE_RUN;
|
|
// Setup goals and DO NOT warn other monsters
|
|
FoundHuntTarget(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Work through all the different AI states
|
|
if (self.state == LOST_STATE_STAND || self.state == LOST_STATE_WALK) {
|
|
// At beginning of frame block and reversing?
|
|
if (self.count == 0 && self.idlereverse) {
|
|
if (self.idlebusy == LOST_TURNLEFT || self.idlebusy == LOST_TURNRIGHT) {
|
|
self.idlebusy = LOST_IDLE;
|
|
self.idlereverse = FALSE;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
}
|
|
// At end of frame block and going forward?
|
|
else if (self.count == LOST_ANIMBLOCK-1 && !self.idlereverse) {
|
|
// Always reset turn left/right animation while walking
|
|
if (self.state == LOST_STATE_WALK) {
|
|
if (self.idlebusy == LOST_IDLELEFT) {
|
|
self.idlebusy = LOST_TURNLEFT;
|
|
self.idlereverse = TRUE;
|
|
}
|
|
else if (self.idlebusy == LOST_IDLERIGHT) {
|
|
self.idlebusy = LOST_TURNRIGHT;
|
|
self.idlereverse = TRUE;
|
|
}
|
|
// Return from all other idle animations
|
|
else {
|
|
self.idlebusy = LOST_IDLE;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
}
|
|
// Idle animation designed for standing around
|
|
else {
|
|
if (self.idlebusy == LOST_IDLE && self.waitmin < time) {
|
|
// Randonly pick an idle animation (difference stand/walk)
|
|
self.lip = random();
|
|
if (self.lip < 0.4) self.idlebusy = LOST_TURNLEFT;
|
|
else if (self.lip < 0.6) self.idlebusy = LOST_TURNRIGHT;
|
|
else if (self.lip < 0.8) self.idlebusy = LOST_IDLEB;
|
|
else self.idlebusy = LOST_IDLEF;
|
|
}
|
|
// Return from opening/closing mouth idle animations
|
|
else if (self.idlebusy == LOST_IDLEB || self.idlebusy == LOST_IDLEF) {
|
|
self.idlebusy = LOST_IDLE;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
// Turning head left direction
|
|
else if (self.idlebusy == LOST_TURNLEFT) {
|
|
self.idlebusy = LOST_IDLELEFT;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
// Turning head right direction
|
|
else if (self.idlebusy == LOST_TURNRIGHT) {
|
|
self.idlebusy = LOST_IDLERIGHT;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
// Looking left and randomly think about returning
|
|
else if (self.idlebusy == LOST_IDLELEFT) {
|
|
if (self.waitmin < time && random() < 0.5) {
|
|
self.idlebusy = LOST_TURNLEFT;
|
|
self.idlereverse = TRUE;
|
|
}
|
|
}
|
|
// Looking right and randomly think about returning
|
|
else if (self.idlebusy == LOST_IDLERIGHT) {
|
|
if (self.waitmin < time && random() < 0.5) {
|
|
self.idlebusy = LOST_TURNRIGHT;
|
|
self.idlereverse = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// All other states (RUN, PAIN, GUARD)
|
|
// Reset any previous idle animations
|
|
self.idlereverse = FALSE;
|
|
self.idlebusy = FALSE;
|
|
}
|
|
|
|
// Check frame direction and update counter
|
|
if (self.idlereverse) self.count = self.count - 1;
|
|
else self.count = self.count + 1;
|
|
if (self.count >= LOST_ANIMBLOCK) self.count = 0;
|
|
if (self.count < 0) self.count = LOST_ANIMBLOCK-1;
|
|
|
|
// Busy with an idle animation?
|
|
if (self.idlebusy > 0) fstate = self.idlebusy;
|
|
// Update frame animation block with frame counter
|
|
self.frame = fstate * LOST_ANIMBLOCK + self.count;
|
|
// Store current origin position
|
|
self.oldorigin = self.origin;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_flymode =
|
|
{
|
|
self.solid = SOLID_SLIDEBOX; // Reset just in case
|
|
self.movetype = MOVETYPE_FLY; // Turn body into projectile
|
|
// make sure onground is not set, cannot use velocity otherwise
|
|
self.flags = self.flags - (self.flags & FL_ONGROUND);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_stepmode =
|
|
{
|
|
self.velocity = '0 0 0'; // Back to regular movement
|
|
self.avelocity = '0 0 0'; // Cancel any rotation velocity
|
|
self.angles_x = self.angles_z = 0; // Reset any X/Y angles
|
|
self.solid = SOLID_SLIDEBOX; // Always reset
|
|
self.movetype = MOVETYPE_STEP; // Back to discreet movement
|
|
};
|
|
|
|
//======================================================================
|
|
// All stand, walk and run functions are condensed down to one entry
|
|
// so that the lost souls can move dynamic and have skin updates
|
|
//
|
|
void() lost_stand1 = [ $idle1, lost_stand1 ] {
|
|
if (self.state != LOST_STATE_GUARD) self.state = LOST_STATE_STAND;
|
|
lost_flymode(); // allow velocity updates
|
|
lost_update(LOST_IDLE); // Update skin + velocity
|
|
ai_stand();
|
|
};
|
|
void() lost_guard1 = { self.state = LOST_STATE_GUARD; lost_stand1(); };
|
|
|
|
void() lost_walk1 = [ $idle1, lost_walk1 ] {
|
|
self.state = LOST_STATE_WALK;
|
|
lost_stepmode(); // back to discreet step mode
|
|
lost_update(LOST_IDLE); // Update skin
|
|
ai_walk(8);
|
|
};
|
|
void() lost_run1 = [ $idle1, lost_run1 ] {
|
|
self.state = LOST_STATE_RUN;
|
|
lost_stepmode(); // back to discreet step mode
|
|
lost_update(LOST_IDLE); // Update skin
|
|
ai_run(8);
|
|
};
|
|
|
|
//============================================================================
|
|
// Range Charge attack
|
|
//============================================================================
|
|
void() lost_charge_touch =
|
|
{
|
|
// Stop anymore touching
|
|
self.touch = SUB_Null;
|
|
self.think = self.th_run;
|
|
|
|
// Do not damage other souls with charge attacks
|
|
// Prevents packs from killing themselves
|
|
if (self.classtype != other.classtype && other.takedamage) {
|
|
T_Damage (other, self, self, DAMAGE_LOSTCHARGE, DAMARMOR);
|
|
spawn_touchblood (self, other, DAMAGE_LOSTCHARGE*3);
|
|
|
|
// If touched a player, nudge them backwards
|
|
if (other.flags & FL_CLIENT) {
|
|
self.lostsearch = FALSE;
|
|
makevectors (self.angles);
|
|
other.velocity = other.velocity + (v_forward * 200 + '0 0 50');
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_charge1 =
|
|
{
|
|
// Play hiss sound, sighted enemy, charging
|
|
sound (self, CHAN_WEAPON, "lostsoul/charge.wav", 1, ATTN_NORM);
|
|
|
|
self.count = 0; // reset start of animation block
|
|
lost_flymode(); // allow velocity updates
|
|
lost_update(LOST_CHARGE); // Update skin
|
|
// Stop tracking if easy/normal skill
|
|
if (skill < SKILL_HARD) self.lostsearch = FALSE;
|
|
|
|
self.cnt = 0; // reset speed updates
|
|
self.attack_speed = SPEED_LOSTSTART; // Initial speed
|
|
self.pos1 = SUB_orgEnemyTarget() + '0 0 12'; // Aim towards head
|
|
self.pos2 = normalize(self.pos1 - self.origin); // Vector direction
|
|
self.touch = lost_charge_touch; // Damage/end function
|
|
self.velocity = self.pos2 * self.attack_speed; // Launch body missile!
|
|
self.angles = vectoangles(self.velocity); // Turn straight away
|
|
|
|
self.think = self.th_charge;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_charge2 =
|
|
{
|
|
if (self.health < 1) return;
|
|
lost_update(LOST_CHARGE);
|
|
self.nextthink = time + 0.1;
|
|
|
|
self.cnt = self.cnt + 1; // speed updates
|
|
if (self.cnt > 12) self.think = self.th_run; // End of charge attack
|
|
// Keep travelling forward regardless of enemy
|
|
else {
|
|
// Keep increasing speed every 0.1s (check for max speed)
|
|
if (self.attack_speed < SPEED_LOSTMAX)
|
|
self.attack_speed = self.attack_speed + SPEED_LOSTCHARGE;
|
|
self.velocity = self.pos2 * self.attack_speed;
|
|
self.think = self.th_charge;
|
|
}
|
|
};
|
|
|
|
//======================================================================
|
|
// MELEE ATTACK - Biting
|
|
//======================================================================
|
|
void() lost_bite =
|
|
{
|
|
local float ldmg;
|
|
local entity enemy_targ;
|
|
|
|
enemy_targ = SUB_entEnemyTarget();
|
|
|
|
if (!enemy_targ) return;
|
|
if (self.health < 1) return;
|
|
|
|
ai_charge(10); // Get closer for extra bite
|
|
ai_damagebreakable(10); // Damage any breakables
|
|
if (!ai_checkmelee(MONAI_MELEELOSTSOUL)) return; // Too far away
|
|
|
|
// Can the target bleed?
|
|
if (enemy_targ.takedamage < 1) return;
|
|
|
|
sound (self, CHAN_WEAPON, "lostsoul/bite.wav", 1, ATTN_NORM);
|
|
|
|
// Lost Soul bite is very weak
|
|
ldmg = (random() + random() + random()) * 3;
|
|
T_Damage (enemy_targ, self, self, ldmg, DAMARMOR);
|
|
|
|
// Check for poisonous attribute (new poison version)
|
|
if (self.poisonous) PoisonDeBuff(self.enemy);
|
|
|
|
// Spawn blood at mouth of Lost Soul
|
|
spawn_touchblood (self, enemy_targ, ldmg*3);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_melee2 =
|
|
{
|
|
if (self.health < 1) return;
|
|
lost_update(LOST_MELEE); // Update skin
|
|
self.cnt = self.cnt + 1; // Next frame
|
|
|
|
// Check for bite attack
|
|
if (self.cnt == 3) lost_bite();
|
|
|
|
// End of melee attack?
|
|
if (self.cnt >= 5) {
|
|
// One chop of the jaws and then fly away time
|
|
self.think = self.th_run;
|
|
|
|
// Check if enemy is still within range for another chop!
|
|
// if (ai_checkmelee(MONAI_MELEELOSTSOUL) && SUB_healthEnemyTarget() > 0)
|
|
// self.think = self.th_melee;
|
|
// else self.think = self.th_run;
|
|
}
|
|
else self.think = lost_melee2;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_melee1 =
|
|
{
|
|
self.count = 0; // reset start of animation block
|
|
self.cnt = 0; // Reset bite frame counter
|
|
lost_flymode(); // stay in flymode just in case collison probs
|
|
lost_update(LOST_MELEE); // Update skin
|
|
|
|
self.think = lost_melee2;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//======================================================================
|
|
// MINION - Grow and spin up from nothing
|
|
//======================================================================
|
|
void() lost_growangle = {self.angles_y = self.angles_y + self.lefty;};
|
|
void() lost_grow1 = [ $grow1, lost_grow2 ] {};
|
|
void() lost_grow2 = [ $grow2, lost_grow3 ] {lost_growangle();};
|
|
void() lost_grow3 = [ $grow3, lost_grow4 ] {lost_growangle();};
|
|
void() lost_grow4 = [ $grow4, lost_grow5 ] {lost_growangle();};
|
|
void() lost_grow5 = [ $grow5, lost_grow6 ] {lost_growangle();};
|
|
void() lost_grow6 = [ $grow6, lost_grow7 ] {lost_growangle();};
|
|
void() lost_grow7 = [ $grow7, lost_grow8 ] {lost_growangle();};
|
|
void() lost_grow8 = [ $grow8, lost_grow9 ] {lost_growangle();};
|
|
void() lost_grow9 = [ $grow9, lost_grow10] {lost_growangle();};
|
|
void() lost_grow10= [ $grow10, lost_run1 ] {
|
|
// Is the lost stuck? cannot move?
|
|
if (pointcontents(self.origin) == CONTENT_SOLID) {
|
|
// Time to die!
|
|
self.health = self.gibhealth;
|
|
Killed(self, self);
|
|
}
|
|
else {
|
|
self.state = LOST_STATE_RUN;
|
|
self.count = 0;
|
|
lost_update(LOST_IDLE); // Update skin
|
|
|
|
// Finally spin back to original position
|
|
self.angles_y = self.angles_y + self.lefty;
|
|
// Setup goals and warn other monsters
|
|
FoundHuntTarget(TRUE);
|
|
|
|
// Restore all think state functions
|
|
self.th_stand = lost_stand1;
|
|
self.th_altstand = lost_guard1;
|
|
self.th_walk = lost_walk1;
|
|
self.th_run = lost_run1;
|
|
self.th_melee = lost_melee1;
|
|
self.th_missile = lost_charge1;
|
|
self.th_charge = lost_charge2;
|
|
|
|
// Start charging at player if not dead and got an enemy
|
|
if (self.health > 0 && self.enemy) {
|
|
ai_face();
|
|
self.think = self.th_missile;
|
|
}
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() lost_grow =
|
|
{
|
|
// Only call wakeup function once
|
|
self.th_stand = self.th_walk = self.th_run = SUB_Null;
|
|
if (random() < 0.5) self.lefty = 36;
|
|
else self.lefty = -36;
|
|
monster_sightsound();
|
|
lost_stepmode();
|
|
lost_grow1();
|
|
};
|
|
|
|
//============================================================================
|
|
// PAIN in the head!
|
|
//============================================================================
|
|
void() lost_inpain =
|
|
{
|
|
// Update pain frame
|
|
lost_update(self.attack_rage);
|
|
|
|
// Next pain animation
|
|
self.inpain = self.inpain + 1;
|
|
if (self.inpain >= 5) self.think = self.th_run;
|
|
else self.think = lost_inpain;
|
|
self.nextthink = time + 0.1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void(entity inflictor, entity attacker, float damage) lost_pain =
|
|
{
|
|
// Check all pain conditions and set up what to do next
|
|
monster_pain_check(attacker, damage);
|
|
|
|
if (self.state != LOST_STATE_PAIN) {
|
|
// Stop all velocity and reset movetype
|
|
lost_stepmode();
|
|
sound (self, CHAN_VOICE, self.pain_sound, 1, ATTN_NORM);
|
|
}
|
|
|
|
// Work out which direction the damage came from (enemy)
|
|
if (infront(attacker)) self.attack_rage = LOST_PAINB; // Hit from infront
|
|
else self.attack_rage = LOST_PAINF; // Hit from behind
|
|
|
|
self.state = LOST_STATE_PAIN;
|
|
// Reset pain and current frame counter
|
|
self.inpain = self.count = 0;
|
|
lost_inpain();
|
|
};
|
|
|
|
//============================================================================
|
|
void() lost_die1 = [ $pain1, lost_die2 ] {};
|
|
void() lost_die2 = [ $pain2, lost_die2 ] {
|
|
// Lost souls explode when they die
|
|
T_RadiusDamage (self, self, DAMAGE_LOSTSOUL, world, DAMAGEALL);
|
|
if (random() < 0.5) self.lip = EXPLODE_SMALL;
|
|
else if (random() < 0.9) self.lip = EXPLODE_MED;
|
|
else self.lip = EXPLODE_BIG;
|
|
SpawnExplosion(self.lip, self.origin, "lostsoul/death.wav");
|
|
// Goto final resting place
|
|
entity_remove(self, 0.1);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
void() lost_die =
|
|
{
|
|
// Pre-check routine to tidy up extra entities
|
|
monster_death_precheck();
|
|
|
|
// Make sure body does not drop to the ground
|
|
self.flags = self.flags | FL_FLY;
|
|
self.takedamage = DAMAGE_NO;
|
|
lost_die1();
|
|
};
|
|
|
|
/*======================================================================
|
|
QUAKED monster_lostsoul (1 0.2 0) (-16 -16 -24) (16 16 24)
|
|
======================================================================*/
|
|
void() setup_lostsoul;
|
|
void() monster_lostsoul =
|
|
{
|
|
if (deathmatch) { remove(self); return; }
|
|
|
|
self.mdl = "progs/mon_lostsoul.mdl";
|
|
precache_model (self.mdl);
|
|
|
|
self.idle_sound = "lostsoul/idle1.wav";
|
|
self.idle_sound2 = "lostsoul/idle2.wav";
|
|
precache_sound (self.idle_sound);
|
|
precache_sound (self.idle_sound2);
|
|
|
|
precache_sound ("lostsoul/charge.wav"); // charge hiss/scream
|
|
precache_sound ("lostsoul/bite.wav"); // chomp with teeth
|
|
|
|
self.pain_sound = "lostsoul/pain1.wav";
|
|
precache_sound (self.pain_sound);
|
|
precache_sound ("lostsoul/death.wav");
|
|
|
|
// Cache lost soul is a special class used for precache only
|
|
if (self.classtype != CT_CACHELOSTSOUL) setup_lostsoul();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() monster_skullwizminion = {
|
|
self.classtype = CT_CACHELOSTSOUL;
|
|
monster_lostsoul();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() setup_lostsoul =
|
|
{
|
|
self.solid = SOLID_NOT; // No interaction with world
|
|
self.movetype = MOVETYPE_NONE; // Static item, no movement
|
|
if (self.bboxtype < 1) self.bboxtype = BBOX_TINY;
|
|
if (self.health < 1) self.health = 30;
|
|
self.yaw_speed = 35; // Really fast at turning
|
|
self.gibhealth = MON_NEVERGIB; // Cannot be gibbed by weapons
|
|
self.gibbed = FALSE; // Still in one piece
|
|
self.pain_flinch = 10; // Always go into pain
|
|
self.blockudeath = TRUE; // no humanoid death sound
|
|
self.steptype = FS_FLYING; // Silent feet
|
|
self.pain_longanim = FALSE; // No long pain animation
|
|
if (self.height < 1) self.height = MONAI_ABOVEDIST; // Custom height
|
|
self.meleeattack = time + 4; // no rotation at spawn
|
|
self.mangle = self.angles; // idle rotate left/right
|
|
self.idlebusy = 0; // Tilt/turn head animations
|
|
self.idlereverse = FALSE; // Reverse direction for idle
|
|
self.exactskin = rint(random()*5); // Randomize eye glow
|
|
self.lostenemy = world; // Setup old enemy
|
|
self.losttimer = LARGE_TIMER; // Stop lost timer
|
|
self.lostsearch = FALSE; // Not searching for an enemy
|
|
self.deathstring = " was found by a Lost Soul\n";
|
|
|
|
// I can understand lost souls would be the perfect candidate for
|
|
// poison attacks, but they are tricky already! default = disabled.
|
|
self.poisonous = FALSE;
|
|
|
|
// Always reset Ammo Resistance to be consistent
|
|
self.resist_shells = self.resist_nails = 0;
|
|
self.resist_rockets = self.resist_cells = 0;
|
|
|
|
self.th_checkattack = LostCheckAttack;
|
|
self.th_pain = lost_pain;
|
|
self.th_die = lost_die;
|
|
|
|
//----------------------------------------------------------------------
|
|
// Special spawning minion need to start spinning
|
|
if (self.classtype == CT_MINIONLOSTSOUL) {
|
|
self.th_stand = self.th_walk = self.th_run = lost_grow;
|
|
self.th_missile = SUB_Null;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// Default lost soul setup
|
|
else {
|
|
self.th_stand = lost_stand1;
|
|
self.th_altstand = lost_guard1;
|
|
self.th_walk = lost_walk1;
|
|
self.th_run = lost_run1;
|
|
self.th_melee = lost_melee1;
|
|
self.th_missile = lost_charge1;
|
|
self.th_charge = lost_charge2;
|
|
}
|
|
|
|
self.classtype = CT_MONLOSTSOUL;
|
|
self.classgroup = CG_WIZARD;
|
|
self.classmove = MON_MOVEFLY;
|
|
|
|
monster_start();
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// A code way to spawn lost souls (requires monster_skullwizminion entity)
|
|
//----------------------------------------------------------------------------
|
|
void(vector minion_org, entity minion_targ) minion_lostsoul =
|
|
{
|
|
local entity minion;
|
|
|
|
// Check if there is space to spawn entity
|
|
if (entity_pcontent(minion_org)) return;
|
|
|
|
// Self = skull wizard, there is no egg
|
|
update_minioncount(self, 1); // Update spawn counters
|
|
|
|
minion = spawn();
|
|
minion.classname = "monster_lostsoul"; // 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.classtype = CT_MINIONLOSTSOUL; // Special minion class
|
|
minion.enemy = minion_targ; // Target to attack
|
|
minion.spawnflags = 0;
|
|
minion.classgroup = CG_WIZARD; // Don't turn on master
|
|
minion.minion_active = TRUE; // Minion flag
|
|
minion.bodyfadeaway = TRUE; // Get rid of body
|
|
|
|
minion.mdl = "progs/mon_lostsoul.mdl";
|
|
|
|
minion.pain_sound = "lostsoul/pain1.wav";
|
|
minion.idle_sound = "lostsoul/idle1.wav";
|
|
minion.idle_sound2 = "lostsoul/idle2.wav";
|
|
|
|
minion.nextthink = time + 0.01;
|
|
minion.think = setup_lostsoul;
|
|
};
|