587 lines
21 KiB
Plaintext
587 lines
21 KiB
Plaintext
/*==============================================================================
|
|
Jim (Heavily inspired by bob from Quoth - Kell/Necros/Preach)
|
|
New models, skins and animations, code
|
|
==============================================================================*/
|
|
// Slight movement of wings
|
|
$frame idle1 idle2 idle3 idle4 idle5 idle6
|
|
|
|
// Twist wings backward and evil eyes
|
|
$frame idle7 idle8 idle9 idle10 idle11 idle12
|
|
|
|
// Wobble gun (+/- 5 degrees) and evil eyes
|
|
$frame idle13 idle14 idle15 idle16 idle17 idle18
|
|
|
|
// Idle while gun is point left or right
|
|
$frame idleleft1 idleleft2 idleleft3 idleleft4 idleleft5 idleleft6
|
|
$frame idleright1 idleright2 idleright3 idleright4 idleright5 idleright6
|
|
|
|
// Idle and turn gun left or right 30 degrees
|
|
$frame idleturnl1 idleturnl2 idleturnl3 idleturnl4 idleturnl5 idleturnl6
|
|
$frame idleturnr1 idleturnr2 idleturnr3 idleturnr4 idleturnr5 idleturnr6
|
|
|
|
// Custom wing movements for combat
|
|
$frame movedn1 movedn2 movedn3 movedn4 movedn5 movedn6
|
|
$frame moveup1 moveup2 moveup3 moveup4 moveup5 moveup6
|
|
|
|
// Flap wings and get angry eyes
|
|
$frame pain1 pain2 pain3 pain4 pain5 pain6
|
|
|
|
// Downward spiral with twisted wings
|
|
$frame zdeath1 zdeath2 zdeath3 zdeath4 zdeath5 zdeath6
|
|
|
|
float JS_IDLE = 0; // Default state
|
|
float JS_IDLE2 = 1; // Special idle (stationary)
|
|
float JS_IDLE3 = 2; // Special idle (stationary)
|
|
float JS_IDLELEFT = 3; // Looking left
|
|
float JS_IDLERIGHT = 4; // Looking right
|
|
float JS_TURNLEFT = 5; // Turning left (not looped)
|
|
float JS_TURNRIGHT = 6; // Turning right (not looped)
|
|
float JS_DOWN = 7; // Combat move
|
|
float JS_UP = 8; // Combat move
|
|
float JS_PAIN = 9; // Resets current animation
|
|
float JS_DYING = 10; // Final animation
|
|
|
|
// Size of each animation block
|
|
float JS_ANIMBLOCK = 6;
|
|
|
|
//======================================================================
|
|
// Update Jim every frame with skin/sound updates
|
|
// fstate (see above for animation block details)
|
|
//======================================================================
|
|
void(float fstate) jim_update =
|
|
{
|
|
// If jim is dead, no more updates
|
|
if (self.health < 1) return;
|
|
|
|
// Update ther gun idle (buzzing) sound
|
|
if (self.t_width < time) {
|
|
self.t_width = time + 0.9;
|
|
// Play on auto channel so it overlaps with existing sound
|
|
sound (self, CHAN_AUTO, "jim/gunidle2.wav", 0.3, ATTN_WEAPON);
|
|
}
|
|
|
|
// Time for an idle sound?
|
|
if (self.idletimer < time) monster_idle_sound();
|
|
|
|
// Jim has a glowing gun and eye skin feature!
|
|
if (self.t_length < time) {
|
|
self.t_length = time + 0.2;
|
|
self.exactskin = self.exactskin + 1;
|
|
if (self.exactskin > 2) self.exactskin = 0;
|
|
self.skin = self.exactskin;
|
|
}
|
|
|
|
// In combat?
|
|
if (self.enemy) {
|
|
self.idlebusy = FALSE;
|
|
if (fstate == JS_IDLE) {
|
|
if (self.origin_z > self.oldorigin_z) fstate = JS_UP;
|
|
else if (self.origin_z < self.oldorigin_z) fstate = JS_DOWN;
|
|
}
|
|
}
|
|
else {
|
|
// At beginning of frame block and reversing?
|
|
if (self.count == 0 && self.idlereverse) {
|
|
if (self.idlebusy == JS_TURNLEFT || self.idlebusy == JS_TURNRIGHT) {
|
|
self.idlebusy = JS_IDLE;
|
|
self.idlereverse = FALSE;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
}
|
|
// At end of frame block and going forward?
|
|
else if (self.count == JS_ANIMBLOCK-1 && !self.idlereverse) {
|
|
// Staring forward, doing nothing
|
|
if (self.idlebusy == JS_IDLE && self.waitmin < time) {
|
|
// Only start idle animations if stationary
|
|
if (self.velocity_x == 0 && self.velocity_y == 0) {
|
|
if (self.idletimer < time) monster_idle_sound();
|
|
self.lip = random();
|
|
if (self.lip < 0.3) self.idlebusy = JS_TURNLEFT;
|
|
else if (self.lip < 0.6) self.idlebusy = JS_TURNRIGHT;
|
|
else if (self.lip < 0.8) self.idlebusy = JS_IDLE2;
|
|
else self.idlebusy = JS_IDLE3;
|
|
}
|
|
}
|
|
// Return from wing flex, gun swing idle animations
|
|
else if (self.idlebusy == JS_IDLE2 || self.idlebusy == JS_IDLE3) {
|
|
self.idlebusy = JS_IDLE;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
// Turning eyes/gun in left direction
|
|
else if (self.idlebusy == JS_TURNLEFT) {
|
|
self.idlebusy = JS_IDLELEFT;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
// Turning eyes/gun to right direction
|
|
else if (self.idlebusy == JS_TURNRIGHT) {
|
|
self.idlebusy = JS_IDLERIGHT;
|
|
self.waitmin = time + 2 + random() * 4;
|
|
}
|
|
// Looking left and randomly think about returning
|
|
else if (self.idlebusy == JS_IDLELEFT) {
|
|
if (self.waitmin < time && random() < 0.5) {
|
|
self.idlebusy = JS_TURNLEFT;
|
|
self.idlereverse = TRUE;
|
|
}
|
|
}
|
|
// Looking right and randomly think about returning
|
|
else if (self.idlebusy == JS_IDLERIGHT) {
|
|
if (self.waitmin < time && random() < 0.5) {
|
|
self.idlebusy = JS_TURNRIGHT;
|
|
self.idlereverse = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Change the movement type so that jim can easily move up/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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check frame direction and update counter
|
|
if (self.idlereverse) self.count = self.count - 1;
|
|
else self.count = self.count + 1;
|
|
if (self.count >= JS_ANIMBLOCK) self.count = 0;
|
|
if (self.count < 0) self.count = JS_ANIMBLOCK-1;
|
|
|
|
// Busy with an idle animation?
|
|
if (self.idlebusy > 0) fstate = self.idlebusy;
|
|
// Update frame animation block with frame counter
|
|
self.frame = fstate * JS_ANIMBLOCK + self.count;
|
|
// Store current origin position
|
|
self.oldorigin = self.origin;
|
|
};
|
|
|
|
//======================================================================
|
|
// All stand, walk and run functions are condensed down to one entry
|
|
// because the robot has a constant skin/sound update that has to
|
|
// happen at specific intervals
|
|
//
|
|
void() jim_stand1 =[ $idle1, jim_stand1 ] {
|
|
// Standing idle has gentle bobbing up and down
|
|
self.solid = SOLID_SLIDEBOX;
|
|
self.movetype = MOVETYPE_FLY;
|
|
jim_update(JS_IDLE); ai_stand();
|
|
};
|
|
void() jim_walk1 =[ $idle1, jim_walk1 ] {
|
|
// Movement is steps, not velocity
|
|
self.solid = SOLID_SLIDEBOX;
|
|
self.movetype = MOVETYPE_STEP;
|
|
jim_update(JS_IDLE); ai_walk(8);
|
|
};
|
|
void() jim_run1 =[ $idle1, jim_run1 ] {
|
|
// Movement is steps, only velocity when firing weapon
|
|
self.solid = SOLID_SLIDEBOX;
|
|
self.movetype = MOVETYPE_STEP;
|
|
jim_update(JS_IDLE);
|
|
ai_run(16);
|
|
if (self.movespeed != 0) ai_face();
|
|
};
|
|
|
|
//======================================================================
|
|
// Range (LASERS or ROCKETS)
|
|
//======================================================================
|
|
void() jim_laser =
|
|
{
|
|
local vector org, dir, vec;
|
|
|
|
if (!self.enemy) return;
|
|
if (self.health < 1) return;
|
|
|
|
// Always make sure there is no monster or obstacle in the way
|
|
// Cannot use enemy entity direct, enemytarget will be active
|
|
if ( !visxray(SUB_entEnemyTarget(), self.attack_offset, '0 0 12', FALSE) ) return;
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
sound (self, CHAN_WEAPON, "enforcer/enfire.wav", 1, ATTN_NORM);
|
|
|
|
makevectors (self.angles);
|
|
org = self.origin + attack_vector(self.attack_offset);
|
|
|
|
// Aim high to catch jumping players
|
|
dir = SUB_orgEnemyTarget() + '0 0 12';
|
|
vec = normalize(dir - org);
|
|
// Variable laser speed, original unit was just 800 every skill
|
|
self.attack_speed = SPEED_JIMPROJ + (skill * SPEED_JIMPROJSKILL);
|
|
launch_projectile(org, vec, CT_PROJ_LASER, self.attack_speed);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Straight aim rocket, no pre-calculation or steering (skill based speed)
|
|
//----------------------------------------------------------------------
|
|
void() jim_rocket =
|
|
{
|
|
local vector org, dir, vec;
|
|
|
|
if (!self.enemy) return;
|
|
if (self.health < 1) return;
|
|
|
|
// Always make sure there is no monster or obstacle in the way
|
|
// Cannot use enemy entity direct, enemytarget will be active
|
|
if ( !visxray(SUB_entEnemyTarget(), self.attack_offset, '0 0 -12', FALSE) ) return;
|
|
|
|
self.effects = self.effects | EF_MUZZLEFLASH;
|
|
sound (self, CHAN_WEAPON, "jim/rocket_fire.wav", 1, ATTN_NORM);
|
|
|
|
makevectors (self.angles);
|
|
org = self.origin + attack_vector(self.attack_offset);
|
|
|
|
// Aim low to catch players with splash damage
|
|
dir = SUB_orgEnemyTarget() - '0 0 12';
|
|
vec = normalize(dir - org);
|
|
|
|
// Variable rocket speed, matching laser (very nasty)
|
|
self.attack_speed = SPEED_JIMPROJ + (skill * SPEED_JIMPROJSKILL);
|
|
Launch_Missile (org, vec, '0 0 0', CT_PROJ_JIM2, self.attack_speed);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void(float vspeed) jim_vel = {
|
|
// Turn and face enemy and update attack velocity
|
|
ai_face();
|
|
if (self.movespeed == 0)
|
|
self.velocity = self.attack_track * vspeed;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Calculate new attack vector for firing lasers at the player
|
|
//----------------------------------------------------------------------------
|
|
void() jim_attack =
|
|
{
|
|
self.solid = SOLID_SLIDEBOX;
|
|
if (self.movespeed == 0) self.movetype = MOVETYPE_FLY;
|
|
makevectors(self.angles);
|
|
// Always fly upwards away from the player
|
|
self.pos1 = v_up * (50 + random() * 100);
|
|
// Randomly pick a left or right strafe direction
|
|
self.pos2 = v_right * (crandom() * 200);
|
|
// Always try to back away from the player
|
|
self.pos3 = v_forward * (random() * 100);
|
|
|
|
// Merge all the randomness together
|
|
self.attack_track = self.pos1 + self.pos2 + self.pos3;
|
|
// Check nothing is in the way, estimate vector of attack
|
|
traceline(self.origin, self.origin+self.attack_track, FALSE, self);
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() jim_fire1 =[ $idle1, jim_fire2 ] {jim_update(JS_IDLE);
|
|
if (self.classtype == CT_MONJIM)
|
|
sound (self, CHAN_WEAPON, "jim/laser_load.wav", 1, ATTN_NORM);
|
|
else sound (self, CHAN_WEAPON, "jim/rocket_load.wav", 1, ATTN_NORM);
|
|
};
|
|
void() jim_fire2 =[ $idle1, jim_fire3 ] {jim_update(JS_IDLE);jim_attack();
|
|
if (skill < SKILL_HARD) jim_vel(0.4);
|
|
else jim_vel(1.5);
|
|
};
|
|
void() jim_fire3 =[ $idle1, jim_fire4 ] {
|
|
jim_update(JS_IDLE); if (skill < SKILL_HARD) jim_vel(0.6);};
|
|
void() jim_fire4 =[ $idle1, jim_fire5 ] { jim_update(JS_IDLE);
|
|
if (skill < SKILL_HARD) jim_vel(0.8);};
|
|
void() jim_fire5 =[ $idle1, jim_fire6 ] {jim_update(JS_IDLE);jim_vel(1.0);};
|
|
void() jim_fire6 =[ $idle1, jim_fire7 ] {
|
|
jim_update(JS_IDLE); if (skill < SKILL_HARD) jim_vel(1.2);
|
|
if (self.classtype == CT_MONJIM) jim_laser();
|
|
else jim_rocket();
|
|
};
|
|
void() jim_fire7 =[ $idle1, jim_fire8 ] {jim_update(JS_IDLE);
|
|
if (skill < SKILL_HARD) jim_vel(1.4);
|
|
if (self.classtype == CT_MONJIM) jim_laser();
|
|
};
|
|
void() jim_fire8 =[ $idle1, jim_fire9 ] {jim_update(JS_IDLE);
|
|
if (skill < SKILL_HARD) jim_vel(1.3);
|
|
if (self.classtype == CT_MONJIM && skill >= SKILL_NORMAL) jim_laser();
|
|
};
|
|
void() jim_fire9 =[ $idle1, jim_fire10] {jim_update(JS_IDLE);
|
|
if (skill < SKILL_HARD) jim_vel(1.1);
|
|
if (self.classtype == CT_MONJIM && skill >= SKILL_HARD) jim_laser();
|
|
};
|
|
void() jim_fire10 =[ $idle1, jim_fire11]{jim_update(JS_IDLE);jim_vel(0.5);};
|
|
void() jim_fire11 =[ $idle1, jim_run1 ] {jim_update(JS_IDLE);
|
|
// Chance of bob instantly firing again
|
|
if (random() < 0.8) SUB_AttackFinished( 1 + random() );
|
|
else SUB_AttackFinished( 1 + random()*0.5 );
|
|
};
|
|
|
|
//============================================================================
|
|
// ROBOT PAIN!?!
|
|
//============================================================================
|
|
void() jim_inpain =
|
|
{
|
|
// Keep cycling the pain animation
|
|
self.think = jim_inpain;
|
|
self.nextthink = time + 0.1;
|
|
|
|
// Start of pain cycle
|
|
if (self.inpain == 0) {
|
|
// Spawn a pile of sparks and dust falling down
|
|
particle_dust(self.origin, 10+random()*10, PARTICLE_BURST_YELLOW);
|
|
self.pos1 = '0 0 0';
|
|
self.pos1_x = random() * self.lefty; // Pitch away from dmg
|
|
self.pos1_z = crandom() * -10; // Roll left / right
|
|
}
|
|
// Finished, back to combat
|
|
else if (self.inpain >= 5) {
|
|
// reset pitch/roll
|
|
self.angles_x = self.angles_z = 0;
|
|
self.think = self.th_run;
|
|
}
|
|
else {
|
|
// Keep moving in the pitch/roll direction
|
|
self.pos1 = self.pos1 * 1.2;
|
|
self.angles = self.angles + self.pos1;
|
|
}
|
|
|
|
// Update pain frame
|
|
jim_update(JS_PAIN);
|
|
// Next pain animation
|
|
self.inpain = self.inpain + 1;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void(entity inflictor, entity attacker, float damage) jim_pain =
|
|
{
|
|
// Check all pain conditions and set up what to do next
|
|
monster_pain_check(attacker, damage);
|
|
|
|
// Stop any ai_run velocity and reset movetype
|
|
self.velocity = '0 0 0';
|
|
self.solid = SOLID_SLIDEBOX;
|
|
self.movetype = MOVETYPE_STEP;
|
|
if (random() < 0.5) sound (self, CHAN_VOICE, self.pain_sound, 1, ATTN_NORM);
|
|
else sound (self, CHAN_VOICE, self.pain_sound2, 1, ATTN_NORM);
|
|
self.inpain = 0;
|
|
|
|
// Work out which direction the damage came from (enemy)
|
|
if (infront(attacker)) self.lefty = 10; // Hit from infront
|
|
else self.lefty = -10; // Hit from behind
|
|
|
|
jim_inpain();
|
|
};
|
|
|
|
//============================================================================
|
|
// SLOW DYING ROBOT!?!
|
|
//============================================================================
|
|
void() jim_dying2;
|
|
void() jim_is_dead;
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() jim_dying1 =
|
|
{
|
|
// Setup correct death frame
|
|
self.frame = JS_DYING + self.cnt;
|
|
self.cnt = self.cnt + 1;
|
|
|
|
// Start spinning the robot out of control
|
|
// This update works with avelocity to create spiral effect
|
|
makevectors(self.angles);
|
|
self.velocity = (v_forward * 300) + (v_up * -30);
|
|
// Keep spinning out of control (constant direction)
|
|
if (self.lefty == 1) self.angles_y = (self.angles_y + 10);
|
|
else self.angles_y = (self.angles_y - 10);
|
|
|
|
// Spawn some extra smoke trails
|
|
if (random() < 0.3) SpawnProjectileSmoke(self.origin, 150, 50, 150);
|
|
|
|
// Keep looping round
|
|
self.think = jim_dying1;
|
|
self.nextthink = time + 0.1;
|
|
|
|
// Next frame/function?
|
|
if (self.cnt > 3) {
|
|
self.think = jim_dying2; // Next function
|
|
self.waitmin = time + 1; // Setup timer for next part
|
|
self.cnt = 0; // Reset death animation
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() jim_dying2 =
|
|
{
|
|
// Setup correct death frame
|
|
self.frame = JS_DYING + self.cnt;
|
|
self.cnt = self.cnt + 1;
|
|
if (self.cnt > 5) self.cnt = 0;
|
|
|
|
// Medium explosion + sound OR smoke projectile trail
|
|
if (random() > 0.7) SpawnExplosion(EXPLODE_MED, self.origin, "jim/explode_minor.wav");
|
|
else if (random() < 0.3) SpawnProjectileSmoke(self.origin, 150, 50, 150);
|
|
|
|
// Turn down towards the ground
|
|
makevectors(self.angles);
|
|
self.solid = SOLID_BBOX; // Collision + touch
|
|
// Slow down the velocity to accent the spinning
|
|
self.velocity = (self.velocity * 0.2) + ((v_forward * 100) * 0.8);
|
|
self.velocity = normalize(self.velocity) * 300;
|
|
self.velocity_z = self.velocity_z - 200;
|
|
// Keep spinning out of control (constant direction)
|
|
if (self.lefty == 1) self.angles_y = (self.angles_y + 10);
|
|
else self.angles_y = (self.angles_y - 10);
|
|
|
|
// Keep looping round
|
|
self.nextthink = time + 0.1;
|
|
if (self.waitmin < time) self.think = jim_is_dead;
|
|
else self.think = jim_dying2;
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() jim_is_dead =
|
|
{
|
|
self.think = self.touch = SUB_Null;
|
|
|
|
// Final fireworks!
|
|
particle_dust(self.origin, 10+random()*10, PARTICLE_BURST_YELLOW);
|
|
SpawnProjectileSmoke(self.origin, 150, 50, 150);
|
|
SpawnProjectileSmoke(self.origin, 150, 50, 150);
|
|
SpawnExplosion(EXPLODE_BIG, self.origin, "jim/explode_major.wav");
|
|
|
|
// Goto final resting place
|
|
entity_hide(self);
|
|
};
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void() jim_die =
|
|
{
|
|
// Pre-check routine to tidy up extra entities
|
|
monster_death_precheck();
|
|
|
|
// Flush every sound channel to kill gun buzzing sound
|
|
sound(self, CHAN_WEAPON, SOUND_EMPTY, 1, ATTN_NORM);
|
|
sound(self, CHAN_VOICE, SOUND_EMPTY, 1, ATTN_NORM);
|
|
sound(self, CHAN_ITEM, SOUND_EMPTY, 1, ATTN_NORM);
|
|
sound(self, CHAN_FEET, SOUND_EMPTY, 1, ATTN_NORM);
|
|
sound(self, CHAN_EXTRA1, SOUND_EMPTY, 1, ATTN_NORM);
|
|
sound(self, CHAN_EXTRA2, SOUND_EMPTY, 1, ATTN_NORM);
|
|
|
|
// Special explosive death event
|
|
// First function creates the upward spiral (negative avelocity)
|
|
// Second function accents the spiral (1s forward movement)
|
|
// Third final impact (touch function goes here as well)
|
|
//
|
|
sound (self, CHAN_VOICE, "jim/death1.wav", 1, ATTN_NORM);
|
|
self.solid = SOLID_NOT; // No world colision
|
|
self.movetype = MOVETYPE_FLY; // Free velocity movement
|
|
self.touch = jim_is_dead; // still can touch stuff
|
|
self.cnt = 0; // Death frame 0
|
|
self.movespeed = 0; // Make sure turret mode off
|
|
// The avelocity works better when negative (twisting upward)
|
|
self.avelocity_x = (random() * 50) - 100;
|
|
self.avelocity_y = (random() * 50) - 100;
|
|
self.avelocity_z = (random() * 50) - 100;
|
|
|
|
// Decide which direction to spin
|
|
if (random() < 0.5) self.lefty = 1;
|
|
else self.lefty = 0;
|
|
|
|
// Spawn dust and sprite explosion
|
|
particle_dust(self.origin, 10+random()*10, PARTICLE_BURST_YELLOW);
|
|
SpawnExplosion(EXPLODE_BIG, self.origin, "jim/explode_minor.wav");
|
|
SpawnProjectileSmoke(self.origin, 150, 50, 150);
|
|
|
|
jim_dying1();
|
|
};
|
|
|
|
/*======================================================================
|
|
/*QUAKED monster_jim (1 0 0) (-16 -16 -24) (16 16 24) Ambush
|
|
======================================================================*/
|
|
void() monster_jim =
|
|
{
|
|
if (deathmatch) { remove(self); return; }
|
|
|
|
if (self.spawnflags & MON_JIM_ROCKET) self.mdl = "progs/mon_jimrock.mdl";
|
|
else self.mdl = "progs/mon_jim.mdl";
|
|
precache_model (self.mdl);
|
|
|
|
// death/pain/attack sounds
|
|
precache_sound("jim/death1.wav");
|
|
self.pain_sound = "jim/pain1.wav";
|
|
self.pain_sound2 = "jim/pain2.wav";
|
|
precache_sound(self.pain_sound);
|
|
precache_sound(self.pain_sound2);
|
|
|
|
precache_sound("jim/explode_minor.wav");
|
|
precache_sound("jim/explode_major.wav");
|
|
precache_sound("jim/gunidle2.wav"); // Long buzzing sound
|
|
|
|
self.sight_sound = "jim/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_SHORT;
|
|
self.gibhealth = MON_NEVERGIB; // Cannot be gibbed by weapons
|
|
self.gibbed = FALSE;
|
|
self.pain_flinch = 30; // Sometimes flinch
|
|
self.steptype = FS_FLYING; // Silent feet
|
|
self.pain_longanim = FALSE; // No long pain animation
|
|
self.blockudeath = TRUE; // No humanoid death sound
|
|
if (self.height < 1) self.height = MONAI_ABOVEDIST; // Custom height
|
|
self.oldorigin = self.origin; // Used for wing angles
|
|
self.idlebusy = 0; // Rotate gun idle states
|
|
self.idlereverse = FALSE; // Reverse direction for idle
|
|
self.attack_offset = '20 0 -10'; // front of laser/rocket
|
|
self.poisonous = FALSE; // Robots are not poisonous
|
|
|
|
// Always reset Ammo Resistance to be consistent
|
|
self.resist_shells = self.resist_nails = 0;
|
|
self.resist_rockets = self.resist_cells = 0;
|
|
|
|
self.th_checkattack = JimCheckAttack;
|
|
self.th_stand = jim_stand1;
|
|
self.th_walk = jim_walk1;
|
|
self.th_run = jim_run1;
|
|
self.th_pain = jim_pain;
|
|
self.th_die = jim_die;
|
|
self.th_missile = jim_fire1;
|
|
|
|
self.classgroup = CG_ROBOT;
|
|
self.classmove = MON_MOVEFLY;
|
|
|
|
if (self.spawnflags & MON_JIM_ROCKET) {
|
|
self.classtype = CT_MONJIMROCKET;
|
|
if (self.health < 1) self.health = 50;
|
|
self.deathstring = " was blown away by Jim\n";
|
|
|
|
// Classic idle sound + new variant
|
|
self.idle_sound = "jim/idle1.wav";
|
|
self.idle_sound2 = "jim/idle3.wav";
|
|
precache_sound (self.idle_sound);
|
|
precache_sound (self.idle_sound2);
|
|
|
|
// Projectile fire and impact (used in projectiles)
|
|
precache_sound("jim/rocket_load.wav"); // Wind up
|
|
precache_sound("jim/rocket_fire.wav"); // Fire
|
|
precache_sound("jim/rocket_hit.wav"); // Impact
|
|
}
|
|
else {
|
|
self.classtype = CT_MONJIM;
|
|
if (self.health < 1) self.health = 100;
|
|
self.deathstring = " was cauterized by Jim\n";
|
|
|
|
// Classic idle sound + new variant
|
|
self.idle_sound = "jim/idle1.wav";
|
|
self.idle_sound2 = "jim/idle2.wav";
|
|
precache_sound (self.idle_sound);
|
|
precache_sound (self.idle_sound2);
|
|
|
|
// Projectile fire and impact (used in projectiles)
|
|
precache_model (MODEL_PROJ_LASER); // Enforcer laser model
|
|
precache_sound("jim/laser_load.wav"); // Wind up
|
|
precache_sound ("enforcer/enfire.wav"); // Fire
|
|
precache_sound ("enforcer/enfstop.wav"); // Impact
|
|
}
|
|
|
|
monster_start();
|
|
};
|
|
|
|
// Re-direct all Quoth Bob requests to Jim!
|
|
void() monster_bob = { monster_jim(); }
|