519 lines
14 KiB
C++
519 lines
14 KiB
C++
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
Teneb
|
||
|
coded by Michael Rogers a.k.a Xsniper
|
||
|
http://xsniper.virtualave.net/
|
||
|
some code done by iD software
|
||
|
|
||
|
This is one of the coolest monsters in the game, was very fun brining him to life!
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
$cd /models/enemies
|
||
|
$origin 0 0 24
|
||
|
$base base
|
||
|
$skin base
|
||
|
|
||
|
$frame idle101 idle102 idle103 idle104 idle105 idle106 idle107 idle108 idle109 idle110
|
||
|
$frame idle111 idle112 idle113 idle114 idle115 idle116 idle117 idle118 idle119 idle120
|
||
|
|
||
|
$frame idle201 idle202 idle203 idle204 idle205 idle206 idle207 idle208 idle209 idle210
|
||
|
$frame idle211 idle212 idle213 idle214 idle215 idle216 idle217 idle218 idle219 idle220
|
||
|
|
||
|
$frame run01 run02 run03 run04 run05 run06
|
||
|
|
||
|
$frame walk01 walk02 walk03 walk04 walk05 walk06 walk07 walk08 walk09 walk10
|
||
|
$frame walk11 walk12
|
||
|
|
||
|
$frame attack101 attack102 attack103 attack104 attack105 attack106 attack107 attack108 attack109
|
||
|
|
||
|
$frame attack201 attack202 attack203
|
||
|
|
||
|
$frame attack301 attack302 attack303 attack304
|
||
|
|
||
|
$frame attack401 attack402 attack403 attack404
|
||
|
|
||
|
$frame attack501 attack502 attack503 attack504 attack505 attack506 attack507
|
||
|
|
||
|
$frame pain101 pain102 pain103 pain104
|
||
|
|
||
|
$frame pain201 pain202 pain203 pain204 pain205
|
||
|
|
||
|
$frame death101 death102 death103 death104 death105 death106 death006
|
||
|
|
||
|
void() teneb_jump1;
|
||
|
void() teneb_jump7;
|
||
|
void() teneb_idlesound;
|
||
|
void() teneb_goinvisible;
|
||
|
void() teneb_govisible;
|
||
|
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
teneb
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
ChecktenebMelee
|
||
|
|
||
|
Returns TRUE if a melee attack would hit right now
|
||
|
==============
|
||
|
*/
|
||
|
float() ChecktenebMelee =
|
||
|
{
|
||
|
if (enemy_range == RANGE_MELEE)
|
||
|
{ // FIXME: check canreach
|
||
|
self.attack_state = AS_MELEE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
==============
|
||
|
ChecktenebJump
|
||
|
|
||
|
==============
|
||
|
*/
|
||
|
float() ChecktenebJump =
|
||
|
{
|
||
|
local vector dist;
|
||
|
local float d;
|
||
|
|
||
|
if (self.origin_z + self.mins_z > self.enemy.origin_z + self.enemy.mins_z
|
||
|
+ 0.75 * self.enemy.size_z)
|
||
|
return FALSE;
|
||
|
|
||
|
if (self.origin_z + self.maxs_z < self.enemy.origin_z + self.enemy.mins_z
|
||
|
+ 0.25 * self.enemy.size_z)
|
||
|
return FALSE;
|
||
|
|
||
|
dist = self.enemy.origin - self.origin;
|
||
|
dist_z = 0;
|
||
|
|
||
|
d = vlen(dist);
|
||
|
|
||
|
if (d < 100)
|
||
|
return FALSE;
|
||
|
|
||
|
if (d > 200)
|
||
|
{
|
||
|
if (random() < 0.9)
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
};
|
||
|
|
||
|
float() tenebCheckAttack =
|
||
|
{
|
||
|
local vector vec;
|
||
|
|
||
|
// if close enough for slashing, go for it
|
||
|
if (ChecktenebMelee ())
|
||
|
{
|
||
|
self.attack_state = AS_MELEE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
if (ChecktenebJump ())
|
||
|
{
|
||
|
self.attack_state = AS_MISSILE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
};
|
||
|
|
||
|
|
||
|
//===========================================================================
|
||
|
|
||
|
void(float side) teneb_Melee =
|
||
|
{
|
||
|
local float ldmg;
|
||
|
local vector delta;
|
||
|
|
||
|
ai_face ();
|
||
|
walkmove (self.ideal_yaw, 12); // allow a little closing
|
||
|
|
||
|
delta = self.enemy.origin - self.origin;
|
||
|
|
||
|
if (vlen(delta) > 100)
|
||
|
return;
|
||
|
if (!CanDamage (self.enemy, self))
|
||
|
return;
|
||
|
|
||
|
sound (self, CHAN_WEAPON, "weapons/demon/clawhit.wav", 1, ATTN_NORM);
|
||
|
ldmg = 10 + 5*random();
|
||
|
T_Damage (self.enemy, self, self, ldmg);
|
||
|
|
||
|
makevectors (self.angles);
|
||
|
};
|
||
|
|
||
|
|
||
|
void() teneb_JumpTouch =
|
||
|
{
|
||
|
local float ldmg;
|
||
|
|
||
|
if (self.health <= 0)
|
||
|
return;
|
||
|
|
||
|
if (other.takedamage)
|
||
|
{
|
||
|
if ( vlen(self.velocity) > 400 )
|
||
|
{
|
||
|
ldmg = 40 + 10*random();
|
||
|
T_Damage (other, self, self, ldmg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!checkbottom(self))
|
||
|
{
|
||
|
if (self.flags & FL_ONGROUND)
|
||
|
{ // jump randomly to not get hung up
|
||
|
//dprint ("popjump\n");
|
||
|
self.touch = SUB_Null;
|
||
|
self.think = teneb_jump1;
|
||
|
self.nextthink = time + 0.1;
|
||
|
|
||
|
// self.velocity_x = (random() - 0.5) * 600;
|
||
|
// self.velocity_y = (random() - 0.5) * 600;
|
||
|
// self.velocity_z = 200;
|
||
|
// self.flags = self.flags - FL_ONGROUND;
|
||
|
}
|
||
|
return; // not on ground yet
|
||
|
}
|
||
|
|
||
|
self.touch = SUB_Null;
|
||
|
self.think = teneb_jump7;
|
||
|
self.nextthink = time + 0.1;
|
||
|
};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_stand1 =[ $idle101, teneb_stand2 ] {ai_stand();teneb_idlesound();};
|
||
|
void() teneb_stand2 =[ $idle102, teneb_stand3 ] {ai_stand();};
|
||
|
void() teneb_stand3 =[ $idle103, teneb_stand4 ] {ai_stand();};
|
||
|
void() teneb_stand4 =[ $idle104, teneb_stand5 ] {ai_stand();};
|
||
|
void() teneb_stand5 =[ $idle105, teneb_stand6 ] {ai_stand();};
|
||
|
void() teneb_stand6 =[ $idle106, teneb_stand7 ] {ai_stand();};
|
||
|
void() teneb_stand7 =[ $idle107, teneb_stand8 ] {ai_stand();};
|
||
|
void() teneb_stand8 =[ $idle108, teneb_stand9 ] {ai_stand();};
|
||
|
void() teneb_stand9 =[ $idle109, teneb_stand10 ] {ai_stand();};
|
||
|
void() teneb_stand10 =[ $idle110, teneb_stand11 ] {ai_stand();};
|
||
|
void() teneb_stand11 =[ $idle111, teneb_stand12 ] {ai_stand();};
|
||
|
void() teneb_stand12 =[ $idle112, teneb_stand13 ] {ai_stand();};
|
||
|
void() teneb_stand13 =[ $idle113, teneb_stand14 ] {ai_stand();};
|
||
|
void() teneb_stand14 =[ $idle104, teneb_stand15 ] {ai_stand();};
|
||
|
void() teneb_stand15 =[ $idle105, teneb_stand16 ] {ai_stand();};
|
||
|
void() teneb_stand16 =[ $idle106, teneb_stand17 ] {ai_stand();};
|
||
|
void() teneb_stand17 =[ $idle107, teneb_stand18 ] {ai_stand();};
|
||
|
void() teneb_stand18 =[ $idle108, teneb_stand19 ] {ai_stand();};
|
||
|
void() teneb_stand19 =[ $idle109, teneb_stand20 ] {ai_stand();};
|
||
|
void() teneb_stand20 =[ $idle120, teneb_stand1 ] {ai_stand();};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_walk1 =[ $walk01, teneb_walk2 ] {ai_walk(8);teneb_idlesound();};
|
||
|
void() teneb_walk2 =[ $walk02, teneb_walk3 ] {ai_walk(6);};
|
||
|
void() teneb_walk3 =[ $walk03, teneb_walk4 ] {ai_walk(6);};
|
||
|
void() teneb_walk4 =[ $walk04, teneb_walk5 ] {ai_walk(7);};
|
||
|
void() teneb_walk5 =[ $walk05, teneb_walk6 ] {ai_walk(4);};
|
||
|
void() teneb_walk6 =[ $walk06, teneb_walk7 ] {ai_walk(6);};
|
||
|
void() teneb_walk7 =[ $walk07, teneb_walk8 ] {ai_walk(10);};
|
||
|
void() teneb_walk8 =[ $walk08, teneb_walk9 ] {ai_walk(10);};
|
||
|
void() teneb_walk9 =[ $walk09, teneb_walk10 ] {ai_walk(4);};
|
||
|
void() teneb_walk10 =[ $walk10, teneb_walk11 ] {ai_walk(6);};
|
||
|
void() teneb_walk11 =[ $walk11, teneb_walk12 ] {ai_walk(10);};
|
||
|
void() teneb_walk12 =[ $walk12, teneb_walk1 ] {ai_walk(10);};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_run1 =[ $run01, teneb_run2 ] {ai_run(20);teneb_idlesound();};
|
||
|
void() teneb_run2 =[ $run02, teneb_run3 ] {ai_run(15);};
|
||
|
void() teneb_run3 =[ $run03, teneb_run4 ] {ai_run(36);};
|
||
|
void() teneb_run4 =[ $run04, teneb_run5 ] {ai_run(20);};
|
||
|
void() teneb_run5 =[ $run05, teneb_run6 ] {ai_run(15);};
|
||
|
void() teneb_run6 =[ $run06, teneb_run1 ] {ai_run(36);};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_jump1 =[ $attack501, teneb_jump2 ] {ai_face();};
|
||
|
void() teneb_jump2 =[ $attack502, teneb_jump3 ] {ai_face();};
|
||
|
void() teneb_jump3 =[ $attack503, teneb_jump4 ]
|
||
|
{
|
||
|
ai_face();
|
||
|
|
||
|
self.touch = teneb_JumpTouch;
|
||
|
makevectors (self.angles);
|
||
|
self.origin_z = self.origin_z + 1;
|
||
|
self.velocity = v_forward * 600 + '0 0 250';
|
||
|
if (self.flags & FL_ONGROUND)
|
||
|
self.flags = self.flags - FL_ONGROUND;
|
||
|
};
|
||
|
void() teneb_jump4 =[ $attack504, teneb_jump5 ] {};
|
||
|
void() teneb_jump5 =[ $attack505, teneb_jump6 ] {};
|
||
|
void() teneb_jump6 =[ $attack506, teneb_jump1 ]
|
||
|
{
|
||
|
self.nextthink = time + 3;
|
||
|
// if three seconds pass, assume teneb is stuck and jump again
|
||
|
};
|
||
|
void() teneb_jump7 =[ $attack507, teneb_run1 ] {};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_atta1 =[ $attack101, teneb_atta2 ] {ai_charge(20);teneb_govisible();};
|
||
|
void() teneb_atta2 =[ $attack102, teneb_atta3 ] {ai_charge(25);};
|
||
|
void() teneb_atta3 =[ $attack103, teneb_atta4 ] {ai_charge(30);teneb_Melee(200);};
|
||
|
void() teneb_atta4 =[ $attack104, teneb_atta5 ] {ai_charge(25);};
|
||
|
void() teneb_atta5 =[ $attack105, teneb_atta6 ] {ai_charge(20);};
|
||
|
void() teneb_atta6 =[ $attack106, teneb_atta7 ] {ai_charge(30);};
|
||
|
void() teneb_atta7 =[ $attack107, teneb_atta8 ] {ai_charge(25);teneb_Melee(-200);};
|
||
|
void() teneb_atta8 =[ $attack108, teneb_atta9 ] {ai_charge(20);};
|
||
|
void() teneb_atta9 =[ $attack109, teneb_run1 ] {ai_charge(30);teneb_goinvisible();};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_attb1 =[ $attack301, teneb_attb2 ] {ai_charge(20);teneb_govisible();ai_melee();};
|
||
|
void() teneb_attb2 =[ $attack302, teneb_attb3 ] {ai_charge(25);ai_melee();};
|
||
|
void() teneb_attb3 =[ $attack303, teneb_attb4 ] {ai_charge(30);ai_melee();};
|
||
|
void() teneb_attb4 =[ $attack304, teneb_run1 ] {ai_charge(25);ai_melee();teneb_goinvisible();};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_paina1 =[ $pain101, teneb_paina2 ] {};
|
||
|
void() teneb_paina2 =[ $pain102, teneb_paina3 ] {};
|
||
|
void() teneb_paina3 =[ $pain103, teneb_paina4 ] {};
|
||
|
void() teneb_paina4 =[ $pain104, teneb_run1 ] {};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_painb1 =[ $pain201, teneb_painb2 ] {};
|
||
|
void() teneb_painb2 =[ $pain202, teneb_painb3 ] {};
|
||
|
void() teneb_painb3 =[ $pain203, teneb_painb4 ] {};
|
||
|
void() teneb_painb4 =[ $pain204, teneb_painb5 ] {};
|
||
|
void() teneb_painb5 =[ $pain205, teneb_run1 ] {};
|
||
|
|
||
|
void(entity attacker, float damage) teneb_pain =
|
||
|
{
|
||
|
if (self.touch == teneb_JumpTouch)
|
||
|
return;
|
||
|
|
||
|
if (self.pain_finished > time)
|
||
|
return;
|
||
|
|
||
|
self.pain_finished = time + 1;
|
||
|
|
||
|
//OUCH THAT HURT!
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/pain1.wav", 1, ATTN_NORM);
|
||
|
|
||
|
if (random()*200 > damage)
|
||
|
return; // didn't flinch
|
||
|
|
||
|
if (random() <= 0.5)
|
||
|
teneb_paina1 ();
|
||
|
else
|
||
|
teneb_painb1 ();
|
||
|
};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_die1 =[ $death101, teneb_die2 ] {};
|
||
|
void() teneb_die2 =[ $death102, teneb_die3 ] {};
|
||
|
void() teneb_die3 =[ $death103, teneb_die4 ] {};
|
||
|
void() teneb_die4 =[ $death104, teneb_die5 ] {};
|
||
|
void() teneb_die5 =[ $death105, teneb_die6 ] {};
|
||
|
void() teneb_die6 =[ $death106, teneb_die7 ] {self.solid = SOLID_NOT;};
|
||
|
void() teneb_die7 =[ $death006, teneb_die7 ] {banish_monster();};
|
||
|
|
||
|
void() teneb_die =
|
||
|
{
|
||
|
//ARG I'M DIEING TELL THE WORLD ABOUT IT! MAKE SOME NOISE BABY!
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/death.wav", 1, ATTN_NORM);
|
||
|
|
||
|
//Make sure they can see me die
|
||
|
teneb_govisible();
|
||
|
|
||
|
//Just die already!
|
||
|
teneb_die1 ();
|
||
|
};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
void() teneb_attack =
|
||
|
{
|
||
|
//TAKE THIS! GRRR I SOUND FEROCIOUS!
|
||
|
if (random() <= 0.9)
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/pain2.wav", 1, ATTN_NORM);
|
||
|
|
||
|
//Hmm... should I claw him or bite him?
|
||
|
if (random() <= 0.7)
|
||
|
teneb_atta1(); //I think I'll claw him most of the time!
|
||
|
else
|
||
|
teneb_attb1(); //Sometimes I like to chew on things... ;)
|
||
|
};
|
||
|
|
||
|
void() teneb_missile =
|
||
|
{
|
||
|
//TAKE THIS! GRRR I SOUND FEROCIOUS!
|
||
|
if (random() <= 0.9)
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/pain2.wav", 1, ATTN_NORM);
|
||
|
|
||
|
//I'm gonna jump all over him!
|
||
|
teneb_jump1();
|
||
|
};
|
||
|
|
||
|
void() teneb_idlesound =
|
||
|
{
|
||
|
local float r;
|
||
|
|
||
|
//make a lot of noise, but not all the time
|
||
|
if (random() <= 0.7)
|
||
|
{
|
||
|
//pick one of three random sounds
|
||
|
r = random();
|
||
|
if (r <= 0.33)
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/idle2.wav", 1, ATTN_NORM);
|
||
|
else if (r <= 0.66)
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/idle3.wav", 1, ATTN_NORM);
|
||
|
else
|
||
|
sound (self, CHAN_VOICE, "enemy/teneb/idle4.wav", 1, ATTN_NORM);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void() teneb_goinvisible =
|
||
|
{
|
||
|
//setmodel (self, "");
|
||
|
//setsize (self, '-32 -32 -24', '32 32 64');
|
||
|
self.alpha = set_fade(0.2);
|
||
|
};
|
||
|
|
||
|
void() teneb_govisible =
|
||
|
{
|
||
|
//setmodel (self, "models/enemies/teneb.md2");
|
||
|
//setsize (self, '-32 -32 -24', '32 32 64');
|
||
|
self.alpha = 1;
|
||
|
};
|
||
|
|
||
|
//============================================================================
|
||
|
|
||
|
/*QUAKED monster_teneb (1 0 0) (-32 -32 -24) (32 32 64) Ambush
|
||
|
|
||
|
*/
|
||
|
void() monster_teneb =
|
||
|
{
|
||
|
if (deathmatch)
|
||
|
{
|
||
|
remove(self);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
precache_model ("models/enemies/teneb.md2");
|
||
|
|
||
|
precache_sound ("enemy/teneb/death.wav"); //death sound
|
||
|
precache_sound ("enemy/teneb/idle2.wav"); //idle sound
|
||
|
precache_sound ("enemy/teneb/idle3.wav"); //idle sound
|
||
|
precache_sound ("enemy/teneb/idle4.wav"); //idle sound
|
||
|
precache_sound ("enemy/teneb/pain1.wav"); //pain sound
|
||
|
precache_sound ("enemy/teneb/pain2.wav"); //attack sound
|
||
|
precache_sound ("weapons/demon/clawhit.wav"); //attack sound
|
||
|
|
||
|
self.solid = SOLID_SLIDEBOX;
|
||
|
self.movetype = MOVETYPE_STEP;
|
||
|
|
||
|
setmodel (self, "models/enemies/teneb.md2");
|
||
|
|
||
|
setsize (self, '-32 -32 -24', '32 32 64');
|
||
|
// setsize (self, VEC_HULL2_MIN, VEC_HULL2_MAX);
|
||
|
self.health = set_health(300);
|
||
|
self.speed = set_speed(900);
|
||
|
self.dtype = 4; //dodge type do nothing
|
||
|
|
||
|
self.th_stand = teneb_stand1;
|
||
|
self.th_walk = teneb_walk1;
|
||
|
self.th_run = teneb_run1;
|
||
|
self.th_die = teneb_die;
|
||
|
self.th_melee = teneb_attack;
|
||
|
self.th_missile = teneb_missile;
|
||
|
self.th_pain = teneb_pain;
|
||
|
|
||
|
//my new classname is?
|
||
|
self.classname = "monster_teneb";
|
||
|
|
||
|
//set up our monflag
|
||
|
self.monflag = "TRUE";
|
||
|
|
||
|
//difficulty optimization
|
||
|
remove_monster();
|
||
|
|
||
|
//start out invisible
|
||
|
teneb_goinvisible();
|
||
|
|
||
|
walkmonster_start();
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
=================
|
||
|
create_tenebbot
|
||
|
|
||
|
Creates a Teneb at the specified 'spot'.
|
||
|
This is used by demon portals in Flood Co-op.
|
||
|
=================
|
||
|
*/
|
||
|
void(vector spot) create_tenebbot =
|
||
|
{
|
||
|
local entity bot;
|
||
|
|
||
|
// start entity and place it in world
|
||
|
bot = spawn();
|
||
|
bot.angles = self.angles;
|
||
|
setorigin (bot, spot);
|
||
|
bot.fixangle = TRUE;
|
||
|
spawn_tfog (bot.origin);
|
||
|
spawn_tdeath (bot.origin, bot);
|
||
|
|
||
|
// set size and shape
|
||
|
bot.solid = SOLID_SLIDEBOX;
|
||
|
bot.movetype = MOVETYPE_STEP;
|
||
|
setmodel (bot, "models/enemies/teneb.md2");
|
||
|
setsize (bot, '-32 -32 -24', '32 32 64');
|
||
|
bot.flags = bot.flags | FL_MONSTER;
|
||
|
bot.takedamage = DAMAGE_AIM;
|
||
|
|
||
|
// define his animation
|
||
|
bot.th_stand = teneb_stand1;
|
||
|
bot.th_walk = teneb_walk1;
|
||
|
bot.th_run = teneb_run1;
|
||
|
bot.th_die = teneb_die;
|
||
|
bot.th_melee = teneb_attack;
|
||
|
bot.th_missile = teneb_missile;
|
||
|
bot.th_pain = teneb_pain;
|
||
|
bot.health = set_health(300);
|
||
|
bot.speed = set_speed(900);
|
||
|
bot.dtype = 4; //dodge type do nothing
|
||
|
|
||
|
// polish him up
|
||
|
bot.classname = "monster_teneb";
|
||
|
bot.ideal_yaw = bot.angles * '0 1 0';
|
||
|
bot.yaw_speed = 120;
|
||
|
bot.view_ofs = '0 0 22';
|
||
|
|
||
|
//set up our monflag
|
||
|
bot.monflag = "TRUE";
|
||
|
|
||
|
//start out invisible
|
||
|
bot.alpha = set_fade(0.2);
|
||
|
|
||
|
// begin his thinking
|
||
|
bot.nextthink = time + 0.1 + random();
|
||
|
bot.think = th_stuck;
|
||
|
|
||
|
};
|
||
|
|
||
|
|