hexen2-hw-hc/archer.hc

899 lines
20 KiB
C++
Raw Permalink Normal View History

1999-01-27 00:00:00 +00:00
/*
* $Header: /HexenWorld/HCode/archer.hc 2 3/27/98 1:34p Mgummelt $
*/
/*
==============================================================================
Q:\art\models\monsters\archerK\final\archer.hc
==============================================================================
*/
// For building the model
$cd Q:\art\models\monsters\archerK\final
$origin 0 0 0
$base base skin
$skin skin
$flags 0
//
$frame backup1 backup2 backup3 backup4 backup5
$frame backup6 backup7 backup8
//
$frame deathA1 deathA2 deathA3 deathA4 deathA5
$frame deathA6 deathA7 deathA8 deathA9 deathA10
$frame deathA11 deathA12 deathA13 deathA14 deathA15
$frame deathA16 deathA17 deathA18 deathA19 deathA20
$frame deathA21 deathA22
//
$frame draw1 draw2 draw3 draw4 draw5
$frame draw6 draw7 draw8 draw9 draw10
$frame draw11 draw12 draw13
//
$frame duck1 duck2 duck3 duck4 duck5
$frame duck6 duck7 duck8 duck9 duck10
$frame duck11 duck12 duck13 duck14
//
$frame fire1 fire2 fire3 fire4
//
$frame pain1 pain2 pain3 pain4 pain5
$frame pain6 pain7 pain8 pain9 pain10
$frame pain11 pain12 pain13 pain14 pain15
$frame pain16
//
$frame patrol1 patrol2 patrol3 patrol4 patrol5
$frame patrol6 patrol7 patrol8 patrol9 patrol10
$frame patrol11 patrol12 patrol13 patrol14 patrol15
$frame patrol16 patrol17 patrol18 patrol19 patrol20
$frame patrol21 patrol22
//
$frame redraw1 redraw2 redraw3 redraw4 redraw5
$frame redraw6 redraw7 redraw8 redraw9 redraw10
$frame redraw11 redraw12
//
$frame tranA1 tranA2 tranA3 tranA4 tranA5
$frame tranA6 tranA7 tranA8 tranA9 tranA10
$frame tranA11 tranA12 tranA13
//
$frame tranB1 tranB2 tranB3 tranB4 tranB5
$frame tranB6 tranB7 tranB8 tranB9 tranB10
$frame tranB11 tranB12 tranB13 tranB14 tranB15
$frame tranB16
//
$frame tranC1 tranC2 tranC3 tranC4 tranC5
$frame tranC6 tranC7 tranC8 tranC9 tranC10
//
$frame tranD1 tranD2 tranD3 tranD4 tranD5
$frame tranD6 tranD7 tranD8 tranD9
//
$frame waitA1 waitA2 waitA3 waitA4 waitA5
$frame waitA6 waitA7 waitA8 waitA9 waitA10
$frame waitA11 waitA12 waitA13 waitA14 waitA15
$frame waitA16 waitA17 waitA18
//
$frame waitB1 waitB2 waitB3 waitB4 waitB5
$frame waitB6 waitB7 waitB8 waitB9 waitB10
$frame waitB11 waitB12
//
$frame walk1 walk2 walk3 walk4 walk5
$frame walk6 walk7 walk8 walk9 walk10
$frame walk11 walk12 walk13 walk14 walk15
$frame walk16
float ARCHER_STUCK = 2; // Archer can't run
float GREEN_ARROW = 0;
float RED_ARROW = 1;
float GOLD_ARROW = 2;
void archer_run(void);
void archer_stand(void);
void archerredraw(void);
void archerdraw(void);
void()archerdrawhold;
void archermissile(void);
void()archer_check_defense;
float archer_check_shot(void)
{
vector spot1,spot2;
makevectors(self.angles);
// see if any entities are in the way of the shot
spot1 = self.origin + v_right * 10 + v_up * 36;
spot2 = self.enemy.origin + self.enemy.view_ofs;
traceline (spot1, spot2, FALSE, self);
if (trace_ent.thingtype >= THINGTYPE_WEBS)
traceline(trace_endpos, spot2, FALSE, trace_ent);
if (trace_ent != self.enemy)
{
if ((trace_ent.thingtype!=THINGTYPE_GLASS) || !trace_ent.takedamage ||
(trace_ent.flags & FL_MONSTER && trace_ent.classname!="player_sheep"))
{
return FALSE;
}
}
return TRUE;
}
void archer_duck () [++ $duck1..$duck14]
{
ai_face();
if(cycle_wrapped)
{
setsize(self,'-16 -16 0','16 16 56');
if(infront(self.enemy))
self.think=archermissile;
else
self.think=archerdrawhold;
thinktime self : 0;
return;
}
else if (self.frame==$duck1)
if (self.classname == "monster_archer")
sound (self, CHAN_VOICE, "archer/growl.wav", 1, ATTN_NORM);
else
sound (self, CHAN_VOICE, "archer/pain2.wav", 1, ATTN_NORM);
else if (self.frame == $duck5)
setsize(self,'-16 -16 0','16 16 28');
else if (self.frame==$duck7)
{
archer_check_defense();
thinktime self :0.2;
}
}
void archer_check_defense()
{
entity enemy_proj;
if(random(2)>skill/10+self.skin/2)
return;
if (self.enemy.last_attack+0.5<time&&self.oldenemy.last_attack+0.5<time)
return;
enemy_proj=look_projectiles();
if(!enemy_proj)
if(lineofsight(self,self.enemy))
{
enemy_proj=self.enemy;
self.level=vlen(self.enemy.origin-self.origin)/1000;
}
else
return;
if(mezzo_check_duck(enemy_proj))
{
if(self.think==archer_duck)
self.frame=$duck6;
else
{
self.think=archer_duck;
thinktime self : 0;
}
return;
}
}
void archer_arrow_touch(void)
{
float damg;
vector delta;
if (pointcontents(self.origin) == CONTENT_SKY)
{
remove(self);
return;
}
if (self.owner.classname == "monster_archer")
damg = random(5,7);
else
{
if (self.classname == "red_arrow")
damg = random(8,12);
else // the gold arrow
damg = random(13,17);
}
if ((other.classname == "player") && (self.classname == "gold_arrow"))
{
delta = other.origin - self.origin;
other.velocity = delta * 10;
if (other.flags & FL_ONGROUND)
other.flags (-) FL_ONGROUND;
other.velocity_z = 150;
}
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 (self.classname == "gold_arrow")
CreateSpark (self.origin);
else if (self.classname == "red_arrow")
CreateRedSpark (self.origin);
else
CreateGreenSpark (self.origin);
remove(self);
}
void archer_dying (void) [++ $deathA1..$deathA22]
{
stopSound(self,CHAN_WEAPON);
//sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM);
if (self.health < -80)
{
chunk_death();
remove(self);
}
if (cycle_wrapped)
{
self.frame = $deathA22;
MakeSolidCorpse();
}
}
void() archer_die =
{
// check for gib
if (self.health < -30)
{
chunk_death();
return;
}
else
{
if (self.classname == "monster_archer")
sound (self, CHAN_VOICE, "archer/death.wav", 1, ATTN_NORM);
else
sound (self, CHAN_VOICE, "archer/death2.wav", 1, ATTN_NORM);
}
archer_dying();
};
/*-----------------------------------------
archer_pain - flinch in pain
-----------------------------------------*/
void archer_pain_anim () [++ $pain1 .. $pain16]
{
if (self.frame == $pain2)
{
if (self.classname == "monster_archer")
sound (self, CHAN_VOICE, "archer/pain.wav", 1, ATTN_NORM);
else
sound (self, CHAN_VOICE, "archer/pain2.wav", 1, ATTN_NORM);
}
if (self.frame==$pain16)
{
self.pain_finished = time + random(4,6);
archerredraw();
}
}
void archer_pain (entity attacker, float damg)
{
if ((self.frame >= $pain1) && (self.frame <= $pain16)) // Still going through pain anims
return;
if (self.attack_state == AS_MISSILE)
return;
// Hurt him, will he flinch or fight back?
setsize(self,'-16 -16 0','16 16 56');
if ((self.pain_finished > time) || (random() < .5))
{
archerredraw();
}
else
archer_pain_anim();
}
void archer_launcharrow (float arrowtype,vector spot1,vector spot2)
{
self.last_attack=time;
makevectors(self.angles);
if (arrowtype==GREEN_ARROW)
{
sound (self, CHAN_WEAPON, "archer/arrowg.wav", 1, ATTN_NORM);
Create_Missile(self,spot1,spot2, "models/akarrow.mdl","green_arrow",0,1000,archer_arrow_touch);
}
else if (arrowtype==RED_ARROW)
{
sound (self, CHAN_WEAPON, "archer/arrowr.wav", 1, ATTN_NORM);
CreateRedFlash(spot1);
Create_Missile(self,spot1,spot2,"models/akarrow.mdl","red_arrow",1,1000,archer_arrow_touch);
}
else
{
sound (self, CHAN_WEAPON, "archer/arrowg.wav", 1, ATTN_NORM);
CreateRedFlash(spot1);
Create_Missile(self,spot1,spot2,"models/akarrow.mdl","gold_arrow",2,1000,archer_arrow_touch);
}
newmis.drawflags(+)MLS_ABSLIGHT;
newmis.abslight=0.5;
thinktime newmis : 2.5;
}
/*-----------------------------------------
archerdrawdone - drawing his bow to fire
-----------------------------------------*/
void archerdrawdone () [-- $tranA7..$tranA1]
{
archer_check_defense();
walkmove(self.angles_y,0.5,FALSE);
if(visible(self.enemy))
ai_face();
if(cycle_wrapped)
{
self.pausetime = time + random(.5,2);
self.attack_state = AS_WAIT;
archer_run();
}
}
/*-----------------------------------------
archermissile - fire his arrow
-----------------------------------------*/
void archermissile () [++ $fire1..$fire4]
{
float enemy_range,chance,ok,tspeed;
vector spot1, spot2;
self.attack_state = AS_MISSILE;
if(visible(self.enemy))
ai_face();
else
{
self.think=self.th_run;
thinktime self : 0;
return;
}
if (self.frame == $fire2) // FIRE!!!!
{
makevectors(self.angles);
spot1 = self.origin + v_forward*4 + v_right * 10 + v_up * 36;
if(self.classname=="monster_archer_lord")
{
tspeed=vlen(self.enemy.velocity);
if(tspeed>100)
spot2=extrapolate_pos_for_speed(spot1,1000,self.enemy,0.3);
}
if(spot2=='0 0 0')
{
spot2 = self.enemy.origin + self.enemy.view_ofs;
traceline (spot1, spot2, FALSE, self);
if(trace_ent.thingtype>=THINGTYPE_WEBS)
traceline (trace_endpos, spot2, FALSE, trace_ent);
if (trace_ent == self.enemy)
ok=TRUE;
else if((trace_ent.health<=25||trace_ent.thingtype>=THINGTYPE_WEBS)&&trace_ent.takedamage&&(!trace_ent.flags&FL_MONSTER||trace_ent.classname=="player_sheep"))
ok=TRUE;
}
else
ok=TRUE;
if(ok)
{
enemy_range = range(self.enemy);
if (enemy_range < RANGE_MELEE) // Which arrow to use?
chance = 0.80;
else if (enemy_range < RANGE_NEAR)
chance = 0.50;
else if (enemy_range < RANGE_MID)
chance = 0.30;
else if (enemy_range < RANGE_FAR)
chance = 0.10;
if (self.classname=="monster_archer")
{
if (random(1) < chance)
archer_launcharrow(RED_ARROW,spot1,spot2);
else
archer_launcharrow(GREEN_ARROW,spot1,spot2);
}
else // Archer Lord
{
if (random(1) < chance)
archer_launcharrow(GOLD_ARROW,spot1,spot2);
else
archer_launcharrow(RED_ARROW,spot1,spot2);
}
self.attack_finished = time + random(.5,1);
if(!self.skin)
chance-=0.3;
chance+=skill/10;
// Reattack?
if (random () > chance) // Is he done?
archerdrawdone();
}
else
{
self.attack_finished = time + random();
stopSound(self,CHAN_WEAPON);
//sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM);
archer_run();
return;
}
}
if(cycle_wrapped)
{
self.frame = $draw10 ;
archerredraw();
}
else if(visible(self.enemy))
ai_face();
}
/*-----------------------------------------
archerredraw - redrawing his bow to fire
-----------------------------------------*/
void archerredraw () [++ $redraw1..$redraw12]
{
self.attack_state = AS_MISSILE;
archer_check_defense();
if (self.frame == $redraw8)
sound (self, CHAN_WEAPON, "archer/draw.wav", 1, ATTN_NORM);
if (cycle_wrapped)
archermissile();
else if(visible(self.enemy))
ai_face();
else
{
self.think=self.th_run;
thinktime self : 0;
return;
}
}
/*-----------------------------------------
archerdrawhold - waiting for right chance to attack
-----------------------------------------*/
void archerdrawhold ()
{
float chance,startframe,endframe;
if(visible(self.enemy))
ai_face();
else
{
self.think=self.th_run;
thinktime self : 0;
return;
}
archer_check_defense();
startframe=$waitB1;
endframe=$waitB12;
if (!self.spawnflags & ARCHER_STUCK)
{
if (vlen(self.enemy.origin - self.origin)<=200)
{
if(infront(self.enemy))
if(walkmove(self.angles_y+180,3,FALSE))
{
startframe=$backup1;
endframe=$backup8;
}
}
}
AdvanceFrame(startframe,endframe);
self.think=archerdrawhold;
thinktime self : 0.05;
enemy_range = range(self.enemy);
if (enemy_range < RANGE_NEAR && random() < 0.4)
archermissile();
else if (cycle_wrapped||random()<skill/20+self.skin/10)
{
if (!archer_check_shot())
{
stopSound(self,CHAN_WEAPON);
//sound (self, CHAN_WEAPON, "misc/null.wav", 1, ATTN_NORM);
self.attack_state = AS_STRAIGHT;
self.think = archer_run;
thinktime self : HX_FRAME_TIME;
return;
}
if (self.classname == "monster_archer") // Monster archer's not so smart
{
if (enemy_range <= RANGE_MELEE)
chance = 1;
else if (enemy_range <= RANGE_NEAR)
chance = 0.70;
else if (enemy_range <= RANGE_MID)
chance = 0.60;
else if (enemy_range <= RANGE_FAR)
chance = 0.50;
}
else
chance = 1;
if (random() < chance)
archermissile();
}
}
/*-----------------------------------------
archerdraw - drawing his bow to fire
-----------------------------------------*/
void archerdraw () [++ $tranA1..$tranA13]
{
archer_check_defense();
if (self.frame == $tranA1) // See if he should even try to shoot
{
if (!archer_check_shot())
{
self.attack_state = AS_STRAIGHT;
self.think = archer_run;
thinktime self : HX_FRAME_TIME;
return;
}
}
if (self.frame == $tranA6)
sound (self, CHAN_WEAPON, "archer/draw.wav", 1, ATTN_NORM);
if ((self.frame >= $tranA1) && (self.frame <= $tranA13))
walkmove(self.angles_y+180,.28,FALSE);
if (cycle_wrapped) // Archer has drawn the arrow
archerdrawhold();
else if (visible(self.enemy))
ai_face();
else
{
self.think=self.th_run;
thinktime self : 0;
return;
}
if ((random(1)<.10) && (self.frame == $walk1))
{
if (self.classname == "monster_archer")
sound (self, CHAN_BODY, "archer/growl.wav", 1, ATTN_NORM);
else
{
if (random() < .70)
sound (self, CHAN_BODY, "archer/growl2.wav", 1, ATTN_NORM);
else
sound (self, CHAN_BODY, "archer/growl3.wav", 1, ATTN_NORM);
}
}
}
/*-----------------------------------------
archer_run - run towards the enemy
-----------------------------------------*/
void archer_run(void)
{
self.think = archer_run;
thinktime self : HX_FRAME_TIME;
archer_check_defense();
if ((random(1)<.10) && (self.frame == $walk1))
{
if (self.classname == "monster_archer")
sound (self, CHAN_VOICE, "archer/growl.wav", 1, ATTN_NORM);
else
{
if (random() < .70)
sound (self, CHAN_VOICE, "archer/growl2.wav", 1, ATTN_NORM);
else
sound (self, CHAN_VOICE, "archer/growl3.wav", 1, ATTN_NORM);
}
}
if ((self.spawnflags & ARCHER_STUCK) || (self.attack_state == AS_WAIT))
{
AdvanceFrame($waitA1,$waitA18);
ai_run(0);
}
else
{
AdvanceFrame($walk1,$walk16);
ai_run(4);
}
}
/*-----------------------------------------
archer_walk - walking his beat
-----------------------------------------*/
void archer_walk(void) [++ $patrol1..$patrol22]
{
thinktime self : HX_FRAME_TIME + .01; // Make him move a little slower so his run will look faster
archer_check_defense();
if ((random()<.05) && (self.frame == $patrol1))
{
if (self.classname == "monster_archer")
sound (self, CHAN_VOICE, "archer/growl.wav", 1, ATTN_NORM);
else
{
if (random() < .70)
sound (self, CHAN_VOICE, "archer/growl2.wav", 1, ATTN_NORM);
else
sound (self, CHAN_VOICE, "archer/growl3.wav", 1, ATTN_NORM);
}
}
if (self.spawnflags & ARCHER_STUCK) // No moving
{
self.frame = $patrol1; // FIXME: this should be the wait animations
ai_walk(0);
}
else
ai_walk(2.3);
}
/*-----------------------------------------
archer_stand - standing and waiting
-----------------------------------------*/
void archer_stand(void) [++ $waitA1..$waitA18]
{
if (random()<0.5)
{
archer_check_defense();
ai_stand();
}
}
/*QUAKED monster_archer (1 0.3 0) (-16 -16 0) (16 16 50) AMBUSH STUCK JUMP PLAY_DEAD DORMANT
The Archer Knight monster
-------------------------FIELDS-------------------------
Health : 80
Experience Pts: 25
Favorite TV shows: Friends & Baywatch
Favorite Color: Blue
Likes: Shooting arrows into people and long walks along the beach
Dislikes: Anything having to do with Pauly Shore
--------------------------------------------------------
*/
void monster_archer ()
{
if (deathmatch)
{
remove(self);
return;
}
if (!self.flags2 & FL_SUMMONED)
{
precache_archer();
}
CreateEntityNew(self,ENT_ARCHER,"models/archer.mdl",archer_die);
self.th_stand = archer_stand;
self.th_walk = archer_walk;
self.th_run = archer_run;
self.th_melee = archerdraw;
self.th_missile = archerdraw;
self.th_pain = archer_pain;
self.decap = 0;
self.headmodel = "models/archerhd.mdl";
self.mintel = 7;
self.monsterclass = CLASS_GRUNT;
self.experience_value = 25;
self.health = 80;
self.flags (+) FL_MONSTER;
self.yaw_speed = 10;
self.view_ofs = '0 0 40';
walkmonster_start();
}
/*QUAKED monster_archer_lord (1 0.3 0) (-16 -16 0) (16 16 50) AMBUSH STUCK JUMP PLAY_DEAD DORMANT
The Archer Lord monster
-------------------------FIELDS-------------------------
Health : 325
Experience Pts: 200
Favorite Cities: Madrid & Las Vegas
Favorite Flower: Orchid
What people don't know about me: I cry at sad movies
What people say when they see me: Don't shoot!! Don't shoot!!
--------------------------------------------------------
*/
void monster_archer_lord ()
{
if (deathmatch)
{
remove(self);
return;
}
if (!self.flags2 & FL_SUMMONED)
{
precache_model("models/archer.mdl");
precache_model("models/archerhd.mdl");
precache_model("models/gspark.spr");
precache_sound ("archer/arrowg.wav");
precache_sound ("archer/arrowr.wav");
precache_sound ("archer/growl2.wav");
precache_sound ("archer/growl3.wav");
precache_sound ("archer/pain2.wav");
precache_sound ("archer/sight2.wav");
precache_sound ("archer/death2.wav");
precache_sound ("archer/draw.wav");
}
CreateEntityNew(self,ENT_ARCHER,"models/archer.mdl",archer_die);
self.th_stand = archer_stand;
self.th_walk = archer_walk;
self.th_run = archer_run;
self.th_melee = archerdraw;
self.th_missile = archerdraw;
self.th_pain = archer_pain;
self.decap = 0;
self.headmodel = "models/archerhd.mdl";
self.mintel = 7;
self.monsterclass = CLASS_HENCHMAN;
self.experience_value = 200;
self.health = 325;
self.skin = 1;
self.flags (+) FL_MONSTER;
self.yaw_speed = 10;
self.view_ofs = '0 0 40';
walkmonster_start();
}
/*
=================
ArcherCheckAttack
=================
*/
float ArcherCheckAttack (void)
{
local vector spot1, spot2;
local entity targ;
local float chance;
// if (enemy_range <= RANGE_MELEE) // Enemy is too close...attack
// {
// self.attack_state = AS_MISSILE;
self.pain_finished = 0;
// self.attack_finished = 0;
// }
if (self.attack_finished > time)
return FALSE;
if (!enemy_vis)
return FALSE;
if (enemy_range == RANGE_FAR)
{
if (self.attack_state != AS_STRAIGHT)
{
self.attack_state = AS_STRAIGHT;
archer_run ();
}
return FALSE;
}
targ = self.enemy;
makevectors(self.angles);
// see if any entities are in the way of the shot
spot1 = self.origin + v_right * 10 + v_up * 36;
spot2 = targ.origin + targ.view_ofs;
traceline (spot1, spot2, FALSE, self);
if(trace_ent.thingtype>=THINGTYPE_WEBS)
traceline (trace_endpos, spot2, FALSE, trace_ent);
if (trace_ent != targ)
if((trace_ent.health>25&&trace_ent.thingtype!=THINGTYPE_GLASS)||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep"))
{
self.attack_state = AS_STRAIGHT;
return FALSE;
}
// Chances of attack
// 50% at MID range
// 65% at NEAR range
// 80% at MELEE range
enemy_range = range(self.enemy);
if (enemy_range == RANGE_MELEE)
chance = 0.40;
else if (enemy_range == RANGE_NEAR)
chance = 0.3;
else if (enemy_range == RANGE_MID)
chance = 0.2;
else
chance = 0;
if ((random () < chance) && (self.attack_state != AS_MISSILE)) // Will he attack?
{
self.attack_state = AS_MISSILE;
return TRUE;
}
if (enemy_range == RANGE_MID)
{
if (random (1) < .5) // Will he side step?
self.attack_state = AS_SLIDING;
else
self.attack_state = AS_STRAIGHT;
}
else
{
if (self.attack_state != AS_SLIDING)
self.attack_state = AS_SLIDING;
}
return FALSE;
}