Updated blood and gore code, it's less WIP now. Updated readme with some additional notes. Removed Heretic compat player classes as they are no longer needed.
824 lines
21 KiB
Text
824 lines
21 KiB
Text
Class BioAmmo : Ammo
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_BIOAMMO";
|
|
Inventory.PickupMessage "$I_BIOAMMO";
|
|
Inventory.Amount 25;
|
|
Inventory.MaxAmount 100;
|
|
Ammo.BackpackAmount 50;
|
|
Ammo.BackpackMaxAmount 200;
|
|
Ammo.DropAmount 10;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BIOA A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class BioAmmo2 : BioAmmo // small variant
|
|
{
|
|
Default
|
|
{
|
|
Tag "$T_BIOAMMO2";
|
|
Inventory.PickupMessage "$I_BIOAMMO2";
|
|
Inventory.Amount 10;
|
|
Ammo.DropAmount 5;
|
|
Scale 0.5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BIOA A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class BioSpark : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+FORCEXYBILLBOARD;
|
|
+MISSILE;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
-BOUNCEAUTOOFF;
|
|
+BOUNCEAUTOOFFFLOORONLY;
|
|
BounceType "Doom";
|
|
BounceFactor 0.5;
|
|
WallBounceFactor 0.5;
|
|
Gravity 0.2;
|
|
Scale 0.06;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
frame = Random[GES](0,4);
|
|
roll = FRandom[GES](0,360);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
GBLB # 1 Bright A_FadeOut(FRandom[GES](0.005,0.015));
|
|
Wait;
|
|
Death:
|
|
GBLB # 1 Bright A_FadeOut(FRandom[GES](0.04,0.06));
|
|
Wait;
|
|
Dummy:
|
|
GBLB ABCDE -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class BioHitbox : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 3;
|
|
Height 6;
|
|
+SHOOTABLE;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+NOBLOOD;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
if ( target ) A_SetSize(1.5*target.scale.x,3*target.scale.y);
|
|
}
|
|
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
|
{
|
|
if ( inflictor == target ) return 0;
|
|
if ( target && !target.InStateSequence(target.CurState,target.ResolveState("XDeath")) )
|
|
{
|
|
target.bAMBUSH = true;
|
|
target.ExplodeMissile(null,inflictor);
|
|
}
|
|
return 0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
SetOrigin(target.pos-(0,0,height*0.5),true);
|
|
}
|
|
override bool CanCollideWith( Actor other, bool passive )
|
|
{
|
|
return !target.bNoGravity; // don't "intercept" while flying (doesn't seem to work, but at least I tried)
|
|
}
|
|
}
|
|
|
|
Class BioLight : DynamicLight
|
|
{
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
Args 64,255,48,8;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
SetOrigin(target.pos,true);
|
|
args[LIGHT_INTENSITY] = int(8*target.Scale.x);
|
|
}
|
|
}
|
|
|
|
Class BioXLight : DynamicLight
|
|
{
|
|
double lifetime;
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
Args 64,255,48,30;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
lifetime = 1.0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
args[LIGHT_RED] = int(64*lifetime);
|
|
args[LIGHT_GREEN] = int(255*lifetime);
|
|
args[LIGHT_BLUE] = int(48*lifetime);
|
|
lifetime -= 0.05;
|
|
if ( lifetime <= 0 ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class BioGel : Actor
|
|
{
|
|
Actor l, b;
|
|
enum EHitType
|
|
{
|
|
HIT_NONE,
|
|
HIT_WALL,
|
|
HIT_CEILING,
|
|
HIT_FLOOR
|
|
};
|
|
int hittype;
|
|
int deadtimer, dttimer;
|
|
Line atline;
|
|
int atside;
|
|
int atpart;
|
|
int atplane;
|
|
Sector atsector;
|
|
double atz;
|
|
double rollvel, pitchvel, yawvel;
|
|
Vector3 normal;
|
|
Actor atbridge;
|
|
bool onbridge;
|
|
Vector3 atbridgeofs;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
vel.z += 3;
|
|
deadtimer = 105;
|
|
l = Spawn("BioLight",pos);
|
|
l.target = self;
|
|
rollvel = FRandom[GES](10,30)*RandomPick[GES](-1,1);
|
|
pitchvel = FRandom[GES](10,30)*RandomPick[GES](-1,1);
|
|
yawvel = FRandom[GES](10,30)*RandomPick[GES](-1,1);
|
|
}
|
|
override int SpecialMissileHit( Actor victim )
|
|
{
|
|
if ( victim == b ) return 1;
|
|
if ( (victim is 'BioHitbox') && ((victim.target == master) || (victim.target.master == master)) ) return 1;
|
|
return -1;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( !bNOGRAVITY )
|
|
{
|
|
roll += rollvel;
|
|
pitch += pitchvel;
|
|
pitch += yawvel;
|
|
if ( waterlevel > 0 )
|
|
{
|
|
vel.xy *= 0.98;
|
|
rollvel *= 0.98;
|
|
pitchvel *= 0.98;
|
|
yawvel *= 0.98;
|
|
}
|
|
}
|
|
if ( deadtimer > -2 )
|
|
{
|
|
if ( onbridge ) // attempt to follow the movement of the bridge (if it's moving)
|
|
{
|
|
if ( atbridge )
|
|
{
|
|
if ( !Warp(atbridge,atbridgeofs.x,atbridgeofs.y,atbridgeofs.z,0,WARPF_ABSOLUTEOFFSET|WARPF_USECALLERANGLE|WARPF_COPYINTERPOLATION) )
|
|
deadtimer = min(deadtimer,0);
|
|
}
|
|
else deadtimer = min(deadtimer,0);
|
|
}
|
|
if ( atline ) // attempt to follow the movement of the line
|
|
{
|
|
if ( atpart == 1 )
|
|
{
|
|
if ( atline.flags&Line.ML_DONTPEGTOP ) SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(1)),true);
|
|
else SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside?0:1].sector.GetPlaneTexZ(1)),true);
|
|
}
|
|
else if ( atpart == -1 )
|
|
{
|
|
if ( atline.flags&Line.ML_DONTPEGBOTTOM ) SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(0)),true);
|
|
else SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside?0:1].sector.GetPlaneTexZ(0)),true);
|
|
}
|
|
else if ( atline.flags&Line.ML_DONTPEGBOTTOM ) SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(0)),true);
|
|
else SetOrigin(Vec2OffsetZ(0,0,atz+atline.sidedef[atside].sector.GetPlaneTexZ(1)),true);
|
|
if ( (pos.z > ceilingz) || (pos.z < floorz) ) deadtimer = min(deadtimer,0);
|
|
}
|
|
else if ( atsector ) // attempt to follow the movement of the plane
|
|
{
|
|
SetOrigin(Vec2OffsetZ(0,0,atz+cursector.GetPlaneTexZ(atplane)),true);
|
|
if ( ceilingz-floorz <= 2 ) deadtimer = min(deadtimer,0);
|
|
}
|
|
}
|
|
if ( !InStateSequence(CurState,FindState("XDeath")) && ((!bNOGRAVITY && !Random[GES](0,2)) || !Random[GES](0,10)) )
|
|
{
|
|
int numpt = Min(20,int(Scale.x*2))+Random[GES](-1,1);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[GES](-1,1),FRandom[GES](-1,1),FRandom[GES](-1,1)).unit()*FRandom[GES](1,3);
|
|
Vector3 spos = pos+normal*2*scale.x;
|
|
let s = Spawn("BioSpark",spos);
|
|
s.vel = pvel;
|
|
if ( normal != (0,0,0) ) s.vel += normal*2;
|
|
}
|
|
}
|
|
if ( deadtimer <= -1 ) return;
|
|
let bi = BlockThingsIterator.Create(self,4*Scale.x);
|
|
while ( bi.Next() )
|
|
{
|
|
if ( !bi.Thing || !bi.Thing.bSHOOTABLE || (bi.Thing == b) || ((bi.Thing is 'BioHitbox') && ((bi.Thing.target.master == self) || (bi.Thing.target == master))) || (bi.Thing == target && !bHITOWNER) ) continue;
|
|
if ( (Distance2D(bi.Thing)-bi.Thing.radius <= 4*Scale.x) && ((bi.Thing.pos.z <= pos.z+4*Scale.x) && (bi.Thing.pos.z+bi.Thing.height >= pos.z-4*Scale.x)) ) deadtimer = 0;
|
|
}
|
|
if ( deadtimer-- <= 0 )
|
|
{
|
|
deadtimer = -1;
|
|
SetStateLabel("XDeath");
|
|
}
|
|
}
|
|
// align self to what surface was hit, currently does not support 3d floors + slopes properly
|
|
virtual void AlignSelf()
|
|
{
|
|
bINTERPOLATEANGLES = false;
|
|
bHITOWNER = true;
|
|
A_NoGravity();
|
|
A_Stop();
|
|
if ( bAMBUSH )
|
|
{
|
|
SetStateLabel("XDeath");
|
|
return;
|
|
}
|
|
A_SetSize(0.1,0);
|
|
if ( tracer && tracer.bACTLIKEBRIDGE )
|
|
{
|
|
atbridge = tracer;
|
|
onbridge = true;
|
|
if ( (pos.x+radius) <= (atbridge.pos.x-atbridge.radius) )
|
|
{
|
|
// west side
|
|
normal = (-1,0,0);
|
|
SetOrigin((atbridge.pos.x-atbridge.radius,pos.y,pos.z),false);
|
|
atbridgeofs = pos-atbridge.pos;
|
|
angle = 180;
|
|
pitch = 0;
|
|
roll = 180; // otherwise it slides upwards (UT changes roll like this too)
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_WALL;
|
|
}
|
|
else if ( (pos.x-radius) >= (atbridge.pos.x+atbridge.radius) )
|
|
{
|
|
// east side
|
|
normal = (1,0,0);
|
|
SetOrigin((atbridge.pos.x+atbridge.radius,pos.y,pos.z),false);
|
|
atbridgeofs = pos-atbridge.pos;
|
|
angle = 0;
|
|
pitch = 0;
|
|
roll = 180; // otherwise it slides upwards (UT changes roll like this too)
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_WALL;
|
|
}
|
|
else if ( (pos.y+radius) <= (atbridge.pos.y-atbridge.radius) )
|
|
{
|
|
// north side
|
|
normal = (0,-1,0);
|
|
SetOrigin((pos.x,atbridge.pos.y-atbridge.radius,pos.z),false);
|
|
atbridgeofs = pos-atbridge.pos;
|
|
angle = 270;
|
|
pitch = 0;
|
|
roll = 180; // otherwise it slides upwards (UT changes roll like this too)
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_WALL;
|
|
}
|
|
else if ( (pos.y-radius) >= (atbridge.pos.y+atbridge.radius) )
|
|
{
|
|
// south side
|
|
normal = (0,1,0);
|
|
SetOrigin((pos.x,atbridge.pos.y+atbridge.radius,pos.z),false);
|
|
atbridgeofs = pos-atbridge.pos;
|
|
angle = 90;
|
|
pitch = 0;
|
|
roll = 180; // otherwise it slides upwards (UT changes roll like this too)
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_WALL;
|
|
}
|
|
else if ( pos.z >= (atbridge.pos.z+atbridge.height) )
|
|
{
|
|
// top of actor
|
|
normal = (0,0,1);
|
|
SetOrigin((pos.x,pos.y,atbridge.pos.z+atbridge.height),false);
|
|
atbridgeofs = pos-atbridge.pos;
|
|
pitch = -90;
|
|
angle = 0;
|
|
roll = FRandom[GES](0,360);
|
|
hittype = HIT_FLOOR;
|
|
}
|
|
else if ( (pos.z+height) <= atbridge.pos.z )
|
|
{
|
|
// bottom of actor
|
|
normal = (0,0,-1);
|
|
SetOrigin((pos.x,pos.y,atbridge.pos.z),false);
|
|
pitch = 90;
|
|
angle = 0;
|
|
roll = FRandom[GES](0,360);
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_CEILING;
|
|
}
|
|
else
|
|
{
|
|
// inside of actor, just shove to the top or bottom based on our Z velocity
|
|
if ( vel.z <= 0 )
|
|
{
|
|
normal = (0,0,1);
|
|
SetOrigin((pos.x,pos.y,atbridge.pos.z+atbridge.height),false);
|
|
atbridgeofs = pos-atbridge.pos;
|
|
pitch = -90;
|
|
angle = 0;
|
|
roll = FRandom[GES](0,360);
|
|
hittype = HIT_FLOOR;
|
|
}
|
|
else
|
|
{
|
|
normal = (0,0,-1);
|
|
SetOrigin((pos.x,pos.y,atbridge.pos.z),false);
|
|
pitch = 90;
|
|
angle = 0;
|
|
roll = FRandom[GES](0,360);
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_CEILING;
|
|
}
|
|
}
|
|
}
|
|
else if ( BlockingFloor )
|
|
{
|
|
atsector = BlockingFloor;
|
|
atplane = 0;
|
|
normal = BlockingFloor.floorplane.Normal;
|
|
pitch = asin(-normal.z);
|
|
angle = atan2(normal.y,normal.x);
|
|
roll = FRandom[GES](0,360);
|
|
SetOrigin((pos.x,pos.y,floorz)+normal*0.5,false);
|
|
atz = pos.z-BlockingFloor.GetPlaneTexZ(0);
|
|
hittype = HIT_FLOOR;
|
|
}
|
|
else if ( BlockingCeiling )
|
|
{
|
|
atsector = BlockingCeiling;
|
|
atplane = 1;
|
|
normal = BlockingCeiling.ceilingplane.Normal;
|
|
pitch = asin(-normal.z);
|
|
angle = atan2(normal.y,normal.x);
|
|
roll = FRandom[GES](0,360);
|
|
SetOrigin((pos.x,pos.y,ceilingz)+normal*0.5,false);
|
|
atz = pos.z-BlockingCeiling.GetPlaneTexZ(1);
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else if ( normal dot (0,0,-1) > 0.7 )
|
|
hittype = HIT_CEILING;
|
|
else hittype = HIT_FLOOR;
|
|
}
|
|
else if ( BlockingLine )
|
|
{
|
|
atline = BlockingLine;
|
|
normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
|
|
atside = 1;
|
|
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
|
{
|
|
atside = 0;
|
|
normal *= -1;
|
|
}
|
|
Vector3 orig = (BlockingLine.v1.p.x,BlockingLine.v1.p.y,0);
|
|
Vector3 onwall = pos-(normal dot (pos-orig))*normal;
|
|
SetOrigin(onwall+normal*0.5,false);
|
|
// attempt to guess line part (upper/mid/lower)
|
|
if ( !atline.sidedef[1] ) atpart = 0; // mid
|
|
else if ( atline.sidedef[atside?0:1].sector.ceilingplane.ZAtPoint(pos.xy) < pos.z ) atpart = 1; // upper
|
|
else if ( atline.sidedef[atside?0:1].sector.floorplane.ZAtPoint(pos.xy) > pos.z ) atpart = -1; // lower
|
|
else atpart = 0;
|
|
if ( atpart == 1 )
|
|
{
|
|
if ( atline.flags&Line.ML_DONTPEGTOP ) atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(1);
|
|
else atz = pos.z-atline.sidedef[atside?0:1].sector.GetPlaneTexZ(1);
|
|
}
|
|
else if ( atpart == -1 )
|
|
{
|
|
if ( atline.flags&Line.ML_DONTPEGBOTTOM ) atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(0);
|
|
else atz = pos.z-atline.sidedef[atside?0:1].sector.GetPlaneTexZ(0);
|
|
}
|
|
else if ( atline.flags&Line.ML_DONTPEGBOTTOM ) atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(0);
|
|
else atz = pos.z-atline.sidedef[atside].sector.GetPlaneTexZ(1);
|
|
angle = atan2(normal.y,normal.x);
|
|
pitch = 0;
|
|
roll = 180; // otherwise it slides upwards (UT changes roll like this too)
|
|
if ( waterlevel > 0 ) hittype = HIT_FLOOR;
|
|
else hittype = HIT_WALL;
|
|
}
|
|
else
|
|
{
|
|
SetStateLabel("XDeath");
|
|
return;
|
|
}
|
|
b = Spawn("BioHitbox",pos);
|
|
b.target = self;
|
|
A_PlaySound("ges/hit");
|
|
A_SprayDecal("BioSplat",-172);
|
|
int numpt = Min(100,int(Scale.x*10))+Random[GES](-5,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (normal+(FRandom[GES](-.8,.8),FRandom[GES](-.8,.8),FRandom[GES](-.8,.8))).unit()*FRandom[GES](3,6);
|
|
let s = Spawn("BioSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
}
|
|
action void A_DropDrip()
|
|
{
|
|
let d = Spawn("BioSplash",pos+invoker.normal*2*scale.x);
|
|
d.target = target;
|
|
d.angle = angle;
|
|
d.pitch = pitch;
|
|
d.roll = roll;
|
|
d.master = self;
|
|
d.scale = scale*0.5;
|
|
d.vel.z -= 6;
|
|
}
|
|
action void A_GelExplode()
|
|
{
|
|
A_NoGravity();
|
|
A_Stop();
|
|
if ( invoker.l ) invoker.l.Destroy();
|
|
if ( invoker.b ) invoker.b.Destroy();
|
|
let s = Spawn("BioXLight",pos);
|
|
s.args[3] = int(s.args[3]*Scale.x);
|
|
invoker.deadtimer = -2;
|
|
if ( invoker.atline ) invoker.atline.RemoteActivate(target,invoker.atside,SPAC_Impact,pos);
|
|
UTMainHandler.DoBlast(self,Min(175,int(Scale.x*50)),20000*Scale.x);
|
|
A_Explode(int(20*Scale.x),Min(175,int(Scale.x*50)));
|
|
A_PlaySound("ges/explode",CHAN_VOICE);
|
|
int numpt = Min(300,int(Scale.x*30))+Random[GES](-10,10);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[GES](-1,1),FRandom[GES](-1,1),FRandom[GES](-1,1)).unit()*FRandom[GES](3,12);
|
|
let s = Spawn("BioSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Min(100,int(Scale.x*10))+Random[GES](-4,4);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[GES](-1,1),FRandom[GES](-1,1),FRandom[GES](-1,1)).unit()*FRandom[GES](1.2,2.4);
|
|
let s = Spawn("UTSmoke",pos+invoker.normal*4);
|
|
s.vel = pvel;
|
|
s.scale *= 2;
|
|
s.A_SetRenderStyle(0.5,STYLE_AddShaded);
|
|
if ( Random[GES](0,1) ) s.SetShade("40FF60");
|
|
else s.SetShade("60FF40");
|
|
}
|
|
Scale *= 0.5;
|
|
}
|
|
Default
|
|
{
|
|
Obituary "$O_BIORIFLE";
|
|
DamageType 'Slime';
|
|
RenderStyle "Add";
|
|
Radius 3;
|
|
Height 3;
|
|
Scale 2;
|
|
Speed 11;
|
|
Gravity 0.25;
|
|
PROJECTILE;
|
|
-NOGRAVITY;
|
|
+SKYEXPLODE;
|
|
+EXPLODEONWATER;
|
|
+FORCERADIUSDMG;
|
|
+FORCEXYBILLBOARD;
|
|
+MOVEWITHSECTOR;
|
|
+NODAMAGETHRUST;
|
|
+HITTRACER;
|
|
+INTERPOLATEANGLES;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
GELF ABCDEFGHIJKLM 1 Bright;
|
|
Loop;
|
|
Death:
|
|
GELH A 1 Bright AlignSelf();
|
|
GELH BCDEFGHIJ 1 Bright;
|
|
GELH J 1 Bright A_SetTics(Random[GES](10,30));
|
|
GELH J -1 Bright
|
|
{
|
|
invoker.deadtimer = Random[GES](250,300);
|
|
if ( invoker.hittype == HIT_WALL ) return ResolveState("Slide");
|
|
else if ( invoker.hittype == HIT_CEILING ) return ResolveState("Drip");
|
|
return ResolveState(null);
|
|
}
|
|
Stop;
|
|
Drip:
|
|
GELD ABCDEFGH 4 Bright;
|
|
GELD I 4 Bright A_DropDrip();
|
|
GELD JKLM 4 Bright;
|
|
GELH J -1 Bright;
|
|
Stop;
|
|
Slide:
|
|
GELS ABCDEF 3 Bright;
|
|
GELS G -1 Bright;
|
|
Stop;
|
|
Crash:
|
|
XDeath:
|
|
TNT1 A 0 A_JumpIf(invoker.HitType==HIT_NONE,"Explode");
|
|
TNT1 A 0 A_JumpIf(invoker.HitType==HIT_WALL,2);
|
|
GELH J 1 Bright A_SetTics(Random[GES](3,10));
|
|
Goto Explode;
|
|
GELS G 1 Bright A_SetTics(Random[GES](3,10));
|
|
Goto Explode;
|
|
Explode:
|
|
TNT1 A 0 A_Jump(255,"Explode1","Explode2","Explode3");
|
|
Explode1:
|
|
GEX1 A 1 Bright A_GelExplode();
|
|
GEX1 BCDEFGHIJK 3 Bright;
|
|
Stop;
|
|
Explode2:
|
|
GEX2 A 1 Bright A_GelExplode();
|
|
GEX2 BCDEFGHIJK 3 Bright;
|
|
Stop;
|
|
Explode3:
|
|
GEX3 A 1 Bright A_GelExplode();
|
|
GEX3 BCDEFGHIJK 3 Bright;
|
|
Stop;
|
|
Shrivel:
|
|
GELX ABCDEFGHIJKL 1 Bright; // UNUSED
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class BioGlob : BioGel
|
|
{
|
|
int numsplash;
|
|
|
|
override void AlignSelf()
|
|
{
|
|
Super.AlignSelf();
|
|
if ( !bAMBUSH && (Scale.x > 1) ) numsplash = int(2*Scale.x)-1;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
Vector3 ofs = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
for ( int i=0; i<2; i++ )
|
|
{
|
|
if ( numsplash-- <= 0 ) return;
|
|
Vector3 dir = (ofs+(FRandom[GES](-.8,.8),FRandom[GES](-.8,.8),FRandom[GES](-.8,.8))).unit();
|
|
A_SetScale(scale.x-0.15);
|
|
let d = Spawn("BioSplash",pos+ofs*4);
|
|
d.target = target;
|
|
d.master = self;
|
|
d.scale *= FRandom[GES](0.5,0.7);
|
|
d.angle = atan2(dir.y,dir.x);
|
|
d.pitch = -asin(dir.z);
|
|
d.vel = (cos(d.angle)*cos(d.pitch),sin(d.angle)*cos(d.pitch),-sin(d.pitch))*d.speed*FRandom[GES](0.4,0.6);
|
|
d.vel.z -= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
Class BioSplash : BioGel
|
|
{
|
|
override void AlignSelf()
|
|
{
|
|
Super.AlignSelf();
|
|
if ( hittype == HIT_CEILING ) hittype = HIT_FLOOR;
|
|
}
|
|
}
|
|
|
|
Class BioRifle : UTWeapon
|
|
{
|
|
double charge;
|
|
bool bCharging;
|
|
|
|
action void A_BioFire( bool alt = false )
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
if ( !alt )
|
|
{
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
|
|
}
|
|
A_PlaySound("ges/fire",CHAN_WEAPON);
|
|
invoker.FireEffect();
|
|
UTMainHandler.DoFlash(self,Color(48,0,255,0),1);
|
|
A_AlertMonsters();
|
|
if ( alt ) A_QuakeEx(1+int(0.5*invoker.charge),1+int(0.5*invoker.charge),1+int(0.5*invoker.charge),5+int(1.2*invoker.charge),0,64,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05+0.01*invoker.charge);
|
|
else A_QuakeEx(1,1,1,5,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05);
|
|
Vector3 x, y, z;
|
|
double a, s;
|
|
[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
|
|
Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+8.0*y-5.0*z;
|
|
Actor p;
|
|
if ( alt )
|
|
{
|
|
p = Spawn("BioGlob",origin);
|
|
p.A_SetScale(1.0+invoker.charge*0.8);
|
|
UTMainHandler.DoSwing(self,(FRandom[GES](-0.6,-1.3),FRandom[GES](-0.9,-0.2)),1+invoker.charge*0.3,-0.1,3,SWING_Spring,3,2);
|
|
}
|
|
else
|
|
{
|
|
p = Spawn("BioGel",origin);
|
|
UTMainHandler.DoSwing(self,(FRandom[GES](-0.6,-1.3),FRandom[GES](-0.9,-0.2)),2,-0.5,2,SWING_Spring,2,2);
|
|
}
|
|
p.angle = angle;
|
|
p.pitch = BulletSlope();
|
|
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
|
|
p.target = self;
|
|
for ( int i=0; i<12; i++ )
|
|
{
|
|
let s = Spawn("UTViewSmoke",origin);
|
|
UTViewSmoke(s).ofs = (10,4,-3);
|
|
s.scale *= 2.0;
|
|
s.target = self;
|
|
if ( Random[GES](0,1) ) s.SetShade("40FF60");
|
|
else s.SetShade("60FF40");
|
|
s.A_SetRenderStyle(0.5,STYLE_AddShaded);
|
|
UTViewSmoke(s).vvel += (FRandom[GES](0.8,1.6),FRandom[GES](-0.5,0.5),FRandom[GES](-0.5,0.5));
|
|
}
|
|
invoker.charge = 0;
|
|
}
|
|
action void A_BeginCharge()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
invoker.charge = 0;
|
|
A_PlaySound("ges/charge",CHAN_WEAPON);
|
|
invoker.bCharging = true;
|
|
}
|
|
action void A_ChargeUp()
|
|
{
|
|
Weapon weap = Weapon(invoker);
|
|
if ( !weap ) return;
|
|
UTMainHandler.DoSwing(self,(FRandom[GES](-1,1),FRandom[GES](-1,1)),0.02*invoker.charge,0,5,SWING_Spring,0,2);
|
|
if ( invoker.charge >= 5.1 ) return;
|
|
if ( weap.Ammo1.Amount <= 0 ) return;
|
|
if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
|
|
invoker.charge = min(5.1,invoker.charge+0.5);
|
|
}
|
|
override bool CheckAmmo( int fireMode, bool autoSwitch, bool requireAmmo, int ammocount )
|
|
{
|
|
if ( bCharging ) return true;
|
|
return Super.CheckAmmo(fireMode,autoSwitch,requireAmmo,ammocount);
|
|
}
|
|
Default
|
|
{
|
|
Tag "$T_BIORIFLE";
|
|
Inventory.PickupMessage "$I_BIORIFLE";
|
|
Weapon.UpSound "ges/select";
|
|
Weapon.SlotNumber 3;
|
|
Weapon.SelectionOrder 7;
|
|
Weapon.AmmoType "BioAmmo";
|
|
Weapon.AmmoUse 1;
|
|
Weapon.AmmoType2 "BioAmmo";
|
|
Weapon.AmmoUse2 1;
|
|
Weapon.AmmoGive 25;
|
|
UTWeapon.DropAmmo 5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BIOP A -1;
|
|
Stop;
|
|
BIOP B -1;
|
|
Stop;
|
|
Ready:
|
|
BIOS ABCDEFGHIJKLMNOPQRSTUV 1 A_WeaponReady(WRF_NOFIRE);
|
|
Idle:
|
|
BIOI A 1
|
|
{
|
|
invoker.charge = 0;
|
|
invoker.bCharging = false;
|
|
A_CheckReload();
|
|
A_WeaponReady();
|
|
}
|
|
Goto Idle;
|
|
Fire:
|
|
BIOF A 1 A_BioFire();
|
|
BIOF BCDEFGHI 1;
|
|
Goto Idle;
|
|
AltCheck:
|
|
TNT1 A 1;
|
|
TNT1 A 0 A_JumpIf(player.cmd.buttons&BT_ALTATTACK,"AltCheck");
|
|
TNT1 A 0 A_Overlay(PSP_WEAPON,"AltRelease");
|
|
TNT1 A 1;
|
|
Wait;
|
|
AltFire:
|
|
BIOC A 0 A_Overlay(-9999,"AltCheck");
|
|
BIOC A 4 A_BeginCharge();
|
|
BIOC B 5 A_ChargeUp();
|
|
BIOC CD 5;
|
|
BIOC E 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC E 5 A_ChargeUp();
|
|
BIOC FG 5;
|
|
BIOC H 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC H 5 A_ChargeUp();
|
|
BIOC IJ 5;
|
|
BIOC K 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC K 5 A_ChargeUp();
|
|
BIOC LM 5;
|
|
BIOC N 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC N 5 A_ChargeUp();
|
|
BIOC OP 5;
|
|
BIOC Q 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC Q 5 A_ChargeUp();
|
|
BIOC RS 5;
|
|
BIOC T 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC T 5 A_ChargeUp();
|
|
BIOC UV 5;
|
|
BIOC W 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC W 5 A_ChargeUp();
|
|
BIOC XY 5;
|
|
BIOC Z 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIOC Z 5 A_ChargeUp();
|
|
BIC2 AB 5;
|
|
BIC2 C 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
BIC2 C 5 A_ChargeUp();
|
|
BIC2 DE 5;
|
|
BIOM A 0 A_Refire(1);
|
|
Goto AltRelease;
|
|
AltHeld:
|
|
BIOM A 1;
|
|
BIOM A 0 A_Refire("AltHeld");
|
|
AltRelease:
|
|
BIOE A 1
|
|
{
|
|
invoker.charge = min(5.1,invoker.charge+0.1);
|
|
A_Overlay(-9999,null);
|
|
A_WeaponOffset(0,32); // fix sudden psprite lowering
|
|
if ( self is 'UTPlayer' )
|
|
UTPlayer(self).PlayAttacking3();
|
|
}
|
|
BIOF A 2 A_BioFire(true);
|
|
BIOF BCDEFGHI 2;
|
|
Goto Idle;
|
|
Select:
|
|
BIOS A 1 A_Raise(int.max);
|
|
Wait;
|
|
Deselect:
|
|
BIOD ABCDEFGHIJ 1;
|
|
BIOD J 1 A_Lower(int.max);
|
|
Wait;
|
|
}
|
|
}
|