hexen2-data1-hc/meteor.hc
1997-11-11 00:00:00 +00:00

693 lines
18 KiB
C++

/*
* $Header: /H3/game/hcode/meteor.hc 77 9/11/97 12:02p Mgummelt $
*/
/*
==============================================================================
Q:\art\models\weapons\meteor\final\meteor.hc
==============================================================================
*/
// For building the model
$cd Q:\art\models\weapons\meteor\final
$origin 0 0 0
$base BASE skin
$skin skin
$flags 0
//
$frame idle
//
$frame Select1 Select2 Select3 Select4 Select5
$frame Select6 Select7 Select8 Select9 Select10
$frame Select11 Select12 Select13 Select14 Select15
$frame Select16 Select17 Select18
//
$frame fire1 fire2 fire3 fire4 fire5
$frame fire6 fire7 fire8 fire9
void MeteoriteFizzle (void)
{
CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2);
remove(self);
}
void MeteorThink (void)
{
if(self.lifetime<time)
if(self.dmg==3)
MeteoriteFizzle();
else
MultiExplode();
if(self.dmg>3)
CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2);
self.think=MeteorThink;
thinktime self : 0.3;
}
void MeteorTouch (void)
{
if(other.controller==self.owner)
return;
if(self.dmg==3)
{
if(other==world)
{
if(!self.pain_finished&&random()<0.3)
{
sound(self.controller,CHAN_BODY,"misc/rubble.wav",1,ATTN_NORM);
self.pain_finished=TRUE;
}
return;
}
else if(other.classname=="meteor")
return;
}
if(other.takedamage&&other.health)
{
T_Damage(other,self,self.owner,self.dmg);
if(self.dmg>3)
{
if((other.flags&FL_CLIENT||other.flags&FL_MONSTER)&&other.mass<200)
{
vector hitdir;
hitdir=self.o_angle*300;
hitdir_z+=150;
if(hitdir_z<0)
hitdir_z=0;
other.velocity=hitdir;
other.flags(-)FL_ONGROUND;
}
self.dmg/=2;
}
}
else if(self.dmg>3)
self.dmg=100;
if(self.dmg>3)
MultiExplode();
else
MeteoriteFizzle();
}
void FireMeteor (string type)
{
vector org;
entity meteor;
meteor=spawn();
setmodel(meteor,"models/tempmetr.mdl");
if(type=="minimeteor")
{
meteor.classname="minimeteor";
meteor.velocity=RandomVector('200 200 0');
meteor.velocity_z=random(200,400);
meteor.lifetime=time + 1.5;
meteor.dmg=3;
meteor.scale=random(0.15,0.45);
meteor.movetype=MOVETYPE_BOUNCE;
org=self.origin;
setsize(meteor,'0 0 0', '0 0 0');
}
else
{
meteor.th_die=MultiExplode;
if(self.classname=="player")
{
self.greenmana-=8;
self.velocity+=normalize(v_forward)*-300;//include mass
self.flags(-)FL_ONGROUND;
}
meteor.classname="meteor";
self.punchangle_x = -6;
sound(self,CHAN_AUTO,"crusader/metfire.wav",1,ATTN_NORM);
self.attack_finished=time + 0.7;
self.effects(+)EF_MUZZLEFLASH;
makevectors(self.v_angle);
meteor.speed=1000;
meteor.o_angle=normalize(v_forward);
meteor.velocity=meteor.o_angle*meteor.speed;
meteor.veer=30;
meteor.lifetime=time + 5;
meteor.dmg=75;
meteor.movetype=MOVETYPE_FLYMISSILE;
org=self.origin+self.proj_ofs+v_forward*12;
setsize(meteor,'0 0 0', '0 0 0');
}
// meteor.abslight = 0.5;
meteor.drawflags(+)MLS_FIREFLICKER;//|MLS_ABSLIGHT;
meteor.avelocity=RandomVector('360 360 360');
if(self.classname=="tornato")
meteor.owner=self.controller;
else if(self.classname=="meteor")
meteor.owner=self.owner;
else
meteor.owner=self;
meteor.controller=self;
meteor.solid=SOLID_BBOX;
meteor.touch=MeteorTouch;
meteor.think=MeteorThink;
thinktime meteor : 0.1;
setorigin(meteor,org);
}
void() tornato_die = [++24 .. 47]
{
if(cycle_wrapped)
{
if(self.enemy)
{
self.enemy.avelocity='0 500 0';
if(self.enemy.flags2&FL_ALIVE)
self.enemy.movetype=self.enemy.oldmovetype;
}
if(self.movechain!=world)
remove(self.movechain);
remove(self);
}
self.movechain.frame+=1;
if(self.movechain.frame>24)
self.movechain.frame=0;
if(self.movechain.scale>0.04)
self.movechain.scale-=0.04;
if(self.movechain.avelocity_y>0)
self.movechain.avelocity_y-=20;
};
void() tornato_spin = [++0 .. 23]
{
float distance,content;
if(time>self.lifetime||self.torncount<self.owner.torncount - 1)
{
self.movechain.drawflags(+)MLS_ABSLIGHT|SCALE_ORIGIN_BOTTOM|SCALE_TYPE_XYONLY;
self.think=tornato_die;
thinktime self : 0;
}
self.movechain.frame+=1;
if(self.movechain.frame>24)
self.movechain.frame=0;
//FIXME: add tracking to movement and firing.
if(random()<0.2)
{
self.velocity_x+=random(-100*self.scale,100*self.scale);
if(fabs(self.velocity_x)>1000)
self.velocity_x/=2;
}
if(random()<0.2)
{
self.velocity_y+=random(-100*self.scale,100*self.scale);
if(fabs(self.velocity_y)>1000)
self.velocity_y/=2;
}
content=pointcontents(self.origin);
if(content==CONTENT_WATER||content==CONTENT_LAVA)
{
self.velocity_z+=random(33,200);
particle4(self.origin,random(20),264*15,PARTICLETYPE_GRAV,random()*10);
particle4(self.origin,random(20),random(406,414),PARTICLETYPE_GRAV,random(10));
}
else if(random()<0.2)
{
distance=random(-30,15);//tries to stay on ground
if(self.goalentity!=world&&self.enemy!=self.goalentity)
if(self.goalentity.origin_z>self.origin_z)//unless goal is above it
distance=random(-30,30);
self.velocity_z+=distance;
if(fabs(self.velocity_z)>333)
self.velocity_z/=3;
}
if(self.enemy!=world)
{
vector org, dir;
float let_go;
self.velocity=self.velocity*0.5;
org=self.origin;
if(self.enemy.size_z>=self.size_z)
org=self.origin;
else
org_z+=random(10)*self.scale+4*self.scale;
if(!self.enemy.flags2&FL_TORNATO_SAFE)
{
self.enemy.velocity='0 0 0';
setorigin(self.enemy,org);
}
else
{
self.enemy.flags2(-)FL_TORNATO_SAFE;
let_go=TRUE;
}
//FIXME: throw the Sheep
if(!let_go&&self.enemy!=world&&!self.enemy.flags2&FL_ALIVE)//Don't let go of it if it's not a creature
if(random()>=0.4||self.goalentity==world||(!visible(self.goalentity))||self.goalentity.health<=0)
self.pain_finished=time+1;
else
{
self.pain_finished=-1;
if(self.goalentity.solid==SOLID_BSP&&self.goalentity.origin=='0 0 0')
dir=normalize((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.enemy.origin);
else
dir=normalize(self.goalentity.origin-self.enemy.origin);
}
if(!let_go&&self.enemy.takedamage&&self.enemy.health>0&&self.pain_finished>time)
{
if(random()<0.3)
T_Damage(self.enemy,self,self.owner,self.scale);//was 3*is this needed with meteors flying out?
}
else
{
if(!let_go)
if(self.pain_finished=-1) //Throw it at my goal!
self.enemy.velocity=dir*350*self.scale;
else
{
self.enemy.velocity_z=random(200*self.scale);
self.enemy.velocity_x=random(200*self.scale,-200*self.scale);
self.enemy.velocity_y=random(200*self.scale,-200*self.scale);
}
self.pain_finished=time;
self.enemy.safe_time=time+3+let_go*7;//let them get thrown away from the tornado for a full 3 seconds
if(self.enemy.flags2&FL_ALIVE)
{
self.enemy.movetype=self.enemy.oldmovetype;
if(self.enemy.classname=="player_sheep")
{
sound(self.enemy,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM);
self.enemy.pain_finished=time+1;
}
}
if(!let_go)
self.enemy.avelocity_y=random(200*self.scale);
self.enemy=self.movechain.movechain=world;
}
if(self.enemy.classname=="player")
{
self.enemy.punchangle_y=random(3,12);//FIXME: Do WRITEBYTE on angles?
self.enemy.punchangle_x=random(-3,3);//FIXME: Do WRITEBYTE on angles?
self.enemy.punchangle_z=random(-3,3);//FIXME: Do WRITEBYTE on angles?
}
if(self.enemy!=world&&self.goalentity==self.enemy)
self.goalentity=world;//Hunt a new target, if it can
}
if(random()<0.3)
{
entity sucker;
float seekspeed;
sucker=findradius(self.origin,500);
while(sucker)
{
if(sucker.takedamage&&sucker.health&&sucker!=self.enemy&&sucker.mass<500*self.scale&&visible(sucker)&&sucker!=self.owner)
if(sucker.movetype&&sucker.movetype!=MOVETYPE_PUSH)
{
seekspeed=(500 - vlen(sucker.origin-self.origin));
sucker.velocity=normalize(self.origin-sucker.origin)*seekspeed;
if(sucker.velocity_z<30)
sucker.velocity_z=30;
sucker.flags(-)FL_ONGROUND;
if(sucker.classname=="player")
sucker.adjust_velocity=sucker.velocity;
}
sucker=sucker.chain;
}
if(self.goalentity!=world&&visible(self.goalentity)&&self.goalentity.health>0)
{
seekspeed = random(150,333);
if(self.goalentity.solid==SOLID_BSP&&self.goalentity.origin=='0 0 0')
distance=vlen((self.goalentity.absmax+self.goalentity.absmin)*0.5-self.origin);
else
distance=vlen(self.goalentity.origin-self.origin);//Swoop in when close!
if(distance<256)
seekspeed+=(256-distance);
if(self.goalentity.velocity)
seekspeed+=vlen(self.goalentity.velocity);
self.velocity=(self.velocity*3+normalize(self.goalentity.origin-self.origin)*seekspeed*self.scale)*0.25;//too fast?
}
else
{
float bestdist;
self.goalentity=world;//out of sight, out of mind
bestdist=1001;
sucker=findradius(self.origin,1000);
while(sucker)
{
if(sucker.takedamage&&sucker.health&&sucker!=self.enemy&&sucker.mass<500*self.scale&&visible(sucker)&&sucker!=self.owner&&!sucker.effects&EF_NODRAW)
{
if(sucker.solid==SOLID_BSP&&sucker.origin=='0 0 0')
distance=vlen((sucker.absmax+sucker.absmin)*0.5-self.origin);
else
distance=vlen(sucker.origin-self.origin);
if(self.goalentity.velocity=='0 0 0')
{
if(sucker.velocity!='0 0 0'&&(sucker.flags2&FL_ALIVE))
{
bestdist=distance;
self.goalentity=sucker;
}
else if(!self.goalentity.flags2&FL_ALIVE)
{
if(sucker.flags2&FL_ALIVE)
{
bestdist=distance;
self.goalentity=sucker;
}
else if(distance<bestdist)
{
bestdist=distance;
self.goalentity=sucker;
}
}
else if(sucker.flags2&FL_ALIVE&&distance<bestdist)
{
bestdist=distance;
self.goalentity=sucker;
}
}
else if(distance<bestdist&&sucker.velocity!='0 0 0'&&(sucker.flags2&FL_ALIVE))
{
bestdist=distance;
self.goalentity=sucker;
}
}
sucker=sucker.chain;
}
}
}
if(random()<0.1)
{
if(random()<0.1)
{
self.proj_ofs_z=random(6,54);
self.v_angle_x=random(-30,30);
self.v_angle_y=random(-360,360);
FireMeteor("meteor");
}
else
FireMeteor("minimeteor");
}
if(self.flags&FL_ONGROUND)
{
vector dir;
self.velocity_z*=-0.333;//Maybe a little more bounce?
self.flags(-)FL_ONGROUND;
dir_z=random(20,70);
distance=random(10,30);
SpawnPuff (self.origin, dir,distance,self);
CreateWhiteSmoke(self.origin,'0 0 8',HX_FRAME_TIME * 2);
}
if(self.t_width<time)
{
sound(self,CHAN_VOICE,"crusader/tornado.wav",1,ATTN_NORM);
self.t_width=time+1;
}
};
void()funnal_touch;
void tornato_merge (void)
{
//FIXME: Don't scale up rocks- just add more rocks?
self.scale+=0.025;
self.owner.scale+=0.025;
self.goalentity.scale-=0.024;
self.goalentity.owner.scale-=0.024;
if(self.scale>=self.target_scale)
{
self.touch=funnal_touch;
self.scale=self.owner.scale=self.target_scale;
self.think=SUB_Null;
self.nextthink=-1;
remove(self.goalentity.owner);
remove(self.goalentity);
}
else
{
self.think=tornato_merge;
thinktime self : 0.01;
}
}
void funnal_touch (void)
{
//FIXME: Ignore the controlling player's projectiles, leaving it in to test
if(other.flags&FL_MONSTER&&other.monsterclass>=CLASS_BOSS)
{
T_Damage(other,self,self.owner,7);
traceline((self.absmin+self.absmax)*0.5,(other.absmin+other.absmax)*0.5,FALSE,self);
SpawnPuff(trace_endpos,randomv('-1 -1 -1','1 1 1'),5,other);
return;
}
if(other==self.controller||other.controller==self.owner||other==world||other==self.owner||other==self.owner||other.classname=="tornato"||(other.classname=="funnal"&&other.aflag)||other.movetype==MOVETYPE_PUSH)
return;
if(self.aflag)
{
self.owner.think=SUB_Remove;
self.think=SUB_Remove;
return;
}
if(other.classname=="funnal"&&other.scale>=1&&self.scale>=1&&other.scale+self.scale<2.5)
{
//Add random to stall the merging
tracearea(self.origin,self.origin,self.mins+other.mins,self.maxs+other.maxs,TRUE,self);
if(trace_fraction<1)
return;
self.goalentity=other;
self.touch=other.touch=SUB_Null;
if(other.controller!=self.controller)
self.owner.owner=self.owner.controller=self.controller=self.owner;
//make scaling gradual
self.drawflags=MLS_ABSLIGHT|SCALE_ORIGIN_BOTTOM;
self.owner.drawflags=SCALE_ORIGIN_BOTTOM;
other.drawflags=MLS_ABSLIGHT+SCALE_ORIGIN_BOTTOM+SCALE_TYPE_XYONLY;
self.target_scale=self.scale+other.scale;
if(self.target_scale>2.5)
self.target_scale=2.5;
setsize(self,self.mins+other.mins,self.maxs+other.maxs);
setsize(self.owner,self.owner.mins+other.owner.mins,self.owner.maxs+other.owner.maxs);
tornato_merge();
}
else if(other!=self.movechain&&other.movetype&&other.mass<500*self.scale&&other.classname!="funnal")//Can't pick up or move extremely heavy objects, bounce off them?
{
if(other.health&&other.takedamage&&other.solid!=SOLID_BSP)//Ignore health>1000?
{
if(!other.touch)
other.touch=obj_push;//Experimental
if(self.movechain==world&&other.safe_time<time)//&&self.scale>=1)
{
self.movechain=other;
other.flags(+)FL_MOVECHAIN_ANGLE;
setorigin(other,self.origin+'0 0 4');//maybe need to take on bounding box of captured enemy too?
other.velocity='0 0 0';
if(other.flags2&FL_ALIVE)
other.avelocity='0 0 0';
else
{
other.avelocity_x=random(360);
other.avelocity_z=random(360);
}
other.oldmovetype=other.movetype;
other.movetype=MOVETYPE_NONE;
self.owner.enemy=other;
self.owner.pain_finished=time+random(3,10);//How long to hold them before throwing them away
if(other.classname=="player_sheep"&&other.flags2&FL_ALIVE)
{
sound(other,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM);
other.pain_finished=time+1;
}
return;
}
}
vector dir;
dir=normalize(self.angles);
dir*=random(200,700)*self.scale;
other.velocity+=dir;
other.velocity_z=random(100,250)*self.scale;
other.flags(-)FL_ONGROUND;
if(other.takedamage)
T_Damage(other,self.owner,self.owner.controller,5*self.scale);
if(other.classname=="player_sheep"&&other.flags2&FL_ALIVE)
{
sound(other,CHAN_VOICE,"misc/sheepfly.wav",1,ATTN_NORM);
other.pain_finished=time+1;
}
}
}
void() tornato_grow = [++48 .. 72]
{
if(cycle_wrapped)
{
self.movechain.scale=1;
self.think=tornato_spin;
thinktime self : 0;
}
self.movechain.frame+=1;
if(self.movechain.frame>24)
self.movechain.frame=0;
self.movechain.scale+=0.04;
};
void FireMeteorTornado (void)
{
/*
FIX:
1: BUG:If pull someone out of water & they die, stay in swim mode.(Fly mode does this with water too)
2: More particles & splash sound when hit water
3: Deflect projectiles
4: Limit 2, if 3rd made, erase 1st
5: Shorten life?
6: Can't hurt owner
9: Player's view should actually be changed with WRITEBYTE's?
10: Screw up aim of people inside tornado
11: gradual suck in, then stick to center?
12: incorporate mass? At least check to see if it can be picked up, maybe give a little resistance
13: Meteors are going through walls
14: Change bounding box to match what it picked up?
15: Scale up to match something big it picked up?
16: Check it there's room in front to make it, and at what height, use v_forward if possible. If not enough room:?
17: Auntie Em, Auntie Em!
18: Don't even consider movetype_push's?
19: Scale randomly?
20: Origins MUST be at bottom
21: Particle and Puff Sprites at origin when onground
22: If pick up something not alive, throw it at goalentity? Random chance?
23: Bounding box should be a little bigger
24: If it hits water while holding a player, it should go down and drown them.
*/
entity tornato,funnal;
vector org;
self.greenmana-=20;
sound(self,CHAN_WEAPON,"crusader/torngo.wav",1,ATTN_NORM);
makevectors(self.v_angle);
org=self.origin+normalize(v_forward)*16;
org_z=self.origin_z+1;
tornato=spawn();
self.torncount+=1;
tornato.torncount=self.torncount;
tornato.solid=SOLID_NOT;
tornato.movetype=MOVETYPE_FLY;
tornato.owner=tornato.controller=self;
tornato.classname="tornato";
tornato.enemy=world;
setmodel(tornato,"models/tornato.mdl");
setsize(tornato,'-18 -18 -3','18 18 64');
tornato.hull=HULL_PLAYER;
setorigin(tornato,org);
tornato.velocity=normalize(v_forward)*250+'0 0 20';
tornato.velocity_z=0;
tornato.scale=1;
if(visible(self.enemy)&&self.enemy.flags2&FL_ALIVE)//Infront too?
tornato.goalentity=self.enemy;
tornato.lifetime=time + 20;
tornato.think=tornato_grow;
thinktime tornato : 0;
funnal=spawn();
funnal.owner=tornato;
funnal.solid=SOLID_TRIGGER;
funnal.classname="funnal";
funnal.movetype=MOVETYPE_FLYMISSILE;
funnal.drawflags(+)MLS_ABSLIGHT|SCALE_ORIGIN_BOTTOM|SCALE_TYPE_ZONLY;
funnal.abslight=0.2;
funnal.scale=0.01;
tornato.movechain=funnal;
funnal.avelocity='0 100 0';
funnal.controller=self;
funnal.touch=funnal_touch;
funnal.lifetime=time+1.7;
setmodel(funnal,"models/funnal.mdl");
setsize(funnal,'-18 -18 -3','18 18 64');
funnal.hull=HULL_PLAYER;
setorigin(funnal,org);
}
void()meteor_ready_loop;
void() Cru_Met_Attack;
void meteor_power_fire (void)
{
self.wfs = advanceweaponframe($fire1,$fire9);
self.th_weapon=meteor_power_fire;
if(self.weaponframe==$fire2 && self.attack_finished<=time)
FireMeteorTornado();
if(self.wfs==WF_CYCLE_WRAPPED)
{
self.last_attack=time;
meteor_ready_loop();
}
}
void meteor_fire (void)
{
self.wfs = advanceweaponframe($fire1,$fire9);
self.th_weapon=meteor_fire;
if((!self.button0||self.attack_finished>time)&&self.wfs==WF_CYCLE_WRAPPED)
{
self.last_attack=time;
meteor_ready_loop();
}
else if(self.weaponframe==$fire1 &&self.attack_finished<=time)
FireMeteor("meteor");
}
void() Cru_Met_Attack =
{
if(self.artifact_active&ART_TOMEOFPOWER)
self.th_weapon=meteor_power_fire;
else
self.th_weapon=meteor_fire;
thinktime self : 0;
};
void meteor_ready_loop (void)
{
self.weaponframe = $idle;
self.th_weapon=meteor_ready_loop;
}
void meteor_select (void)
{
//go to ready loop, not relaxed?
self.wfs = advanceweaponframe($Select1,$Select16);
self.weaponmodel = "models/meteor.mdl";
self.th_weapon=meteor_select;
self.last_attack=time;
if(self.wfs==WF_CYCLE_WRAPPED)
{
self.attack_finished = time - 1;
meteor_ready_loop();
}
}
void meteor_deselect (void)
{
self.wfs = advanceweaponframe($Select16,$Select1);
self.th_weapon=meteor_deselect;
if(self.wfs==WF_CYCLE_WRAPPED)
W_SetCurrentAmmo();
}