1643 lines
36 KiB
C++
1643 lines
36 KiB
C++
|
/*
|
||
|
* $Header: /H3/game/hcode/imp.hc 38 9/11/97 7:13p Rjohnson $
|
||
|
*/
|
||
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
IMP
|
||
|
MG&RJ
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
void()imp_fly;
|
||
|
void()imp_hover;
|
||
|
void()imp_abort_swoop;
|
||
|
void()imp_touch;
|
||
|
float()imp_find_target;
|
||
|
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;
|
||
|
self.enemy.flags(-)FL_FLY;
|
||
|
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 summoned_imp_die () [-- $impup23 .. $impup1]
|
||
|
{
|
||
|
if(self.health<-40)
|
||
|
{
|
||
|
chunk_death();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(self.frame==$impup23)
|
||
|
{
|
||
|
self.velocity='0 0 0';
|
||
|
self.effects=EF_DIMLIGHT;
|
||
|
self.movetype=MOVETYPE_NOCLIP;
|
||
|
self.drawflags=SCALE_ORIGIN_CENTER;
|
||
|
self.flags=0;
|
||
|
self.solid=SOLID_NOT;
|
||
|
self.avelocity_y=-200;
|
||
|
self.count=0;
|
||
|
sound (self, CHAN_VOICE, "imp/diebig.wav", 1, ATTN_NORM);
|
||
|
}
|
||
|
|
||
|
self.velocity='0 0 0';
|
||
|
self.scale-=0.025;
|
||
|
self.avelocity_y-=10;
|
||
|
|
||
|
self.count+=1;
|
||
|
if(self.count==4)
|
||
|
self.count=0;
|
||
|
else if(self.frame<$impup23)
|
||
|
self.frame+=1;
|
||
|
|
||
|
if(self.frame==$impup1)
|
||
|
{
|
||
|
sound (self, CHAN_BODY, "items/itmspawn.wav", 1, ATTN_NORM);
|
||
|
CreateRedFlash(self.origin);
|
||
|
remove(self);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void imp_die ()
|
||
|
{
|
||
|
self.touch=SUB_Null;
|
||
|
if(self.health<-30)
|
||
|
{
|
||
|
sound (self, CHAN_BODY, "misc/null.wav", 1, ATTN_NONE);
|
||
|
chunk_death();
|
||
|
return;
|
||
|
}
|
||
|
if(self.frame!=$death14)
|
||
|
AdvanceFrame($death1,$death14);
|
||
|
|
||
|
if(self.frame==$death1)
|
||
|
{
|
||
|
self.flags(-)FL_ONGROUND;
|
||
|
if(self.skin==3)
|
||
|
sound (self, CHAN_BODY, "imp/diebig.wav", 1, ATTN_NORM);
|
||
|
else
|
||
|
sound (self, CHAN_BODY, "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;
|
||
|
|
||
|
MonsterCheckContents();
|
||
|
|
||
|
if(self.frame==$death14 &&self.flags&FL_ONGROUND)
|
||
|
MakeSolidCorpse();
|
||
|
else if(self.health<-30)
|
||
|
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();
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void imp_ferry ()
|
||
|
{
|
||
|
float dist;
|
||
|
vector org;
|
||
|
movetogoal(self.speed);
|
||
|
/*
|
||
|
if(!check_z_move(self.level))
|
||
|
imp_up_down();
|
||
|
*/
|
||
|
self.velocity_z=self.goalentity.origin_z-self.origin_z;
|
||
|
if(fabs(self.velocity_z)>self.speed*30)
|
||
|
if(self.velocity_z<0)
|
||
|
self.velocity_z=self.speed*-30;
|
||
|
else
|
||
|
self.velocity_z=self.speed*30;
|
||
|
self.flags(-)FL_ONGROUND;
|
||
|
// dprintf("Boost : %s\n",self.velocity_z);
|
||
|
|
||
|
org=self.goalentity.origin;
|
||
|
dist=vlen(self.origin-org);
|
||
|
if(dist<=self.size_x)
|
||
|
{
|
||
|
imp_drop();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
org=self.enemy.origin;
|
||
|
org_z=self.enemy.absmax_z;
|
||
|
dist=vlen(self.origin-org);
|
||
|
if(dist>200)
|
||
|
{
|
||
|
imp_drop();
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
self.enemy.velocity=normalize(self.origin-org)*dist*5;
|
||
|
self.enemy.angles=self.angles;
|
||
|
|
||
|
MonsterCheckContents();
|
||
|
}
|
||
|
|
||
|
void imp_pick_up (void)
|
||
|
{
|
||
|
self.attack_state=AS_FERRY;
|
||
|
self.goalentity=find(world,targetname,self.target);
|
||
|
self.enemy.flags(+)FL_FLY;
|
||
|
}
|
||
|
|
||
|
void imp_set_speeds ()
|
||
|
{
|
||
|
float anglediff,dist;
|
||
|
if(pointcontents(self.origin+self.view_ofs)==CONTENT_WATER)
|
||
|
dist=4;
|
||
|
else
|
||
|
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*self.scale; //tweaks to speed based on anim frame
|
||
|
|
||
|
if(!visible(self.enemy))
|
||
|
{
|
||
|
if(self.mintel)
|
||
|
SetNextWaypoint();
|
||
|
if(self.search_time<time&&self.goalentity==self.enemy&&self.trigger_field.classname=="waypoint")
|
||
|
self.goalentity=self.trigger_field;
|
||
|
self.level*=4;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self.search_time=time+5; //If lose, right, keep searching for 5 secs
|
||
|
self.goalentity=self.enemy;
|
||
|
self.wallspot=(self.enemy.absmin+self.enemy.absmax)*0.5;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float imp_check_too_close ()
|
||
|
{
|
||
|
float enemy_zdiff,enemy_hdist;
|
||
|
if(!visible(self.enemy))
|
||
|
return FALSE;
|
||
|
|
||
|
if(self.attack_state==AS_FERRY)
|
||
|
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;
|
||
|
if((self.skin==3&&self.enemy==self.controller&&vlen(self.enemy.origin-self.origin)<128)||(self.goalentity==world||self.enemy==world))
|
||
|
{
|
||
|
self.think=self.th_stand;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(self.attack_state!=AS_FERRY)
|
||
|
checkenemy();
|
||
|
|
||
|
self.velocity=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.goalentity.classname=="waypoint")
|
||
|
{
|
||
|
dprint("Following waypoints");
|
||
|
if(self.search_time<time)
|
||
|
dprint("exclusively\n");
|
||
|
else
|
||
|
self.goalentity=self.enemy;
|
||
|
}
|
||
|
*/
|
||
|
if(self.attack_state==AS_STRAIGHT)
|
||
|
{
|
||
|
if(too_close)
|
||
|
{
|
||
|
if(!walkmove(self.angles_y,self.speed,FALSE))
|
||
|
movetogoal(self.speed);
|
||
|
}
|
||
|
else
|
||
|
movetogoal(self.speed);
|
||
|
}
|
||
|
if(self.attack_state==AS_SLIDING)
|
||
|
{
|
||
|
enemy_yaw = vectoyaw(self.goalentity.origin - self.origin);
|
||
|
movedist=self.speed;
|
||
|
ai_run_slide();
|
||
|
}
|
||
|
|
||
|
movestep(0,0,self.level, FALSE);//slight up/down movement
|
||
|
|
||
|
if(!check_z_move(self.level))
|
||
|
imp_up_down();
|
||
|
|
||
|
if(random()<0.2)
|
||
|
if(self.enemy!=world&&self.search_time>=time)
|
||
|
{
|
||
|
enemy_vis=visible(self.enemy);
|
||
|
if(!enemy_vis||!clear_path(self.enemy,FALSE))
|
||
|
{
|
||
|
vector go_dir;
|
||
|
float r,minspeed,maxspeed;
|
||
|
makevectors(self.angles);
|
||
|
minspeed=100*self.scale;
|
||
|
maxspeed=300*self.scale;
|
||
|
r=random();
|
||
|
if(r<0.25)
|
||
|
go_dir=check_axis_move(v_right,minspeed,maxspeed);
|
||
|
else if(r<=0.5)
|
||
|
go_dir=check_axis_move(v_up,minspeed,maxspeed);
|
||
|
else if(r<=0.75)
|
||
|
go_dir=check_axis_move((v_right+v_up)*0.5,minspeed,maxspeed);
|
||
|
else
|
||
|
go_dir=check_axis_move((v_right*-1+v_up)*0.5,minspeed,maxspeed);
|
||
|
if(go_dir!='0 0 0')
|
||
|
{
|
||
|
if(r<0.25)
|
||
|
{
|
||
|
self.velocity_x=go_dir_x;
|
||
|
self.velocity_y=go_dir_y;
|
||
|
}
|
||
|
else if(r<=0.5)
|
||
|
self.velocity_z=go_dir_z;
|
||
|
else
|
||
|
self.velocity=go_dir;
|
||
|
self.flags(-)FL_ONGROUND;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(self.spawnflags&PICKUP)
|
||
|
if(random()<0.2)
|
||
|
if(self.target!="")
|
||
|
if(self.origin_z>self.enemy.absmax_z - 8)
|
||
|
if(vlen(self.enemy.origin+self.enemy.proj_ofs-self.origin)<=self.size_x*1.5)
|
||
|
imp_pick_up();
|
||
|
|
||
|
MonsterCheckContents();
|
||
|
}
|
||
|
|
||
|
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||self.attack_state==AS_FERRY)
|
||
|
{
|
||
|
if(self.think!=imp_fly&&self.enemy!=world)
|
||
|
{
|
||
|
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||self.attack_state==AS_FERRY)
|
||
|
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_missile ()
|
||
|
{
|
||
|
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'*self.scale,360 + random()*150, '0 0 0');
|
||
|
do_shard('14 8 0'*self.scale,360 + random()*150, (v_forward * ((random() * 40) - 20)) +
|
||
|
(v_right * ((random() * 40) - 20)) +
|
||
|
(v_up * ((random() * 20) - 10)));
|
||
|
do_shard('14 8 0'*self.scale,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'*self.scale,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'*self.scale,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'*self.scale,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'*self.scale);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void imp_melee ()
|
||
|
{
|
||
|
vector org,destiny;
|
||
|
float dist,damg;
|
||
|
makevectors(self.angles);
|
||
|
org=self.origin+self.proj_ofs+v_forward*16*self.scale;
|
||
|
destiny=(self.enemy.absmin+self.enemy.absmax)*0.5;
|
||
|
dist=vlen(destiny-org);
|
||
|
traceline(org,destiny,FALSE,self);
|
||
|
if(dist>48*self.scale||trace_fraction==1)
|
||
|
{
|
||
|
imp_missile();
|
||
|
return;
|
||
|
}
|
||
|
if(trace_ent.takedamage)
|
||
|
{
|
||
|
string hitsound;
|
||
|
if(self.skin==3)
|
||
|
{
|
||
|
if(trace_ent.thingtype==THINGTYPE_FLESH)
|
||
|
MeatChunks (trace_endpos,v_right*random(-100,-300)+'0 0 200', 3,trace_ent);
|
||
|
hitsound="weapons/slash.wav";
|
||
|
}
|
||
|
else
|
||
|
hitsound="assassin/chntear.wav";
|
||
|
sound(trace_ent,CHAN_AUTO,hitsound,1,ATTN_NORM);
|
||
|
if(self.skin==3)
|
||
|
damg=40;
|
||
|
else
|
||
|
damg=10*self.scale;
|
||
|
T_Damage(trace_ent,self,self,damg);
|
||
|
}
|
||
|
else
|
||
|
hitsound="weapons/gauntht2.wav";
|
||
|
SpawnPuff(trace_endpos,v_right*-100,10*self.scale,trace_ent);
|
||
|
sound(self,CHAN_AUTO,hitsound,1,ATTN_NORM);
|
||
|
}
|
||
|
|
||
|
void imp_attack(void)
|
||
|
{
|
||
|
self.last_attack=time;
|
||
|
if (vlen(self.origin-self.enemy.origin) <= 64*self.scale)
|
||
|
{
|
||
|
self.v_angle=self.angles;
|
||
|
imp_melee ();
|
||
|
}
|
||
|
else
|
||
|
imp_missile();
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
self.last_attack=time;
|
||
|
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+=150;
|
||
|
imp_abort_swoop();
|
||
|
}
|
||
|
else if(enemy_vis&&enemy_infront&&enemy_range<2000)
|
||
|
{
|
||
|
dir=normalize(destiny-org);
|
||
|
self.velocity=dir*(377+self.count*7);
|
||
|
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,org;
|
||
|
check_pos_enemy();
|
||
|
if(self.frame==$swoop1)
|
||
|
{
|
||
|
self.yaw_speed=15;
|
||
|
self.count=140;
|
||
|
self.touch = imp_touch;
|
||
|
self.velocity = '0 0 0';
|
||
|
|
||
|
if(self.skin==3)
|
||
|
sound (self, CHAN_VOICE, "imp/swoopbig.wav", 1, ATTN_NORM);
|
||
|
else
|
||
|
sound (self, CHAN_VOICE, "imp/swoop.wav", 1, ATTN_NORM);
|
||
|
}
|
||
|
|
||
|
ai_face();
|
||
|
self.count *= 1.15;
|
||
|
|
||
|
if (self.frame >= $swoop12)
|
||
|
{ // Start to swoop down
|
||
|
org=self.origin;
|
||
|
org_z=self.absmin_z;
|
||
|
vec = normalize(self.enemy.origin - org + 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.absmin_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';
|
||
|
self.touch = imp_touch;
|
||
|
|
||
|
if(self.skin==3)
|
||
|
sound (self, CHAN_VOICE, "imp/swoopbig.wav", 1, ATTN_NORM);
|
||
|
else
|
||
|
sound (self, CHAN_VOICE, "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,damg_plus;
|
||
|
vector punch,dir;
|
||
|
|
||
|
|
||
|
self.touch=SUB_Null;
|
||
|
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
|
||
|
|
||
|
if(self.frame >= $swpcyc1 && self.frame <= $swpcyc4)
|
||
|
damg_plus=self.count;
|
||
|
else
|
||
|
damg_plus=(self.frame-$swoop16)*5;
|
||
|
|
||
|
if(self.classname == "monster_imp_lord")
|
||
|
{
|
||
|
damg = (33 + (damg_plus *5));
|
||
|
T_Damage (other, self, self.controller, damg);
|
||
|
if(other.monsterclass<CLASS_BOSS)
|
||
|
{
|
||
|
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 + (damg_plus / 2));
|
||
|
T_Damage (other, self, self, damg);
|
||
|
}
|
||
|
if(other.classname=="player"&&other.flags2&FL_ALIVE)
|
||
|
{
|
||
|
makevectors(other.v_angle);
|
||
|
dir=normalize(self.velocity)*-1;
|
||
|
punch_y=v_forward*dir;
|
||
|
punch_z=v_right*dir;
|
||
|
punch_x=v_up*dir;
|
||
|
other.punchangle=punch*(damg/10);
|
||
|
}
|
||
|
}
|
||
|
else if(vlen(self.velocity)>400)
|
||
|
{
|
||
|
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;//??
|
||
|
}
|
||
|
else if(vlen(self.velocity)>300)
|
||
|
sound (self, CHAN_WEAPON, "imp/swoophit.wav", 1, ATTN_NORM);
|
||
|
};
|
||
|
|
||
|
float imp_check_attack ()
|
||
|
{
|
||
|
float enemy_hdist,enemy_zdiff,swoop_no_drop;
|
||
|
vector destiny,org;
|
||
|
if(self.enemy==world)
|
||
|
{
|
||
|
self.think=self.th_stand;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if(self.enemy==self.controller||self.enemy==self)
|
||
|
if(!LocateTarget())
|
||
|
return FALSE;
|
||
|
|
||
|
if(self.attack_state==AS_FERRY)
|
||
|
return FALSE;
|
||
|
|
||
|
enemy_vis=visible(self.enemy);
|
||
|
if (!enemy_vis)
|
||
|
{
|
||
|
if(self.mintel)
|
||
|
SetNextWaypoint();
|
||
|
if(self.search_time<time&&self.goalentity==self.enemy&&self.trigger_field.classname=="waypoint")
|
||
|
self.goalentity=self.trigger_field;
|
||
|
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 (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;
|
||
|
|
||
|
if(!clear_path (self.enemy,FALSE))
|
||
|
if(!CanDamage(self.enemy,self))
|
||
|
return FALSE;
|
||
|
|
||
|
if(self.skin!=3)
|
||
|
if(random()<0.5-skill/10)
|
||
|
return FALSE;
|
||
|
|
||
|
if(!self.spawnflags & MONSTER_STAND_GROUND)
|
||
|
{
|
||
|
float swoop_no_drop;
|
||
|
vector min,max;
|
||
|
swoop_no_drop=FALSE;
|
||
|
|
||
|
enemy_hdist=vhlen(destiny-org);
|
||
|
if(self.skin==3)
|
||
|
enemy_zdiff=self.absmin_z - destiny_z;
|
||
|
else
|
||
|
enemy_zdiff=org_z - destiny_z;
|
||
|
if(enemy_zdiff<=36&&random()<0.3)
|
||
|
swoop_no_drop=TRUE;
|
||
|
|
||
|
if(self.skin!=3)
|
||
|
{
|
||
|
min='-16 -16 0';
|
||
|
max='16 16 28';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
min='-48 -48 42';
|
||
|
max='48 48 42';
|
||
|
}
|
||
|
|
||
|
if(enemy_hdist>70+30*self.scale&&(enemy_zdiff>36||swoop_no_drop))
|
||
|
{
|
||
|
tracearea(org,org-'0 0 1'*enemy_zdiff,min,max,FALSE,self);
|
||
|
if(trace_fraction==1||swoop_no_drop)
|
||
|
{
|
||
|
if(swoop_no_drop)
|
||
|
tracearea(org,destiny,min,max,FALSE,self);
|
||
|
else
|
||
|
tracearea(org-'0 0 1'*enemy_zdiff,destiny,min,max,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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(self.skin==0)//fire imps attack less- try to get close
|
||
|
if(random()<0.5)
|
||
|
return FALSE;
|
||
|
|
||
|
self.think=imp_attack_anim;
|
||
|
thinktime self : 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void imp_hover () [++ $impfly1 .. $impfly20]
|
||
|
{
|
||
|
float too_close;
|
||
|
if (self.frame == $impfly1)
|
||
|
{
|
||
|
if(pointcontents(self.origin+self.view_ofs)==CONTENT_WATER)
|
||
|
self.noise="hydra/turn-s.wav";
|
||
|
else if(self.skin==3)
|
||
|
self.noise="imp/flybig.wav";
|
||
|
else
|
||
|
self.noise="imp/fly.wav";
|
||
|
sound (self, CHAN_BODY, self.noise, 1, ATTN_NORM);
|
||
|
}
|
||
|
|
||
|
if(self.skin==3)
|
||
|
if(self.lifetime<time||!self.controller.flags2&FL_ALIVE||self.controller.imp_count!=self.imp_count)
|
||
|
{
|
||
|
self.think=summoned_imp_die;
|
||
|
thinktime self : 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(self.enemy)
|
||
|
if(self.attack_state!=AS_FERRY)
|
||
|
checkenemy();
|
||
|
|
||
|
if(!self.enemy||(self.enemy==self.controller&&self.skin==3))
|
||
|
if(imp_find_target())
|
||
|
return;
|
||
|
|
||
|
if(self.enemy)
|
||
|
{
|
||
|
self.velocity=self.velocity*(1/1.05);
|
||
|
ai_face();
|
||
|
imp_set_speeds();
|
||
|
check_z_move(self.level);
|
||
|
|
||
|
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_STRAIGHT)
|
||
|
{
|
||
|
self.think=imp_fly;
|
||
|
thinktime self : 0;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(self.enemy!=world||self.goalentity!=world)
|
||
|
if(imp_new_action())
|
||
|
return;
|
||
|
|
||
|
MonsterCheckContents();
|
||
|
}
|
||
|
|
||
|
void() stone_imp_awaken = [++ $impup7 .. $impup23]
|
||
|
{
|
||
|
if(self.frame==$impup10)
|
||
|
{
|
||
|
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
||
|
self.count = 0;
|
||
|
setsize (self, '-16 -16 0', '16 16 36');
|
||
|
self.origin_z-=14;
|
||
|
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= SUB_Null;
|
||
|
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();
|
||
|
ai_pain(2);
|
||
|
if(self.frame==$impup14)
|
||
|
{
|
||
|
self.touch=SUB_Null;
|
||
|
self.think=imp_hover;
|
||
|
thinktime self : 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void imp_pain_anim1 () [-- $impup14 .. $impup6]
|
||
|
{
|
||
|
check_pos_enemy();
|
||
|
ai_pain(2);
|
||
|
if(self.frame==$impup6)
|
||
|
{
|
||
|
self.think=imp_pain_anim2;
|
||
|
thinktime self : 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void(entity attacker, float damage) imp_pain =
|
||
|
{
|
||
|
if(self.monster_awake)
|
||
|
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&&attacker!=world&&self.touch!=SUB_Null&&self.monster_awake)//FIXME: make more logical
|
||
|
return;
|
||
|
|
||
|
self.monster_awake=TRUE;
|
||
|
|
||
|
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
|
||
|
if (self.spawnflags & SF_IMP_DORMANT)
|
||
|
{
|
||
|
self.frame=$impup1;
|
||
|
self.think=stone_imp_awaken;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(self.skin==3)
|
||
|
sound(self,CHAN_VOICE,"imp/upbig.wav",1,ATTN_NORM);
|
||
|
else
|
||
|
sound(self,CHAN_VOICE,"imp/up.wav",1,ATTN_NORM);
|
||
|
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 (LocateTarget())
|
||
|
{ // We found a target
|
||
|
if (self.skin==2)
|
||
|
{
|
||
|
float self_infront, self_vis, enemy_dist, r1, r2;
|
||
|
self_infront=infront_of_ent(self,self.enemy);
|
||
|
self_vis=visible2ent(self,self.enemy);
|
||
|
enemy_dist=vlen(self.origin-self.enemy.origin);
|
||
|
r1=random();
|
||
|
r2=random();
|
||
|
if((self_infront&&self_vis&&r1<0.1&&r2<0.5&&enemy_dist<1000)||enemy_dist<=RANGE_MELEE)
|
||
|
{
|
||
|
self.goalentity = self.enemy;
|
||
|
self.think=stone_imp_awaken;
|
||
|
thinktime self : 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self.goalentity=self.enemy=world;
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self.goalentity = self.enemy;
|
||
|
self.think = self.th_run;
|
||
|
thinktime self : 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if(self.classname=="monster_imp_lord")
|
||
|
self.enemy=self.goalentity=self.controller;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void imp_wait() [++ $impwat1 .. $impwat24]
|
||
|
{
|
||
|
|
||
|
if(self.skin==2)
|
||
|
self.frame=$impwat1;
|
||
|
else if(!self.flags&FL_ONGROUND)
|
||
|
{
|
||
|
self.think=imp_hover;
|
||
|
thinktime self : 0;
|
||
|
}
|
||
|
|
||
|
if(random()<0.5)
|
||
|
if(imp_find_target())
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void imp_fly () [++ $impfly1 .. $impfly20]
|
||
|
{
|
||
|
if (self.frame == $impfly1)
|
||
|
{
|
||
|
if(pointcontents(self.origin+self.view_ofs)==CONTENT_WATER)
|
||
|
self.noise="hydra/turn-s.wav";
|
||
|
else if(self.skin==3)
|
||
|
self.noise="imp/flybig.wav";
|
||
|
else
|
||
|
self.noise="imp/fly.wav";
|
||
|
sound (self, CHAN_BODY, self.noise, 1, ATTN_NORM);
|
||
|
}
|
||
|
|
||
|
if(self.skin==3)
|
||
|
if(self.lifetime<time||!self.controller.flags2&FL_ALIVE||self.controller.imp_count!=self.imp_count)
|
||
|
{
|
||
|
self.think=summoned_imp_die;
|
||
|
thinktime self : 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
imp_move();
|
||
|
|
||
|
if(imp_check_defense())
|
||
|
return;
|
||
|
|
||
|
if(imp_check_attack())
|
||
|
return;
|
||
|
|
||
|
if(imp_new_action())
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
void imp_awaken () [++ $impup1 .. $impup23]
|
||
|
{
|
||
|
if (self.frame == $impup1)
|
||
|
if(self.skin==3)
|
||
|
sound (self, CHAN_VOICE, "imp/upbig.wav", 1, ATTN_NORM);
|
||
|
else
|
||
|
sound (self, CHAN_VOICE, "imp/up.wav", 1, ATTN_NORM);
|
||
|
|
||
|
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.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;
|
||
|
if(!self.use)
|
||
|
self.use = monster_use;
|
||
|
|
||
|
self.pausetime = 99999999;
|
||
|
|
||
|
if(self.targetname!="")
|
||
|
{
|
||
|
self.frame=$impwat1;
|
||
|
self.think=imp_use;
|
||
|
self.nextthink=-1;
|
||
|
}
|
||
|
else if(self.enemy!=world)
|
||
|
imp_awaken();
|
||
|
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_model3 ("models/imp.mdl");
|
||
|
precache_model3 ("models/h_imp.mdl");//empty for now
|
||
|
if (self.classname == "monster_imp_lord")
|
||
|
{
|
||
|
precache_model3 ("models/shardice.mdl");
|
||
|
precache_model ("models/fireball.mdl");
|
||
|
precache_sound3("imp/upbig.wav");
|
||
|
precache_sound3("imp/diebig.wav");
|
||
|
precache_sound3("imp/swoopbig.wav");
|
||
|
precache_sound3("imp/flybig.wav");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
precache_sound3("imp/up.wav");
|
||
|
precache_sound3("imp/die.wav");
|
||
|
precache_sound3("imp/swoop.wav");
|
||
|
precache_sound3("imp/fly.wav");
|
||
|
if (self.classname == "monster_imp_ice")
|
||
|
precache_model3 ("models/shardice.mdl");
|
||
|
else
|
||
|
precache_model ("models/fireball.mdl");
|
||
|
}
|
||
|
precache_sound3("imp/swoophit.wav");
|
||
|
precache_sound3("imp/fireball.wav");
|
||
|
precache_sound3("imp/shard.wav");
|
||
|
precache_sound("hydra/turn-s.wav");
|
||
|
}
|
||
|
|
||
|
self.solid = SOLID_SLIDEBOX;
|
||
|
setmodel (self, "models/imp.mdl");
|
||
|
if (self.classname == "monster_imp_lord")
|
||
|
{
|
||
|
self.drawflags(+)SCALE_ORIGIN_CENTER;
|
||
|
self.scale=2.3;//2?
|
||
|
setsize (self, '-32 -32 -32', '32 32 32');
|
||
|
self.hull=HULL_HYDRA;
|
||
|
self.view_ofs=self.proj_ofs='0 0 82';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self.scale=1;
|
||
|
setsize (self, '-16 -16 0', '16 16 36');
|
||
|
self.view_ofs=self.proj_ofs='0 0 33';
|
||
|
self.hull=HULL_CROUCH;
|
||
|
}
|
||
|
self.headmodel = "models/h_imp.mdl";
|
||
|
|
||
|
if(which_skin==3)
|
||
|
self.flags (+) FL_COLDHEAL|FL_FIREHEAL;
|
||
|
else if(which_skin==1)
|
||
|
self.flags (+) FL_COLDHEAL;
|
||
|
else
|
||
|
self.flags (+) FL_FIREHEAL;
|
||
|
|
||
|
if(self.wait!=-1)
|
||
|
{
|
||
|
self.movetype = MOVETYPE_FLY;
|
||
|
self.takedamage=DAMAGE_YES;
|
||
|
|
||
|
self.skin = which_skin;
|
||
|
self.impType = which_skin;
|
||
|
|
||
|
self.flags2 (+) FL_ALIVE;
|
||
|
self.thingtype=THINGTYPE_FLESH;
|
||
|
if (self.classname == "monster_imp_lord")
|
||
|
{
|
||
|
self.max_health=self.health = 600;
|
||
|
self.experience_value = 3000;
|
||
|
self.mass = 10;
|
||
|
self.th_die = summoned_imp_die;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
self.max_health=self.health = 75+self.skin*25;
|
||
|
self.experience_value = 400 +self.skin*100;
|
||
|
self.mass = 3;
|
||
|
self.th_die = imp_die_init;
|
||
|
}
|
||
|
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_missile = imp_enter_swoop;
|
||
|
self.th_melee = imp_attack;
|
||
|
}
|
||
|
|
||
|
self.yaw_speed=8;
|
||
|
self.speed=10;
|
||
|
self.attack_state = AS_STRAIGHT;
|
||
|
self.level=0;
|
||
|
|
||
|
if (self.spawnflags & SF_IMP_DORMANT)
|
||
|
{
|
||
|
self.classname="gargoyle";
|
||
|
self.scale=1.5;
|
||
|
self.artifact_active (+) ARTFLAG_STONED;
|
||
|
setsize (self, '-16 -16 -14', '16 16 22');
|
||
|
self.origin_z+=14;
|
||
|
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;
|
||
|
|
||
|
total_monsters += 1;
|
||
|
if(self.enemy)
|
||
|
self.th_run();
|
||
|
else 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 gargoyle
|
||
|
Grunt monster - common. Shoots multiple ice shards. Can only be killed by defrosting it.
|
||
|
immune to ice attacks
|
||
|
|
||
|
gargoyle = 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 gargoyle
|
||
|
Grunt monster - common. Shoots a fireball. Can only be killed by defrosting it.
|
||
|
|
||
|
gargoyle = 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 gargoyle
|
||
|
Big imp dude- kicks butt and takes names
|
||
|
|
||
|
gargoyle = 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 ()
|
||
|
{
|
||
|
if(self.flags2&FL_SUMMONED)
|
||
|
self.spawnflags(+)MONSTER_HOVER;
|
||
|
init_imp(3);
|
||
|
}
|