509 lines
11 KiB
C++
509 lines
11 KiB
C++
/*
|
|
* $Header: /HexenWorld/Siege/snake.hc 3 5/25/98 1:39p Mgummelt $
|
|
*/
|
|
/*
|
|
==============================================================================
|
|
|
|
SNAKE
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
// FRAME 0
|
|
$frame rootpose
|
|
//
|
|
// FRAME 1 - 13
|
|
$frame 1cycle1 1cycle2 1cycle3 1cycle4 1cycle5
|
|
$frame 1cycle6 1cycle7 1cycle8 1cycle9 1cycle10
|
|
$frame 1cycle11 1cycle12 1cycle13
|
|
|
|
//
|
|
// FRAME 14 - 26
|
|
$frame 2cycle1 2cycle2 2cycle3 2cycle4 2cycle5
|
|
$frame 2cycle6 2cycle7 2cycle8 2cycle9 2cycle10
|
|
$frame 2cycle11 2cycle12 2cycle13
|
|
|
|
//
|
|
// FRAME 27 - 36
|
|
$frame hiss1 hiss2 hiss3 hiss4 hiss5
|
|
$frame hiss6 hiss7 hiss8 hiss9 hiss10
|
|
|
|
//
|
|
// FRAME 37 - 44
|
|
$frame settle1 settle2 settle3 settle4 settle5
|
|
$frame settle6 settle7 settle8
|
|
|
|
//
|
|
// FRAME 45 - 57
|
|
$frame spitLF1 spitLF2 spitLF3 spitLF4 spitLF5
|
|
$frame spitLF6 spitLF7 spitLF8 spitLF9 spitLF10
|
|
$frame spitLF11 spitLF12 spitLF13
|
|
|
|
//
|
|
// FRAME 58 - 70
|
|
$frame spitRT1 spitRT2 spitRT3 spitRT4 spitRT5
|
|
$frame spitRT6 spitRT7 spitRT8 spitRT9 spitRT10
|
|
$frame spitRT11 spitRT12 spitRT13
|
|
|
|
//
|
|
// FRAME 71 - 81
|
|
$frame sway1 sway2 sway3 sway4 sway5
|
|
$frame sway6 sway7 sway8 sway9 sway10
|
|
$frame sway11
|
|
|
|
//
|
|
// FRAME 72 - 94
|
|
$frame UNsway1 UNsway2 UNsway3 UNsway4 UNsway5
|
|
$frame UNsway6 UNsway7 UNsway8 UNsway9 UNsway10
|
|
$frame UNsway11 UNsway12 UNsway13
|
|
|
|
//
|
|
// FRAME 95 - 113
|
|
$frame wake1 wake2 wake3 wake4 wake5
|
|
$frame wake6 wake7 wake8 wake9 wake10
|
|
$frame wake11 wake12 wake13 wake14 wake15
|
|
$frame wake16 wake17 wake18 wake19
|
|
|
|
void wake_effect (void);
|
|
void snake_think(void);
|
|
float snake_look(void);
|
|
|
|
/*
|
|
void snake_deflect (void)
|
|
{
|
|
local entity item;
|
|
local vector vec, realVec;
|
|
local float diff;
|
|
float DidDeflect;
|
|
|
|
DidDeflect = 0;
|
|
item = findradius(self.origin, 300);
|
|
while (item)
|
|
{
|
|
|
|
if ((item.owner.classname == "player") && ((item.movetype == MOVETYPE_FLYMISSILE) ||
|
|
(item.movetype == MOVETYPE_BOUNCEMISSILE)))
|
|
{
|
|
vec = self.origin - item.origin + self.view_ofs;
|
|
vec = normalize(vec);
|
|
realVec = normalize(item.velocity);
|
|
diff = fabs(vec_x - realVec_x) + fabs(vec_y - realVec_y) + fabs(vec_z - realVec_z);
|
|
if (diff <= 0.4)
|
|
{
|
|
diff = vlen(item.velocity);
|
|
diff = diff / random(1.5, 2.5);
|
|
|
|
vec = '0 0 0' - item.velocity;
|
|
vec = normalize(vec);
|
|
|
|
vec += randomv('-0.5 -0.5 -0.5', '0.5 0.5 0.5');
|
|
vec = normalize(vec);
|
|
|
|
vec = vec * diff;
|
|
|
|
item.velocity = vec;
|
|
|
|
item.angles = vectoangles(item.velocity);
|
|
item.owner = self;
|
|
DidDeflect = 1;
|
|
}
|
|
}
|
|
item = item.chain;
|
|
}
|
|
|
|
if (DidDeflect)
|
|
sound (self, CHAN_WEAPON, "fangel/deflect.wav", 1, ATTN_NORM);
|
|
|
|
}
|
|
|
|
void snake_checkdeflect(void)
|
|
{
|
|
// if (self.enemy.last_attack + 0.5 < time)
|
|
// return;
|
|
|
|
snake_deflect();
|
|
}
|
|
*/
|
|
|
|
|
|
void snake_sleep (void)
|
|
{
|
|
self.nextthink = time - 1;
|
|
self.think = SUB_Null;
|
|
}
|
|
|
|
void snake_settle (void) [++ $settle1 .. $settle8]
|
|
{
|
|
if (self.frame == 38)
|
|
sound (self, CHAN_BODY, "snake/hiss.wav", 1, ATTN_NORM);
|
|
|
|
if (cycle_wrapped)
|
|
wake_effect();
|
|
}
|
|
|
|
void snake_unsway (void) [++ $UNsway1 .. $UNsway13]
|
|
{
|
|
if (self.frame == 86)
|
|
sound (self, CHAN_BODY, "snake/hiss.wav", 1, ATTN_NORM);
|
|
|
|
if (cycle_wrapped)
|
|
snake_settle();
|
|
}
|
|
|
|
|
|
void spit_touch(void)
|
|
{
|
|
float damg;
|
|
vector delta;
|
|
|
|
damg = random(10,14);
|
|
|
|
T_Damage (other, self, self.owner, damg );
|
|
|
|
self.origin = self.origin - 8 * normalize(self.velocity) - '0 0 40';
|
|
sound (self, CHAN_WEAPON, "weapons/explode.wav", 1, ATTN_NORM);
|
|
|
|
if (other)
|
|
{
|
|
delta = other.origin - self.origin;
|
|
other.velocity = delta * 40;
|
|
other.flags (-) FL_ONGROUND;
|
|
other.velocity_z = 275;
|
|
}
|
|
|
|
CreateRedSpark (self.origin);
|
|
|
|
remove(self);
|
|
|
|
}
|
|
|
|
void snake_missile_think (void)
|
|
{
|
|
if (self.lifetime < time)
|
|
{
|
|
spit_touch();
|
|
}
|
|
else
|
|
{
|
|
HomeThink();
|
|
self.angles = vectoangles(self.velocity);
|
|
self.think=snake_missile_think;
|
|
thinktime self : HX_FRAME_TIME;
|
|
}
|
|
}
|
|
|
|
void snake_missile_shoot(vector spot1)
|
|
{
|
|
entity newmis;
|
|
vector diff;
|
|
|
|
newmis = spawn ();
|
|
newmis.owner = self;
|
|
newmis.movetype = MOVETYPE_FLYMISSILE;
|
|
newmis.solid = SOLID_BBOX;
|
|
|
|
setmodel (newmis, "models/goop.mdl");
|
|
setsize (newmis, '0 0 0', '0 0 0');
|
|
setorigin (newmis, spot1);
|
|
|
|
newmis.angles = self.angles;
|
|
|
|
diff = (self.enemy.origin + self.enemy.view_ofs) - spot1;
|
|
newmis.velocity = normalize(diff);
|
|
newmis.angles = vectoangles(newmis.velocity);
|
|
|
|
makevectors (newmis.angles);
|
|
newmis.velocity = newmis.velocity * 1000;
|
|
newmis.speed=1000; //Speed
|
|
newmis.classname = "faminemissile";
|
|
newmis.angles = vectoangles(newmis.velocity);
|
|
|
|
newmis.veer=TRUE; //No random wandering
|
|
newmis.turn_time=3; //Lower the number, tighter the turn
|
|
newmis.ideal_yaw=TRUE;//Only track things in front
|
|
newmis.lockentity = self.enemy;
|
|
newmis.lifetime = time + 1.5;
|
|
newmis.think=snake_missile_think;
|
|
thinktime newmis : HX_FRAME_TIME;
|
|
|
|
newmis.touch = spit_touch;
|
|
}
|
|
|
|
void snake_missile (void)
|
|
{
|
|
makevectors(self.angles);
|
|
snake_missile_shoot(self.origin + v_forward*4 + v_right * 38 + v_up * 195);
|
|
|
|
makevectors(self.angles);
|
|
snake_missile_shoot(self.origin + v_forward*4 + v_right * 57 + v_up * 195);
|
|
}
|
|
|
|
void snake_attackleft (void) [++ $spitLF1 .. $spitLF13]
|
|
{
|
|
|
|
if (self.frame == $spitLF5)
|
|
snake_missile();
|
|
|
|
if (self.frame == 45)
|
|
sound (self, CHAN_WEAPON, "snake/attack.wav", 1, ATTN_NORM);
|
|
|
|
if (cycle_wrapped)
|
|
snake_think();
|
|
}
|
|
|
|
void snake_attackright (void) [++ $spitRT1 .. $spitRT13]
|
|
{
|
|
|
|
if (self.frame == $spitRT5)
|
|
snake_missile();
|
|
|
|
if (self.frame == 61)
|
|
sound (self, CHAN_WEAPON, "snake/attack.wav", 1, ATTN_NORM);
|
|
|
|
if (cycle_wrapped)
|
|
snake_think();
|
|
}
|
|
|
|
void snake_attackfront (void) [++ $wake5 .. $wake19]
|
|
{
|
|
|
|
if (self.frame == $wake10)
|
|
snake_missile();
|
|
|
|
if (self.frame == 100)
|
|
sound (self, CHAN_WEAPON, "snake/attack.wav", 1, ATTN_NORM);
|
|
|
|
if (cycle_wrapped)
|
|
snake_think();
|
|
}
|
|
|
|
void snake_attack (void)
|
|
{
|
|
self.attack_cnt += 1;
|
|
|
|
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
|
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin) - self.angles_y;
|
|
|
|
if (((self.ideal_yaw >= 0) && (self.ideal_yaw < 25)) ||
|
|
((self.ideal_yaw >= -25) && (self.ideal_yaw < -1)))
|
|
snake_attackfront();
|
|
else if ((self.ideal_yaw >= 25) && (self.ideal_yaw < 180))
|
|
snake_attackleft();
|
|
else
|
|
snake_attackright();
|
|
}
|
|
|
|
void snake_dancetongue(void) [++ $2cycle1 .. $2cycle13]
|
|
{
|
|
//hinktime self : HX_FRAME_TIME * 2;
|
|
thinktime self : HX_FRAME_TIME *1.5;
|
|
|
|
if (self.frame == 16)
|
|
sound (self, CHAN_BODY, "snake/hiss.wav", 1, ATTN_NORM);
|
|
|
|
if (cycle_wrapped)
|
|
snake_think();
|
|
}
|
|
|
|
void snake_dancetail(void) [++ $1cycle1 .. $1cycle13]
|
|
{
|
|
//thinktime self : HX_FRAME_TIME * 2;
|
|
thinktime self : HX_FRAME_TIME *1.5;
|
|
|
|
if (cycle_wrapped)
|
|
snake_think();
|
|
}
|
|
|
|
void snake_dance (void)
|
|
{
|
|
self.attack_cnt = 0;
|
|
|
|
|
|
if (random() < .5)
|
|
snake_dancetail();
|
|
else
|
|
snake_dancetongue();
|
|
}
|
|
|
|
void snake_startsway(void) [++ $sway1 .. $sway11]
|
|
{
|
|
//hinktime self : HX_FRAME_TIME * 2;
|
|
thinktime self : HX_FRAME_TIME;
|
|
|
|
if (cycle_wrapped)
|
|
snake_dance();
|
|
}
|
|
|
|
void snake_wake(void) [++ $wake1 .. $wake19]
|
|
{
|
|
//thinktime self : HX_FRAME_TIME * 2;
|
|
thinktime self : HX_FRAME_TIME;
|
|
|
|
if (self.frame < $wake15)
|
|
{
|
|
self.abslight = .25 + (.05 * (self.frame - $wake1));
|
|
}
|
|
else
|
|
self.abslight -= .05;
|
|
|
|
if (self.frame == $wake19)
|
|
self.drawflags (-) MLS_ABSLIGHT;
|
|
|
|
if (self.frame == 100)
|
|
sound (self, CHAN_ITEM, "snake/wake.wav", 1, ATTN_NORM);
|
|
|
|
self.skin = 0;
|
|
if (cycle_wrapped)
|
|
snake_startsway();
|
|
}
|
|
|
|
|
|
float float_null;
|
|
|
|
float snake_look(void)
|
|
{
|
|
entity client;
|
|
|
|
client = checkclient ();
|
|
if (!client)
|
|
{
|
|
return FALSE; // current check entity isn't in PVS
|
|
}
|
|
|
|
if ((client.items & IT_INVISIBILITY) || (client.flags & FL_NOTARGET))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!visible (client))
|
|
return FALSE;
|
|
|
|
if (client.classname == "player")
|
|
{
|
|
self.enemy = client;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void snake_think (void)
|
|
{
|
|
float chance;
|
|
|
|
if (snake_look())
|
|
{
|
|
enemy_range = vlen (self.origin - self.enemy.origin);
|
|
enemy_range -= 80;
|
|
|
|
if (enemy_range < 200)
|
|
chance = 0.90;
|
|
else if (enemy_range < 400)
|
|
chance = 0.80;
|
|
else if (enemy_range < 800)
|
|
chance = 0.70;
|
|
else if (enemy_range < 1200)
|
|
chance = 0.60;
|
|
else if (enemy_range < 2000)
|
|
chance = 0.50;
|
|
else // Too far away
|
|
{
|
|
self.enemy = world;
|
|
chance = 0;
|
|
}
|
|
|
|
chance -= ((self.attack_cnt/2) * .1);
|
|
|
|
if (random() < chance)
|
|
{
|
|
self.last_attack = time;
|
|
snake_attack();
|
|
}
|
|
else
|
|
snake_dance();
|
|
}
|
|
else
|
|
{
|
|
self.enemy = world;
|
|
snake_dance();
|
|
}
|
|
|
|
if (self.last_attack < time - 30)
|
|
snake_unsway();
|
|
}
|
|
|
|
void wake_effect (void)
|
|
{
|
|
entity newent;
|
|
|
|
newent = spawn();
|
|
|
|
setorigin(newent,self.origin);
|
|
sound (self, CHAN_BODY, "snake/life.wav", 1, ATTN_NORM);
|
|
|
|
if (self.colormap != float_null)
|
|
{
|
|
CreateGreySmoke(self.origin + '0 0 60','0 0 12',HX_FRAME_TIME * 20);
|
|
CreateGreySmoke(self.origin + '16 16 55','0 0 12',HX_FRAME_TIME * 20);
|
|
CreateGreySmoke(self.origin + '32 32 80','0 0 12',HX_FRAME_TIME * 20);
|
|
CreateGreySmoke(self.origin + '64 64 50','0 0 12',HX_FRAME_TIME * 20);
|
|
CreateGreySmoke(self.origin + '-16 -16 65','0 0 12',HX_FRAME_TIME * 20);
|
|
CreateGreySmoke(self.origin + '-32 -32 45','0 0 12',HX_FRAME_TIME * 20);
|
|
CreateGreySmoke(self.origin + '-64 -64 70','0 0 12',HX_FRAME_TIME * 20);
|
|
|
|
self.use = SUB_Null;
|
|
self.colormap = float_null;
|
|
self.takedamage = DAMAGE_YES;
|
|
self.drawflags (+) MLS_ABSLIGHT;
|
|
snake_wake();
|
|
}
|
|
else
|
|
{
|
|
self.colormap = 3;
|
|
self.use = wake_effect;
|
|
self.takedamage = DAMAGE_NO;
|
|
snake_sleep();
|
|
}
|
|
}
|
|
|
|
/*QUAKED monster_snake (1 0.3 0) (-80 -80 0) (80 80 200) AMBUSH
|
|
Monster snake that comes to life and attacks
|
|
-------------------------FIELDS-------------------------
|
|
|
|
--------------------------------------------------------
|
|
*/
|
|
void() monster_snake =
|
|
{
|
|
if(deathmatch)
|
|
{
|
|
remove(self);
|
|
return;
|
|
}
|
|
|
|
self.solid = SOLID_BBOX;
|
|
self.movetype = MOVETYPE_NONE;
|
|
self.colormap = 3;
|
|
|
|
precache_model2 ("models/goop.mdl");
|
|
precache_model2 ("models/snake.mdl");
|
|
setmodel (self, "models/snake.mdl");
|
|
|
|
precache_sound2 ("snake/attack.wav");
|
|
precache_sound2 ("snake/hiss.wav");
|
|
precache_sound2 ("snake/wake.wav");
|
|
precache_sound2 ("snake/life.wav");
|
|
precache_sound2 ("fangel/deflect.wav");
|
|
|
|
setsize (self, '-80 -80 0', '80 80 200' );
|
|
self.health = 1200;
|
|
|
|
total_monsters += 1;
|
|
self.view_ofs = '0 0 40';
|
|
self.flags (+) FL_MONSTER;
|
|
self.use = wake_effect;
|
|
self.th_die = chunk_death;
|
|
self.thingtype = THINGTYPE_BROWNSTONE;
|
|
self.monsterclass = CLASS_BOSS;
|
|
|
|
self.counter = 0;
|
|
};
|
|
|