hexen2-siege-hc/newimp.hc
1998-06-24 00:00:00 +00:00

1462 lines
32 KiB
C++

/*
* $Header: /HexenWorld/Siege/newimp.hc 4 6/01/98 2:49a Mgummelt $
*/
/*
==============================================================================
ICE IMP
==============================================================================
*/
// For building the model
$cd c:\model\imp // Directory to find model in
$origin 0 0 0
// baseframe is in iceimp.3ds
$base iceimp
$flags 0
// skin is in iceimp.lbm
$skin skin
$skin skinice
$frame death1 death2 death3 death4 death5
$frame death6 death7 death8 death9 death10
$frame death11 death12 death13 death14
//
$frame impfir1 impfir2 impfir3 impfir4 impfir5
$frame impfir6 impfir7 impfir8 impfir9 impfir10
$frame impfir11 impfir12 impfir13 impfir14 impfir15
$frame impfir16 impfir17 impfir18 impfir19 impfir20
$frame impfir21
//
$frame impfly1 impfly2 impfly3 impfly4 impfly5
$frame impfly6 impfly7 impfly8 impfly9 impfly10
$frame impfly11 impfly12 impfly13 impfly14 impfly15
$frame impfly16 impfly17 impfly18 impfly19 impfly20
//
$frame impup1 impup2 impup3 impup4 impup5
$frame impup6 impup7 impup8 impup9 impup10
$frame impup11 impup12 impup13 impup14 impup15
$frame impup16 impup17 impup18 impup19 impup20
$frame impup21 impup22 impup23
//
$frame impwat1 impwat2 impwat3 impwat4 impwat5
$frame impwat6 impwat7 impwat8 impwat9 impwat10
$frame impwat11 impwat12 impwat13 impwat14 impwat15
$frame impwat16 impwat17 impwat18 impwat19 impwat20
$frame impwat21 impwat22 impwat23 impwat24
//
$frame swoop1 swoop2 swoop3 swoop4 swoop5
$frame swoop6 swoop7 swoop8 swoop9 swoop10
$frame swoop11 swoop12 swoop13 swoop14 swoop15
$frame swoop16 swoop17 swoop18 swoop19 swoop20
//
$frame swpcyc1 swpcyc2 swpcyc3 swpcyc4
//
$frame swpend1 swpend2 swpend3 swpend4 swpend5
$frame swpend6 swpend7 swpend8 swpend9 swpend10
$frame swpend11 swpend12 swpend13 swpend14 swpend15
//
$frame swpout1 swpout2 swpout3 swpout4 swpout5
$frame swpout6 swpout7 swpout8 swpout9 swpout10
$frame swpout11 swpout12 swpout13 swpout14 swpout15
//============================================================================
float MONSTER_STAND_GROUND = 1;
float MONSTER_HOVER = 2;
float SF_IMP_DORMANT = 16;
float PICKUP = 32;
float AS_FERRY = 6;
void()imp_fly;
void()imp_hover;
void()imp_abort_swoop;
float()imp_find_target;
/*
================================================================
checkenemy()
Checks to see if enemy is of the same monstertype and old enemy
is alive and visible. If so, changes back to it's last enemy.
================================================================
*/
void checkenemy (void)
{
entity oldtarget;
if(self.enemy.classname=="player"&&self.enemy.flags2&FL_ALIVE)
return;
if(self.oldenemy.classname=="player"&&(self.oldenemy.flags2&FL_ALIVE)&&visible(self.oldenemy))
{ //FIXME: Whoops! These are unions!!!
if((self.spiderType&&self.enemy.spiderType)||(self.scorpionType&&self.enemy.scorpionType))
self.enemy=self.oldenemy;
else
{
oldtarget=self.enemy;
self.enemy=self.oldenemy;
self.oldenemy=oldtarget;
}
self.goalentity=self.enemy;
}
}
/*
================================================================
fov()
Field-Of-View
Returns TRUE if vector from entity "from" to entity "targ" is
within "scope" degrees of entity "from"'s forward angle.
================================================================
*/
float fov(entity targ,entity from,float scope)
{
vector spot1,spot2;
float dot;
spot1=from.origin+from.proj_ofs;
spot2=(targ.absmin+targ.absmax)*0.5;
if(from.classname=="player")
makevectors(from.v_angle);
else
makevectors(from.angles);
// scope=1 - (scope/180);//converts angles into %
dot=normalize(spot2-spot1)*v_forward;
dot=180 - (dot*180);
// dprintf("FOV value : %s\n",dot);
if(dot<=scope)
return TRUE;
return FALSE;
}
void check_pos_enemy ()
{
if(!self.mintel)
return;
if(!visible(self.enemy))
{
self.attack_state = AS_STRAIGHT;
SetNextWaypoint();
}
else
{
self.goalentity=self.enemy;
self.wallspot=(self.enemy.absmin+self.enemy.absmax)*0.5;
}
}
float clear_path (entity targ)
{
vector destiny,org;
destiny=targ.origin+targ.proj_ofs;
org=(self.absmin+self.absmax)*0.5;
self.attack_state = AS_STRAIGHT;
tracearea (org, destiny, '-16 -16 0','16 16 28',FALSE,self);
if (trace_ent != targ)
{
self.attack_state = AS_SLIDING;
return FALSE;
}
return TRUE;
}
float imp_up_amounts[23] =
{
0,
0,
0,
0,
0,
0,
0,
0,
1,
2,
2,
2,
3,
3,
3,
3,
4,
4,
5,
6,
7,
5,
2
};
float imp_fly_amounts[20] =
{
-0.4,
-0.2,
0,
0,
0.2,
0.4,
0.4,
0.6,
0.6,
0.8,
0.6,
0.4,
0,
0,
-0.2,
-0.4,
-0.8,
-0.8,
-0.8,
-0.4
};
void imp_drop (void)
{
self.attack_state=AS_STRAIGHT;
if(self.classname=="monster_imp_lord")
{
if(self.enemy.classname!="player")
self.enemy.health=10;
self.enemy.movetype=MOVETYPE_BOUNCE;
self.enemy.mass=10000;
self.enemy.velocity_z=-300;
}
}
void imp_die ()
{
if(self.health<-40)
{
chunk_death();
return;
}
if(self.frame!=$death14)
AdvanceFrame($death1,$death14);
if(self.frame==$death1)
{
sound (self, CHAN_WEAPON, "imp/die.wav", 1, ATTN_NORM);
makevectors (self.angles);
self.movetype = MOVETYPE_BOUNCE;
self.velocity_z = 0;
self.velocity += v_forward*80;
if(self.attack_state==AS_FERRY)
imp_drop();
}
if(self.frame==$death8)
if (self.flags & FL_ONGROUND)
{
// when he has finally rested on the ground, finish the death
sound(self,CHAN_BODY,"player/land.wav",1,ATTN_NORM);
self.velocity_x = self.velocity_y = 0.0;
}
else
self.frame==$death7;
if(self.flags & FL_ONGROUND&&!self.aflag)
{
vector new_angles,new_angles2,old_forward,old_right;
float dot,mod;
// dprint("Angles fixed\n");
self.aflag=TRUE;
makevectors(self.angles);
old_forward=v_forward;
old_right=v_right;
traceline(self.origin,self.origin-'0 0 24',TRUE,self);
new_angles=vectoangles(trace_plane_normal);
// dprintf("Pitch of slope: %s\n",new_angles_x);
new_angles_x=(90-new_angles_x)*-1;//Gets actual slope
dprintf("actual slope: %s\n",new_angles_x);
new_angles2='0 0 0';
new_angles2_y=new_angles_y;
makevectors(new_angles2);
mod=v_forward*old_right;
// dprintf("Mod: %s\n",mod);
if(mod<0)
mod=-1;
else
mod=1;
dot=v_forward*old_forward;
// dprintf("difference in yaw: %s\n",dot);
self.angles_x=dot*new_angles_x*mod;
// dprintf("New pitch: %s\n",self.angles_x);
self.angles_z=(1-dot)*new_angles_x*mod*-1;
// dprintf("New roll: %s\n",self.angles_z);
}
if(self.frame==$death14 &&self.flags&FL_ONGROUND)
MakeSolidCorpse();
else if(self.health<-40)
chunk_death();
//check to see if health > 0. if so, come back alive
//This could happen by magic or by fire for fire imp or cold for ice imp
else if(self.health>0)
{
self.takedamage = DAMAGE_YES;
self.flags (+) FL_FLY;
self.flags2 (+) FL_ALIVE;
self.solid = SOLID_SLIDEBOX;
self.movetype = MOVETYPE_FLY;
if(self.classname=="monster_imp_lord")
{
setsize (self, '-32 -32 0', '32 32 56');
self.hull=HULL_SCORPION;
}
else
{
setsize (self, '-16 -16 0', '16 16 36');
self.hull=HULL_CROUCH;
}
self.th_stand ();
return;
}
else
{
self.think=imp_die;
if (self.frame == $death7 || self.frame == $death8 ) // when cycling between these 2 frames during falling, make him flutter more randomly
thinktime self : random(0.05,0.217);
else
thinktime self : HX_FRAME_TIME;
}
}
void imp_die_init ()
{
setsize (self, '-16 -16 0', '16 16 24');
self.hull=HULL_CROUCH;
imp_die();
}
float check_z_move()
{
float goaldist,moverange;
entity targ;
if(self.enemy!=world)
targ=self.enemy;
else if(self.goalentity!=world)
targ=self.goalentity;
else
return FALSE;
if(fabs(targ.origin_z-self.origin_z)<48&&!visible(targ))
return FALSE; //FIXME: Find an up or down
if(visible(targ))
if(!clear_path(targ))
return FALSE;
if(targ.origin_z!=self.absmin_z)
{
goaldist=(targ.absmin_z+targ.absmax_z)*0.5-(self.absmax_z+self.absmin_z)*0.5;
moverange=fabs(self.level);
if(goaldist>0&&goaldist>moverange)
goaldist=moverange;
else if(goaldist<0&&goaldist<moverange*-1)
goaldist=moverange*-1;
movestep(0,0,goaldist, FALSE);
// self.velocity_z=goaldist*3;
}
// else if(self.velocity_z>=1)
// self.velocity_z/=2;
// else
// self.velocity_z=0;
return TRUE;
}
void imp_ferry ()
{
float dist;
vector org;
walkmove(self.angles_y, self.speed, FALSE);
check_z_move();
org=self.enemy.origin;
org_z=self.enemy.absmax_z;
dist=vlen(self.origin-org);
if(dist>200)
{
imp_drop();
return;
}
else// if(self.enemy!=self.movechain)
self.enemy.velocity=normalize(self.origin-org)*dist*5;
self.enemy.angles=self.angles;
}
void imp_pick_up (void)
{
self.attack_state=AS_FERRY;
self.goalentity=find(world,targetname,self.target);
}
void imp_up_down()
{ // Function to make the imp randomly change height
vector end_vec,vec1;
// dprint("CHECKING UP/DOWN\n");
if (self.velocity_z > -10.0 && self.velocity_z < 10.0)
{ // If we aren't moving up/down that much, maybe we want to change altitudes
self.velocity_z = 0.0;
if (random() < 0.3||self.attack_state==AS_SLIDING)
{ // Should we change altitudes?
makevectors (self.angles);
if (random() < 0.5)
{ // Go Down
vec1 = self.origin + v_up*-48;
traceline (self.origin,vec1,FALSE,self);
if(trace_fraction==1)
{
end_vec=vec1+v_forward*32;
traceline (vec1,end_vec,FALSE,self);
if (trace_fraction == 1||random()<0.5)
{
//dprint("Goin down\n");
self.velocity_z = self.speed*-7;
}
}
}
else
{ // Go Up
vec1 = self.origin + v_up*88;
traceline (self.origin,vec1,FALSE,self);
if(trace_fraction==1)
{
end_vec=vec1+v_forward*32;
traceline (vec1,end_vec,FALSE,self);
if (trace_fraction == 1||random()<0.5)
{
//dprint("Goin up\n");
self.velocity_z = self.speed*7;
}
}
}
self.flags(-)FL_ONGROUND;
}
}
else self.velocity_z = self.velocity_z / 1.05;
}
void imp_set_speeds ()
{
float anglediff,dist;
dist=8; //Movement distance this turn
anglediff= fabs(self.angles_y - self.ideal_yaw); //How far we're trying to turn
if (anglediff > 20) // If it is a big distance to turn, then don't move as far forward
dist = dist / 1.5;
self.level=imp_fly_amounts[self.frame - $impfly1];
self.speed = dist + self.level*4; //tweaks to speed based on anim frame
if(!visible(self.enemy))
self.level*=4;
}
float imp_check_too_close ()
{
float enemy_zdiff,enemy_hdist;
if(!visible(self.enemy))
return FALSE;
enemy_zdiff=fabs(self.origin_z-self.enemy.origin_z);
enemy_hdist=vhlen(self.enemy.origin-self.origin);
if(enemy_zdiff>=77&&enemy_hdist<=77)
{
// dprint("too close!\n");
return TRUE;
}
return FALSE;
}
void imp_move ()
{
float too_close;
checkenemy();
self.velocity*=1/1.05;
if(imp_check_too_close())
{
self.ideal_yaw=self.angles_y;
too_close=TRUE;
}
else
ai_face();
imp_set_speeds();
if(self.attack_state==AS_FERRY)
{
imp_ferry();
return;
}
if(self.attack_state==AS_STRAIGHT)
{
if(too_close)
{
if(!walkmove(self.angles_y,self.speed,FALSE))
movetogoal(self.speed);
}
else
movetogoal(self.speed);
}
else if(self.attack_state==AS_SLIDING)
{ //FIXME: make a more intelligent slide?
// dprint("sliding\n");
enemy_yaw = vectoyaw(self.enemy.origin - self.origin);
movedist=self.speed;
ai_run_slide();
}
movestep(0,0,self.level, FALSE);
if(!check_z_move())
imp_up_down();
if(vlen(self.enemy.origin+self.enemy.proj_ofs-self.origin)<20&&random()<0.2&&self.target!=""&&self.origin_z>self.enemy.absmax_z - 8&&self.spawnflags&PICKUP)
imp_pick_up();
}
float imp_new_action ()
{
float too_close;
enemy_vis=visible(self.enemy);
too_close=imp_check_too_close();
if((random()<0.7&&self.enemy.flags2&FL_ALIVE)||!enemy_vis||too_close)
{
if(self.think!=imp_fly)
{
self.think=imp_fly;
thinktime self : 0;
return TRUE;
}
return FALSE;
}
else
{
if(self.think!=imp_hover)
{
self.think=imp_hover;
thinktime self : 0;
return TRUE;
}
return FALSE;
}
return FALSE;
}
void imp_strafe_left () [++ $impfly1 .. $impfly20]
{
vector dir;
ai_face();
if(cycle_wrapped)
{
self.think=imp_fly;
thinktime self : 0;
return;
}
// else if(self.frame==$impfly1)
// dprint("strafing left\n");
makevectors(self.angles);
dir='0 0 0' - (v_right*(self.frame - $impfly1)*30);
self.velocity_x=dir_x;
self.velocity_y=dir_y;
check_pos_enemy();
//FIXME: check for attack?
}
void imp_strafe_right () [++ $impfly1 .. $impfly20]
{
vector dir;
ai_face();
if(cycle_wrapped)
{
self.think=imp_fly;
thinktime self : 0;
return;
}
// else if(self.frame==$impfly1)
// dprint("strafing right\n");
makevectors(self.angles);
dir=v_right*(self.frame - $impfly1)*30;
self.velocity_x=dir_x;
self.velocity_y=dir_y;
check_pos_enemy();
//FIXME: check for attack?
}
void imp_rise () [++ $impfly1 .. $impfly20]
{
check_pos_enemy();
if(self.frame==$impfly1)
{
// dprint("shooting up\n");
self.attack_finished=time+1.7;
self.velocity_z=600;
}
else if(self.frame>$impfly14 &&self.attack_finished<time)
{
self.frame==$impfly14;
self.velocity_z-=100;
}
else
self.velocity_z+=10;
if(self.frame==$impfly20)
{
self.velocity_z=0;
self.think=imp_fly;
thinktime self : 0.05;
}
}
void imp_dive () [++ $swoop1 .. $swoop20]
{
check_pos_enemy();
if(self.frame==$swoop1)
{
// dprint("diving for cover\n");
self.velocity_z=-500;
}
else if(self.frame<$swoop13)
self.velocity_z-=20;
else
self.velocity_z+=100;
if(self.frame==$swoop20)
{
self.velocity_z=0;
self.think=imp_abort_swoop;
thinktime self : 0;
}
}
float imp_check_defense ()
{
vector org,destiny,dir,proj_spot,proj_spot_dir;
float dist,dot;
if(self.enemy.last_attack<time - 1)
return FALSE;
if(fov(self,self.enemy,45))
{
org=self.enemy.origin+self.enemy.proj_ofs; //enemy's firing point, approximately
destiny=(self.absmin+self.absmax)*0.5; //my center
dir=normalize(destiny-org);//direction from enemy to me
dist=vlen(destiny-org); //distance between enemy and me
proj_spot=org+dir*(dist); //projected destination of projectile
proj_spot_dir=normalize(proj_spot-destiny); //that spot's position relativeto me
makevectors(self.angles);
dot = proj_spot_dir * v_right;
if ( dot > 0.1)
{
imp_strafe_left();//Left because you want to go away from it
return TRUE;
}
else if( dot < -0.1)
{
imp_strafe_right();
return TRUE;
}
dot = proj_spot_dir * v_up;
if ( dot > 0.2)
{
imp_dive();
return TRUE;
}
else if( dot < 0)
{
imp_rise();
return TRUE;
}
// dprint("no defense dir found\n");
return FALSE;
}
return FALSE;
}
void imp_attack(void)
{
if (self.origin_z - self.enemy.origin_z < 50 && vhlen(self.origin-self.enemy.origin) < 64)
{
self.v_angle=self.angles;
FireMelee (20,10,64);
}
else if (self.classname == "monster_imp_ice"||(self.classname == "monster_imp_lord"&&random()<0.5))
{
sound (self, CHAN_WEAPON, "imp/shard.wav", 1, ATTN_NORM);
makevectors (self.angles);
do_shard('14 8 0',360 + random()*150, '0 0 0');
do_shard('14 8 0',360 + random()*150, (v_forward * ((random() * 40) - 20)) +
(v_right * ((random() * 40) - 20)) +
(v_up * ((random() * 20) - 10)));
do_shard('14 8 0',360 + random()*150, (v_forward * ((random() * 40) - 20)) +
(v_right * ((random() * 40) - 20)) +
(v_up * ((random() * 20) - 10)));
if (random() < 0.5)
do_shard('14 8 0',360 + random()*150, (v_forward * ((random() * 40) - 20)) +
(v_right * ((random() * 40) - 20)) +
(v_up * ((random() * 20) - 10)));
if (random() < 0.5)
do_shard('14 8 0',360 + random()*150, (v_forward * ((random() * 40) - 20)) +
(v_right * ((random() * 40) - 20)) +
(v_up * ((random() * 20) - 10)));
if (random() < 0.5)
do_shard('14 8 0',360 + random()*150, (v_forward * ((random() * 40) - 20)) +
(v_right * ((random() * 40) - 20)) +
(v_up * ((random() * 20) - 10)));
}
else
{
sound (self, CHAN_WEAPON, "imp/fireball.wav", 1, ATTN_NORM);
do_fireball('14 8 0');
}
}
void imp_attack_anim() [++ $impfir1 .. $impfir21]
{
check_pos_enemy();
ai_face();
if(imp_check_defense())
return;
if (self.frame == $impfir17)
imp_attack();
else if (cycle_wrapped)
{
self.think=imp_fly;
if(visible(self.enemy)&&self.enemy.flags2&FL_ALIVE)
if(random()<0.2+skill/10)
self.think=imp_attack_anim;
self.attack_finished=time + 1;
thinktime self : 0;
}
}
void imp_abort_swoop () [++ $swpout1 .. $swpout15]
{
check_pos_enemy();
if(self.velocity_x>10)
self.velocity_x/=2;
else
self.velocity_x=0;
if(self.velocity_x>10)
self.velocity_y/=2;
else
self.velocity_y=0;
if(self.frame==$swpout15)
{
self.attack_finished=time + 1;
self.think=imp_fly;
thinktime self : 0;
}
}
void imp_swoop_end () [++ $swpend1 .. $swpend15]
{
check_pos_enemy();
self.flags (-) FL_ONGROUND;
// swoop him back up and slow down his forward velocity (xy)
self.velocity_z += 15;
self.velocity_x /= 1.2;
self.velocity_y /= 1.2;
if (self.frame == $swpend15)
{ // Finished swooping
self.attack_finished=time + 1;
self.velocity = '0 0 0';
self.yaw_speed = 8;
self.think=imp_hover;
thinktime self : 0;
}
}
void imp_swoop_charge () [++ $swpcyc1 .. $swpcyc4]
{
vector dir,destiny,org;
check_pos_enemy();
destiny=self.enemy.origin+self.enemy.proj_ofs;
org=(self.absmin+self.absmax)*0.5;
enemy_vis=visible(self.enemy);
enemy_infront=infront(self.enemy);
enemy_range=vlen(destiny - org);
if(self.enemy.last_attack>time - 1 &&fov(self,self.enemy,45))
{
self.velocity_z+=200;
imp_abort_swoop();
}
else if(enemy_vis&&enemy_infront&&enemy_range<2000)
{
dir=normalize(destiny-org);
self.velocity=dir*(400+self.count*10);
ai_face();
self.count += 1;
if (self.flags & FL_ONGROUND || self.count > 30)
{ // Didn't hit our target, so go back up
self.flags (-) FL_ONGROUND;
imp_abort_swoop();
}
}
else
imp_abort_swoop();
}
void imp_enter_swoop () [++ $swoop1 .. $swoop20]
{
vector vec;
check_pos_enemy();
if(self.frame==$swoop1)
{
self.yaw_speed=15;
self.count=140;
self.velocity = '0 0 0';
sound (self, CHAN_BODY, "imp/swoop.wav", 1, ATTN_NORM);
}
ai_face();
self.count *= 1.15;
if (self.frame >= $swoop12)
{ // Start to swoop down
vec = normalize(self.enemy.origin - self.origin + self.enemy.proj_ofs);
self.velocity = vec*self.count;
if (self.frame <= $swoop13 )
{ // If we haven't pulled out yet, keep going straight down
self.velocity_x = self.velocity_y = 0;
}
if(self.origin_z - self.enemy.absmax_z>50&&self.frame==$swoop13)
self.frame=$swoop12;
}
if (self.frame == $swoop12 || self.frame == $swoop13 )
{ // Swoop down cycling frames
if (self.flags & FL_ONGROUND)
{ // Ran into something on the way down
self.flags (-) FL_ONGROUND;
self.frame = $swoop14;
self.count = 280;
}
if (self.origin_z - self.enemy.origin_z < 60)
{ // Have to flatten out soon
self.frame = $swoop14;
self.count = 280;
}
}
if(self.frame==$swoop20)
{
self.count=0;
self.think=imp_swoop_charge;
thinktime self : 0.05;
}
}
void imp_straight_swoop () [-- $swpout15 .. $swpout1]
{
check_pos_enemy();
ai_face();
if(imp_check_defense())
return;
if(self.frame==$swpout15)
{
self.yaw_speed=15;
self.count=140;
self.velocity = '0 0 0';
sound (self, CHAN_BODY, "imp/swoop.wav", 1, ATTN_NORM);
}
if(self.frame==$swpout1)
{
self.count=0;
self.think=imp_swoop_charge;
thinktime self : 0.05;
}
}
void() imp_touch =
{
float damg;
vector punch;
if((self.frame >= $swpcyc1 && self.frame <= $swpcyc4 )||(self.frame>=$swoop16 &&self.frame<=$swoop20))
{ // If we are in swoop attack frames
self.flags (-) FL_ONGROUND;
sound (self, CHAN_WEAPON, "imp/swoophit.wav", 1, ATTN_NORM);
self.think=imp_swoop_end;
if (other.takedamage)
{ // We sucessfully hit something
punch=normalize(self.angles)* -1;
punch *= damg;
other.punchangle=punch;
if(self.classname == "monster_imp_lord")
{
damg = (20 + (self.count *3));
T_Damage (other, self, self.owner, damg);
other.velocity_x += self.velocity_x*2;
other.velocity_y += self.velocity_y*2;
if(other.movetype==MOVETYPE_FLY)
{
if(other.flags&FL_ONGROUND)
other.velocity_z=200;
}
else
other.velocity_z=200;
other.flags(-)FL_ONGROUND;
}
else
{
damg = (10 + (self.count / 2));
T_Damage (other, self, self.owner, damg);
}
}
else if(vlen(self.velocity)>600)
{
makevectors(self.angles);
SpawnPuff(self.origin+self.proj_ofs+v_forward*self.absmax_x,'0 0 0',10,self);
self.pain_finished=-666;
self.think=self.th_pain;
}
self.velocity=other.velocity;//??
}
};
float imp_check_attack ()
{
float enemy_hdist,enemy_zdiff,swoop_no_drop;
vector destiny,org;
if(!self.monster_awake||self.enemy==world)
return FALSE;
enemy_vis=visible(self.enemy);
if (!enemy_vis)
{
if(self.mintel)
SetNextWaypoint();
self.attack_state = AS_STRAIGHT;
return FALSE;
}
else if(self.mintel)
{
self.goalentity=self.enemy;
self.wallspot=(self.enemy.absmin+self.enemy.absmax)*0.5;
}
if(self.attack_state==AS_FERRY)
return FALSE;
if (time < self.attack_finished)
return FALSE;
enemy_infront=infront(self.enemy);
if(!enemy_infront)
return FALSE;
enemy_range=range(self.enemy);
if (enemy_range == RANGE_FAR)
{
self.attack_state = AS_STRAIGHT;
return FALSE;
}
// see if any entities are in the way of the shot
destiny=self.enemy.origin+self.enemy.proj_ofs;
org=(self.absmin+self.absmax)*0.5;
self.attack_state = AS_STRAIGHT;
traceline (org, destiny, FALSE, self);
if (trace_ent != self.enemy)
{
// dprint("Imp shot blocked by: ");
// dprint(trace_ent.classname);
// dprint("\n");
if(trace_ent.health>25||!trace_ent.takedamage||(trace_ent.flags&FL_MONSTER&&trace_ent.classname!="player_sheep"))
{//Don't have a clear shot, and don't want to shoot obstruction
// dprint("\n");
self.attack_state = AS_SLIDING;
return FALSE;
}
// dprint("- trying to kill it\n");
}
if(random()<0.2+skill/10)
return FALSE;
if(!self.spawnflags & MONSTER_STAND_GROUND)
{
float swoop_no_drop;
swoop_no_drop=FALSE;
enemy_hdist=vhlen(destiny-org);
enemy_zdiff=org_z - destiny_z;
if(enemy_zdiff<=36&&random()<0.3)
swoop_no_drop=TRUE;
if((enemy_hdist>100&&enemy_zdiff>36)||swoop_no_drop)
{
tracearea(org,org-'0 0 1'*enemy_zdiff,'-16 -16 0','16 16 28',FALSE,self);
if(trace_fraction==1||swoop_no_drop)
{
if(swoop_no_drop)
tracearea(org,destiny,'-16 -16 0','16 16 28',FALSE,self);
else
tracearea(org-'0 0 1'*enemy_zdiff,destiny,'-16 -16 0','16 16 28',FALSE,self);
if(trace_ent==self.enemy)
{
if(swoop_no_drop)
self.think=imp_straight_swoop;
else
self.think=imp_enter_swoop;
thinktime self : 0;
return TRUE;
}
}
}
}
self.think=imp_attack_anim;
thinktime self : 0;
return TRUE;
}
void imp_hover() [++ $impfly1 .. $impfly20]
{
float too_close;
checkenemy();
if(!self.enemy)
if(imp_find_target())
return;
self.velocity*=1/1.05;
ai_face();
imp_set_speeds();
check_z_move();
if(imp_check_defense())
return;
if(imp_check_attack())
return;
if(self.enemy!=world)
{
too_close=imp_check_too_close();
enemy_vis=visible(self.enemy);
if(!enemy_vis||too_close||self.attack_state==AS_SLIDING)
{
self.think=imp_fly;
thinktime self : 0;
return;
}
}
if(self.enemy!=world||self.goalentity!=world)
if(imp_new_action())
return;
}
void() stone_imp_awaken = [++ $impup7 .. $impup23]
{
if(self.frame==$impup10)
{
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
self.count = 0;
self.monster_awake = TRUE;
setsize (self, '-16 -16 0', '16 16 36');
self.hull=HULL_CROUCH;
self.mass = 3;
self.health=self.max_health;
self.flags (+) FL_MONSTER | FL_FLY;
self.movetype = MOVETYPE_FLY;
self.takedamage=DAMAGE_YES;
self.touch= imp_touch;
self.th_die = imp_die_init;
self.spawnflags (-) SF_IMP_DORMANT;
self.artifact_active (-) ARTFLAG_STONED;
sound (self, CHAN_VOICE, "fx/wallbrk.wav", 1, ATTN_NORM);
while(chunk_cnt < CHUNK_MAX)
{
CreateModelChunks(self.size,.7);
chunk_cnt+=1;
}
self.skin=self.oldskin;
if(self.skin)
self.classname="monster_imp_ice";
else
self.classname="monster_imp_fire";
self.thingtype=THINGTYPE_FLESH;
self.scale=1;
}
else if(self.frame==$impup23)
self.think=imp_fly;
};
void imp_pain_anim2 () [++ $impup6 .. $impup14]
{
check_pos_enemy();
if(self.frame==$impup14)
{
if (self.spawnflags & SF_IMP_DORMANT)
{
self.frame=$impup1;
self.think=stone_imp_awaken;
}
else
self.think=imp_hover;
thinktime self : 0;
}
}
void imp_pain_anim1 () [-- $impup14 .. $impup6]
{
check_pos_enemy();
if(self.frame==$impup6)
{
self.think=imp_pain_anim2;
thinktime self : 0;
}
}
void(entity attacker, float damage) imp_pain =
{
if(self.pain_finished>time)
return;
if(self.targetname!=""&&self.skin==2)
{
self.think=SUB_Null;
self.nextthink=-1;
return;
}
if(random()<0.5&&self.pain_finished!=-666)//FIXME: make more logical
return;
self.pain_finished=time+3;//only react to pain once every 3 seconds max
if(self.attack_state==AS_FERRY&&random()<0.2)
imp_drop();
//FIXME: pain sound
self.think=imp_pain_anim1;
thinktime self : 0;
};
void imp_use (void)
{
self.use=SUB_Null;
self.targetname="";
if(activator.classname=="player")
self.enemy=self.goalentity=activator;
else
dprint("ERROR: monster not activated by player!\n");
self.frame=$impup1;
self.think=stone_imp_awaken;
thinktime self : random();
}
float imp_find_target(void)
{ // Imp in waiting state
if (self.spawnflags & SF_IMP_DORMANT)
return FALSE;
if (LocateTarget())
{ // We found a target
self.goalentity = self.enemy;
self.think = self.th_run;
thinktime self : 0;
//self.nextthink = time + 6;//why 6?
return TRUE;
}
return FALSE;
}
void imp_wait() [++ $impwat1 .. $impwat24]
{
if(imp_find_target())
return;
}
void imp_fly () [++ $impfly1 .. $impfly20]
{
imp_move();
if(imp_check_defense())
return;
if(imp_check_attack())
return;
if(imp_new_action())
return;
}
void imp_awaken () [++ $impup1 .. $impup23]
{
check_pos_enemy();
movestep(0,0,imp_up_amounts[self.frame - $impup1], FALSE);
walkmove(self.angles_y, imp_up_amounts[self.frame - $impup1] / 2.0, FALSE);
if (self.frame == $impup23)
{
self.monster_awake = TRUE;
self.think = imp_fly;
thinktime self : 0;
}
}
void impmonster_start_go ()
{
self.ideal_yaw = self.angles * '0 1 0';
if (!self.yaw_speed)
self.yaw_speed = 5.0;
self.view_ofs = '0 0 25';
if(!self.use)
self.use = monster_use;
self.pausetime = 99999999;
if(self.targetname)
{
self.frame=$impwat1;
self.think=imp_use;
self.nextthink=-1;
}
else
self.th_stand ();
}
void impmonster_start ()
{
thinktime self : random(0.5);
self.think = impmonster_start_go;
total_monsters = total_monsters + 1;
}
void init_imp (float which_skin)
{
if (deathmatch&&self.wait!=-1&&self.classname!="monster_imp_lord")
{
remove(self);
return;
}
if(!self.flags2&FL_SUMMONED)
{
precache_model2 ("models/imp.mdl");
precache_model2 ("models/h_imp.mdl");//empty for now
if (self.classname == "monster_imp_lord")
{
precache_model2 ("models/shardice.mdl");
precache_model ("models/fireball.mdl");
}
else if (self.classname == "monster_ice_imp"||self.classname == "monster_imp_ice")
precache_model2 ("models/shardice.mdl");
else
precache_model ("models/fireball.mdl");
precache_model2 ("models/ring.mdl");
precache_sound2("imp/up.wav");
precache_sound2("imp/die.wav");
precache_sound2("imp/swoophit.wav");
precache_sound2("imp/swoop.wav");
precache_sound2("imp/fly.wav");
precache_sound2("imp/fireball.wav");
precache_sound2("imp/shard.wav");
}
self.solid = SOLID_SLIDEBOX;
setmodel (self, "models/imp.mdl");
if (self.classname == "monster_imp_lord")
{
self.drawflags(+)SCALE_ORIGIN_BOTTOM;
self.scale=2.5;
setsize (self, '-32 -32 0', '32 32 56');
self.hull=HULL_SCORPION;
}
else
{
setsize (self, '-16 -16 0', '16 16 36');
self.hull=HULL_CROUCH;
}
self.headmodel = "models/h_imp.mdl";
if(self.wait!=-1)
{
self.movetype = MOVETYPE_FLY;
self.takedamage=DAMAGE_YES;
self.skin = which_skin;
self.impType = which_skin;
if(which_skin==3)
self.flags (+) FL_COLDHEAL|FL_FIREHEAL;
else if(which_skin==1)
self.flags (+) FL_COLDHEAL;
else
self.flags (+) FL_FIREHEAL;
self.flags2 (+) FL_ALIVE;
self.thingtype=THINGTYPE_FLESH;
if (self.classname == "monster_imp_lord")
{
self.max_health=self.health = 400;
// self.experience_value = 3000;
self.mass = 10;
}
else
{
self.max_health=self.health = 100;
// self.experience_value = 500;
self.mass = 3;
}
self.mintel = 5;
if (self.spawnflags & MONSTER_HOVER)
{
self.th_stand = imp_hover;
self.th_walk = imp_fly;
}
else
{
self.th_stand = imp_wait;
self.th_walk = imp_awaken;
}
self.th_run = self.th_walk;
self.th_pain = imp_pain;
self.th_die = imp_die_init;
self.th_missile = imp_enter_swoop;
self.th_melee = imp_attack;
self.touch = imp_touch;
}
self.yaw_speed=8;
self.speed=10;
self.attack_state = AS_STRAIGHT;
self.level=0;
self.monster_awake = FALSE;
self.view_ofs=self.proj_ofs='0 0 25';
if (self.spawnflags & SF_IMP_DORMANT)
{
self.classname="gargoyle";
self.scale=1.5;
self.artifact_active (+) ARTFLAG_STONED;
setsize (self, '-16 -16 0', '16 16 36');
self.hull=HULL_CROUCH;
self.takedamage=DAMAGE_NO_GRENADE;
self.thingtype=THINGTYPE_GREYSTONE;
self.movetype=MOVETYPE_PUSHPULL;
self.touch=obj_push;
self.health+=100;
self.mass=100;
self.th_die = chunk_death;
if(self.wait!=-1)
{
self.use = imp_use;
self.oldskin = self.skin;
self.th_stand = imp_wait;
}
self.skin=2;
}
else
self.flags (+) FL_MONSTER | FL_FLY;
if(self.wait!=-1)
impmonster_start();
else
self.frame = $impwat1;
}
/*QUAKED monster_imp_ice (1 0.3 0) (-16 -16 0) (16 16 55) STAND HOVER x x DORMANT
Grunt monster - common. Shoots multiple ice shards. Can only be killed by defrosting it.
DORMANT = uses the grey stone texture to make it look like a gargoyl- will wake up if the player looks at him long enough, gets close, or hurts him.
-------------------------FIELDS-------------------------
wait = if you give it a -1, the gargoyle will not come alive, it's just a decoration
--------------------------------------------------------
*/
void monster_imp_ice ()
{
init_imp(1);
}
/*QUAKED monster_imp_fire (1 0.3 0) (-16 -16 0) (16 16 55) STAND HOVER x x DORMANT
Grunt monster - common. Shoots a fireball. Can only be killed by defrosting it.
DORMANT = uses the grey stone texture to make it look like a gargoyl- will wake up if the player looks at him long enough, gets close, or hurts him.
-------------------------FIELDS-------------------------
wait = if you give it a -1, the gargoyle will not come alive, it's just a decoration
--------------------------------------------------------
*/
void monster_imp_fire ()
{
init_imp(0);
}
/*QUAKED monster_imp_lord (1 0.3 0) (-16 -16 0) (16 16 55) STAND HOVER x x DORMANT
Grunt monster - common. Shoots multiple ice shards. Can only be killed by defrosting it.
DORMANT = uses the grey stone texture to make it look like a gargoyl- will wake up if the player looks at him long enough, gets close, or hurts him.
-------------------------FIELDS-------------------------
wait = if you give it a -1, the gargoyle will not come alive, it's just a decoration
--------------------------------------------------------
*/
void monster_imp_lord ()
{
init_imp(3);
}