swwmgz_m/zscript/weapons/swwm_sparkyboi_fx.zsc

1983 lines
53 KiB
Text

// Biospark Carbine projectiles and effects
Class BiosparkExplLight : PaletteLight
{
Default
{
Tag "SparkExpl";
}
}
Class BiosparkExplLight2 : PaletteLight
{
Default
{
Tag "SparkExpl";
Args 0,0,0,500;
ReactionTime 50;
}
}
Class SparkArcLight : PaletteLight
{
Default
{
Tag "SparkExpl,2";
Args 0,0,0,60;
ReactionTime 20;
}
}
Class SparkArcLightSmall : PaletteLight
{
Default
{
Tag "SparkExpl,2";
Args 0,0,0,30;
ReactionTime 10;
}
}
Class SparkArcLightBig : PaletteLight
{
Default
{
Tag "SparkExpl,2";
Args 0,0,0,90;
ReactionTime 40;
}
}
Class SparkBeamLight : PaletteLight
{
Default
{
Tag "SparkExpl";
ReactionTime 20;
Args 0,0,0,250;
}
}
Class BiosparkHitbox : Actor
{
Default
{
Radius 8;
Height 16;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
override void Tick()
{
if ( !target || !target.InStateSequence(target.CurState,target.FindState("Spawn")) )
{
Destroy();
return;
}
SetOrigin(target.Vec3Offset(0,0,-height*0.5),true);
}
override bool CanCollideWith( Actor other, bool passive )
{
return false;
}
}
Class BigBiosparkHitbox : BiosparkHitbox
{
Default
{
Radius 40;
Height 80;
}
}
Class BigOrbiter : Actor
{
double anglevel, pitchvel;
Default
{
RenderStyle "Add";
Scale 3.;
Radius .1;
Height 0.;
+NOINTERACTION;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
if ( !target || target.InStateSequence(target.CurState,target.FindState("Death")) )
{
Destroy();
return;
}
angle = FRandom[Sparkster](0,360);
pitch = FRandom[Sparkster](0,360);
anglevel = FRandom[Sparkster](5,10);
pitchvel = FRandom[Sparkster](5,10);
speed = FRandom[Sparkster](20,40);
Vector3 x = swwm_CoordUtil.GetAxes(pitch,angle,0);
SetOrigin(level.Vec3Offset(target.pos,x*speed+(0,0,target.height/2)),false);
SetState(FindState("Spawn")+Random[Sparkster](0,9));
}
override void Tick()
{
if ( !target || target.InStateSequence(target.CurState,target.FindState("Death")) )
{
Destroy();
return;
}
if ( isFrozen() ) return;
angle += anglevel;
pitch += pitchvel;
Vector3 x = swwm_CoordUtil.GetAxes(pitch,angle,0);
SetOrigin(level.Vec3Offset(target.pos,x*speed+(0,0,target.height/2)),true);
if ( tics > 0 ) tics--;
if ( !SetState(CurState.NextState) )
return;
}
States
{
Spawn:
BSPK ABCDEFGHIJ 1 Bright;
Loop;
}
}
Class BigBiospark : Actor
{
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("biospark/bigspark",CHAN_VOICE,CHANF_LOOP|CHANF_OVERLAP,1.,.6);
A_StartSound("biospark/bigspark",CHAN_VOICE,CHANF_LOOP|CHANF_OVERLAP,1.,.6);
let h = Spawn("BigBiosparkHitbox",pos);
h.target = self;
if ( special1 <= 0 ) special1 = 4;
for ( int i=0; i<special1*2; i++ )
{
let o = Spawn("BigOrbiter",pos);
o.target = self;
}
health = 300*special1;
}
override void OnDestroy()
{
A_StopSound(CHAN_VOICE);
Super.OnDestroy();
}
void A_BigsparkTick()
{
if ( !(special2%4) )
A_QuakeEx(Random[Sparkster](1,3),Random[Sparkster](1,3),Random[Sparkster](1,3),Random[Sparkster](4,6),0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:FRandom[Sparkster](.1,.3));
special2++;
int numpt = Random[ExploS](8,12);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = FRandom[ExploS](20,40);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.01,.02);
s.scale *= FRandom[ExploS](.3,1.2);
s.specialf1 = FRandom[ExploS](.9,.97);
s.specialf2 = FRandom[ExploS](.02,.06);
s.roll = FRandom[ExploS](0,360);
}
numpt = Random[ExploS](0,2);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = FRandom[ExploS](40,80);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn((!(special2%8)&&!Random[Sparkster](0,10))?"BiosparkArcBig":!Random[Sparkster](0,5)?"BiosparkArc":"BiosparkArcSmall",spos);
s.target = target;
s.master = self;
s.ReactionTime += Random[Sparkster](-6,6);
s.angle = ang;
s.pitch = pt;
}
// push away from nearby geometry
for ( int i=0; i<8; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
FLineTraceData d;
if ( !LineTrace(ang,40,pt,TRF_THRUACTORS|TRF_THRUHITSCAN,data:d) ) continue;
double mag = (40.-d.Distance);
vel -= (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*.02*mag;
}
double magvel = vel.length();
Vector3 dir = vel.unit();
if ( magvel <= double.epsilon ) dir = (cos(angle)*cos(pitch),sin(angle)+cos(pitch),-sin(pitch));
// wander
dir = (dir+(FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1))*FRandom[Sparkster](.05,.1)).unit();
// seek targets
if ( !(special2%5) )
{
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,8000);
while ( bt.Next() )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3D(t)) > 8000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2)).unit();
if ( dist > closest ) continue;
closest = dist;
tracer = t;
}
}
if ( tracer && (tracer.Health > 0) && CheckSight(tracer,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
{
Vector3 dirto = level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2));
double dist = dirto.length();
if ( dist > 4 )
{
dirto /= dist;
dir = (dir+.2*dirto*(clamp(1.-(dist/8000.),0.,1.)**3.)).unit();
}
magvel *= 1.+.1*((dist/8000.)**.3);
}
magvel *= .985;
if ( magvel > 10. ) magvel = 10.;
if ( magvel < 2. ) magvel = 2.;
vel = magvel*dir;
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
// deal (proper) radius damage
let bt2 = BlockThingsIterator.Create(self,500);
while ( bt2.Next() )
{
let t = bt2.Thing;
if ( !t || !t.bSHOOTABLE ) continue;
if ( SWWMUtility.SphereIntersect(t,pos,40) )
{
t.DamageMobj(self,target,4+special1,'Plasma');
if ( t.bISMONSTER && !Random[Sparkster](0,3) )
t.Howl();
Health--;
}
if ( SWWMUtility.SphereIntersect(t,pos,120) )
{
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2));
double dist = dirto.length();
dirto /= dist;
if ( dist > 4. ) SWWMUtility.DoKnockback(t,-dirto,clamp(120.-dist,0.,120.)*100);
}
}
Health -= 3;
if ( bMISSILEMORE ) Health--;
if ( Health <= 0 ) ExplodeMissile();
}
void A_BigSparkExplode()
{
A_StopSound(CHAN_VOICE);
A_AlertMonsters(swwm_uncapalert?0:15000);
SWWMUtility.DoExplosion(self,250,90000,300,100,flags:DE_HOWL);
A_QuakeEx(9,9,9,30,0,1400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollIntensity:1.5);
A_StartSound("biospark/bighit",CHAN_ITEM,attenuation:.4);
A_StartSound("biospark/bighit",CHAN_WEAPON,attenuation:.3);
A_SprayDecal("ShockMarkBig",172);
Scale *= FRandom[ExploS](3.8,4.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](40,60);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,20);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](2,5);
s.scale *= 4.;
s.alpha *= .6;
}
numpt = Random[ExploS](15,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,20);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](30,40);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](4,32);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](3,4);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArcBig",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,6);
}
Spawn("BiosparkExplLight2",pos);
special1 = 0;
}
void A_BigSparkSubExpl()
{
special1++;
if ( special1 > 40 ) return;
double factor = (40-special1)/40.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,100*factor,0.,250*invfct,flags:DE_HOWL);
SWWMUtility.DoExplosion(self,0,-9000*factor,500*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,16)*factor;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(4,5,2)*int(Random[ExploS](40,50)*factor));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 8.*factor;
s.alpha *= factor;
s.bAMBUSH = true;
}
numpt = int(Random[ExploS](8,12));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = (FRandom[ExploS](5,10)+120*invfct);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.03,.08);
s.scale *= FRandom[ExploS](.8,1.2)*factor;
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
if ( special1%2 ) return;
numpt = int(Random[ExploS](0,6)*factor);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArc",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,int(6*factor));
}
}
Default
{
Obituary "$O_SPARKSTER";
DamageType "Plasma";
RenderStyle "Add";
Health 1200;
Radius 2;
Height 4;
Speed 1;
BounceFactor 1.;
WallBounceFactor 1.;
PROJECTILE;
+THRUACTORS;
+DONTSPLASH;
+NOTELEPORT;
+FOILINVUL;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+FORCEXYBILLBOARD;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+CANBOUNCEWATER;
}
States
{
Spawn:
HSPK AABBCCDDEEFFGGHHIIJJ 1 Bright A_BigsparkTick();
Loop;
Death:
TNT1 A 0 A_BigSparkExplode();
XEX2 AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSS 1 Bright A_BigSparkSubExpl();
TNT1 A 1
{
A_BigSparkSubExpl();
A_FadeOut(.02);
}
Wait;
}
}
Class BiosparkBall : Actor
{
int deto;
Default
{
Obituary "$O_SPARKSTER";
DamageType "Plasma";
RenderStyle "Add";
Radius 2;
Height 4;
Speed 20;
PROJECTILE;
+FOILINVUL;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+EXPLODEONWATER;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("biospark/spark",CHAN_VOICE,CHANF_LOOP);
let h = Spawn("BiosparkHitbox",pos);
h.target = self;
if ( bMISSILEMORE ) A_SparkTick(); // potential for arcs to hit shooter if overloaded
}
void A_SparkTick()
{
special2++;
int numpt = Random[ExploS](3,6);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = FRandom[ExploS](5,10);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.01,.02);
s.scale *= FRandom[ExploS](.3,.5);
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
numpt = Random[ExploS](0,2);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = FRandom[ExploS](5,10);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn(!Random[Sparkster](0,10)?"BiosparkArc":"BiosparkArcSmall",spos);
s.target = target;
s.master = self;
s.ReactionTime += Random[Sparkster](0,3);
s.angle = ang;
s.pitch = pt;
}
double magvel = vel.length();
magvel *= 1.03;
if ( magvel > 50. ) magvel = 50.;
Vector3 dir = vel.unit();
if ( bMISSILEMORE )
dir = (dir+(FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1))*FRandom[Sparkster](.006,.012)).unit();
// check targets at an interval, to save on performance
if ( !(special2%5) )
{
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,500);
while ( bt.Next() )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3D(t)) > 500) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2)).unit();
if ( dir dot dirto < .5 ) continue; // don't seek stuff that's behind us
if ( dist > closest ) continue;
closest = dist;
tracer = t;
}
}
if ( tracer && (tracer.Health > 0) && CheckSight(tracer,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
{
Vector3 dirto = level.Vec3Diff(pos,tracer.Vec3Offset(0,0,tracer.height/2));
double dist = dirto.length();
if ( dist > 4 )
{
dirto /= dist;
dir = (dir+.02*dirto*(clamp(1.-(dist/500.),0.,1.)**3.)).unit();
}
}
vel = magvel*dir;
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
if ( deto > 1 )
{
ExplodeMissile();
return;
}
// proximity check
let bt = BlockThingsIterator.Create(self,100);
while ( bt.Next() )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),16) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
deto++;
break;
}
}
override void OnDestroy()
{
A_StopSound(CHAN_VOICE);
Super.OnDestroy();
}
void A_SparkExplode()
{
A_StopSound(CHAN_VOICE);
A_AlertMonsters(swwm_uncapalert?0:5000);
SWWMUtility.DoExplosion(self,50,20000,150,80,flags:DE_HOWL);
A_QuakeEx(6,6,6,16,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.8);
A_StartSound("biospark/hit",CHAN_ITEM,attenuation:.8);
A_StartSound("biospark/hit",CHAN_WEAPON,attenuation:.6);
A_SprayDecal("ShockMark",172);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](16,32);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,12);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](1,4);
s.scale *= 2.8;
s.alpha *= .4;
}
numpt = Random[ExploS](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArc",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,4);
}
Spawn("BiosparkExplLight",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,30,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
void A_SparkSubExpl()
{
special1++;
if ( special1 > 30 ) return;
double factor = (30-special1)/30.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,15*factor,0.,150*invfct,flags:DE_HOWL);
SWWMUtility.DoExplosion(self,0,-5000*factor,300*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8)*factor;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(4,5,2)*int(Random[ExploS](40,50)*factor));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 4.*factor;
s.alpha *= factor;
s.bAMBUSH = true;
}
numpt = int(Random[ExploS](4,8));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = (FRandom[ExploS](5,10)+60*invfct);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.03,.08);
s.scale *= FRandom[ExploS](.8,1.2)*factor;
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
if ( special1%2 ) return;
numpt = int(Random[ExploS](0,5)*factor);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArc",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,int(3*factor));
}
}
States
{
Spawn:
BSPK ABCDEFGHIJ 1 Bright A_SparkTick();
Loop;
Death:
TNT1 A 0 A_SparkExplode();
XEX2 ABCDEFGHIJKLMNOPQRS 1 Bright A_SparkSubExpl();
TNT1 A 1
{
A_SparkSubExpl();
A_FadeOut(.05);
}
Wait;
}
}
Class BiosparkTracer : LineTracer
{
Actor ignoreme;
Array<HitListEntry> hitlist;
Array<Line> ShootThroughList;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE || (Results.HitActor is 'BiosparkHitbox') )
{
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
hitlist.Push(ent);
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class BiosparkBeamImpact : Actor
{
Default
{
Obituary "$O_SPARKSTER";
DamageType "Plasma";
RenderStyle "Add";
Radius .1;
Height 0;
+FOILINVUL;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+FORCEXYBILLBOARD;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_AlertMonsters(swwm_uncapalert?0:2000);
SWWMUtility.DoExplosion(self,40,20000,100,40,flags:DE_HOWL);
A_QuakeEx(3,3,3,12,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.4);
A_StartSound("biospark/beamhit",CHAN_ITEM,attenuation:1.1);
A_StartSound("biospark/beamhit",CHAN_WEAPON,attenuation:.8);
A_SprayDecal("ShockMark",-172);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](8,16);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,12);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](1,4);
s.scale *= 2.;
s.alpha *= .4;
}
numpt = Random[ExploS](5,10);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](10,15);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArcSmall",pos);
s.target = target;
s.master = self;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,4);
}
Spawn("BiosparkExplLight",pos);
}
void A_SparkSubExpl()
{
special1++;
if ( special1 > 30 ) return;
double factor = (30-special1)/30.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,10*factor,0.,50*invfct,flags:DE_HOWL);
SWWMUtility.DoExplosion(self,0.,-5000*factor,100*invfct);
int numpt = int(Random[ExploS](8,16)*factor);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8)*factor;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(4,5,2)*int(Random[ExploS](40,50)*factor));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 4.*factor;
s.alpha *= factor;
s.bAMBUSH = true;
}
numpt = int(Random[ExploS](2,4));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = (FRandom[ExploS](5,10)+60*invfct);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.03,.08);
s.scale *= FRandom[ExploS](.8,1.2)*factor;
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
if ( special1%2 ) return;
numpt = int(Random[ExploS](0,5)*factor);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArcSmall",pos);
s.target = target;
s.master = self;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,int(3*factor));
}
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX2 ABCDEFGHIJKLMNPQRS 1 Bright A_SparkSubExpl();
TNT1 A 1
{
A_SparkSubExpl();
A_FadeOut(.05);
}
Wait;
}
}
Class BiosparkComboImpactSub : Actor
{
Default
{
Obituary "$O_SPARKSTER";
DamageType "Plasma";
RenderStyle "Add";
Scale 1.4;
Alpha .4;
Radius .1;
Height 0;
+FOILINVUL;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+FORCEXYBILLBOARD;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !bAMBUSH ) return;
SWWMUtility.DoExplosion(self,40,10000,300,120,flags:DE_HOWL);
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX2 ABCDEFGHIJKLMNOPQRS 1 Bright;
Stop;
}
}
Class BiosparkComboImpact : Actor
{
Default
{
Obituary "$O_SPARKSTER";
DamageType "Plasma";
RenderStyle "Add";
Scale 2.;
Radius .1;
Height 0;
+FOILINVUL;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+FORCEXYBILLBOARD;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
void FlashPlayer( int str, double rad )
{
if ( !SWWMUtility.InPlayerFOV(players[consoleplayer],self,rad) ) return;
let mo = players[consoleplayer].Camera;
double dist = Distance3D(mo);
str = min(int(str*(1.-(dist/rad))),255);
SWWMHandler.DoFlash(mo,Color(str,248,255,240),10);
SWWMHandler.DoFlash(mo,Color(str,248,255,240),20);
SWWMHandler.DoFlash(mo,Color(str/2,192,255,160),30);
SWWMHandler.DoFlash(mo,Color(str/2,192,255,160),50);
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_AlertMonsters(swwm_uncapalert?0:6000);
SWWMUtility.DoExplosion(self,bAMBUSH?(400+Args[0]*25):200,bAMBUSH?120000:60000,bAMBUSH?500:300,bAMBUSH?350:150,DE_HOWL);
A_QuakeEx(9,9,9,25,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:600,rollIntensity:1.5);
A_StartSound("biospark/bighit",CHAN_ITEM,attenuation:.4);
A_StartSound("biospark/bighit",CHAN_WEAPON,attenuation:.3);
A_SprayDecal("BigShockMark",172);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](16,32);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,24);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](2,5);
s.scale *= 3.;
s.alpha *= .4;
}
numpt = Random[ExploS](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,48);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](6,8);
if ( bAMBUSH ) numpt -= Args[0]/2;
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArcBig",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,5);
if ( bAMBUSH ) s.ReactionTime += 4+Args[0]*3;
}
if ( bAMBUSH )
{
for ( int i=0; i<(Args[0]/2); i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkBeam",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.bMISSILEMORE = true;
s.bMISSILEEVENMORE = true;
}
}
Spawn("BiosparkExplLight2",pos);
FlashPlayer(600,3000);
}
void A_SparkSubExpl()
{
double mx = bAMBUSH?40.:30.;
special1++;
if ( special1 > mx ) return;
double factor = (mx-special1)/mx;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,(bAMBUSH?(100+Args[0]*25):80)*factor,0.,(bAMBUSH?600.:400.)*invfct,flags:DE_HOWL);
SWWMUtility.DoExplosion(self,0.,-12000*factor,(bAMBUSH?800:600)*invfct);
int numpt = int(Random[ExploS](8,16)*factor);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,16)*factor;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(4,5,2)*int(Random[ExploS](40,50)*factor));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 4.*factor;
s.alpha *= factor;
s.bAMBUSH = true;
}
numpt = int(Random[ExploS](4,8));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = (FRandom[ExploS](5,20)+60*invfct);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.03,.08);
s.scale *= FRandom[ExploS](.8,1.2)*factor;
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
if ( special1%2 ) return;
numpt = Random[ExploS](2,5);
if ( bAMBUSH ) numpt -= 2;
numpt = int(numpt*factor);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn(bAMBUSH?"BiosparkArcBig":"BiosparkArc",pos);
s.target = target;
s.master = self;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,int(5*factor));
if ( bAMBUSH ) s.ReactionTime += 2+Args[0]*2;
}
}
void A_SparkSubExpl2()
{
special2++;
int mx = bAMBUSH?40:30;
if ( (special2 > mx) || !(special2%2) ) return;
int numpt = Random[Sparkster](0,mx-special2);
double ang, pt;
double mul = bAMBUSH?9:5;
for ( int i=0; i<numpt; i++ )
{
ang = FRandom[Sparkster](0,360);
pt = FRandom[Sparkster](-90,90);
FLineTraceData d;
Vector3 HitNormal;
LineTrace(ang,FRandom[Sparkster](10,20)+mul*special2,pt,TRF_THRUACTORS,data:d);
hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
else hitnormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
else hitnormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
let p = Spawn("BiosparkComboImpactSub",d.HitLocation+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = target;
p.scale *= 2-special2*.02;
p.alpha *= 1-special2*.02;
p.bAMBUSH = bAMBUSH;
}
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX2 ABCDEFGHIJKLMNOPQRS 1 Bright
{
A_SparkSubExpl();
A_SparkSubExpl2();
}
TNT1 A 1
{
A_SparkSubExpl();
A_SparkSubExpl2();
A_FadeOut(.05);
}
Wait;
}
}
Class BiosparkBeam : Actor
{
Vector3 nextpos, nextdir;
action void A_Trace()
{
let t = new("BiosparkTracer");
t.hitlist.Clear();
t.ignoreme = target;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
t.ShootThroughList.Clear();
t.Trace(pos,CurSector,x,speed,TRACE_HitSky);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
}
for ( int i=0; i<t.hitlist.Size(); i++ )
{
if ( (t.hitlist[i].hitactor is 'BiosparkHitbox') && t.hitlist[i].hitactor.target )
{
let s = Spawn("BiosparkComboImpact",t.hitlist[i].hitactor.pos);
s.target = target;
s.angle = atan2(t.hitlist[i].x.y,t.hitlist[i].x.x);
s.pitch = asin(-t.hitlist[i].x.z);
if ( (t.hitlist[i].hitactor is 'BigBiosparkHitbox') )
{
s.bAMBUSH = true;
s.Args[0] = t.hitlist[i].hitactor.target.special1;
}
t.hitlist[i].hitactor.target.Destroy();
if ( target ) SWWMUtility.AchievementProgressInc('swwm_progress_shock',1,target.player);
}
else
{
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,15000);
t.hitlist[i].hitactor.DamageMobj(self,target,GetMissileDamage(0,0),'Plasma',DMG_THRUSTLESS);
if ( t.hitlist[i].hitactor && t.hitlist[i].hitactor.bISMONSTER && !Random[Sparkster](0,3) )
t.hitlist[i].hitactor.Howl();
}
}
Vector3 normal = -t.Results.HitVector, dir = t.Results.HitVector;
if ( t.Results.HitType == TRACE_HitWall )
{
normal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
if ( !t.Results.Side ) normal *= -1;
t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
else if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.ffloor ) normal = -t.Results.ffloor.top.Normal;
else normal = t.Results.HitSector.floorplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.ffloor ) normal = -t.Results.ffloor.bottom.Normal;
else normal = t.Results.HitSector.ceilingplane.Normal;
}
else
{
t.Results.HitPos = level.Vec3Offset(pos,x*speed);
normal *= 0;
}
invoker.nextpos = level.Vec3Offset(t.Results.HitPos,normal*8.);
if ( t.Results.HitType == TRACE_HasHitSky )
{
bAMBUSH = true;
speed = t.Results.Distance; // shortens in minimap
return;
}
if ( t.Results.HitType != TRACE_HitNone )
{
let s = Spawn("BiosparkBeamImpact",invoker.nextpos);
s.target = target;
s.angle = atan2(normal.y,normal.x);
s.pitch = asin(-normal.z);
bAMBUSH = true;
if ( swwm_omnibust ) BusterWall.Bust(t.Results,GetMissileDamage(0,0),target,t.Results.HitVector,t.Results.HitPos.z);
speed = t.Results.Distance; // shortens in minimap
return;
}
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,500);
while ( bt.Next() )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3D(t)) > 500) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(invoker.nextpos,t.Vec3Offset(0,0,t.height/2));
if ( dir dot dirto < .2 ) continue;
if ( dist > closest ) continue;
closest = dist;
tracer = t;
}
if ( tracer )
{
Vector3 dirto = level.Vec3Diff(invoker.nextpos,tracer.Vec3Offset(0,0,tracer.height/2));
double dist = dirto.length();
if ( dist > 10 )
{
dirto /= dist;
dir = (dir+.1*dirto*(clamp(1.-(dist/500.),0.,1.)**4.)).unit();
}
}
if ( bMISSILEEVENMORE )
dir = (dir+(FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1))*FRandom[Sparkster](.8,.16)).unit();
else if ( bMISSILEMORE )
dir = (dir+(FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1),FRandom[Sparkster](-1,1))*FRandom[Sparkster](.02,.04)).unit();
invoker.nextdir = dir;
}
action void A_Spread()
{
let c = Spawn("BiosparkChildBeam",pos);
c.angle = angle;
c.pitch = pitch;
if ( frame ) c.SetStateLabel("TrailSpawn");
c.roll = Random[Sparkster](0,7)*45;
Vector3 tdir = level.Vec3Diff(pos,invoker.nextpos);
int numpt = Random[Sparkster](-1,3);
for ( int i=0; i<numpt; i++ )
{
let a = Spawn("BiosparkArc",level.Vec3Offset(pos,tdir*FRandom[Sparkster]((frame||bMISSILEMORE)?0:.3,1)));
a.angle = angle;
a.pitch = pitch;
a.target = target;
a.ReactionTime += Random[Sparkster](-4,4);
}
numpt = Random[Sparkster](1,3);
for ( int i=0; i<numpt; i++ )
{
let a = Spawn("BiosparkArcSmall",level.Vec3Offset(pos,tdir*FRandom[Sparkster]((frame||bMISSILEMORE)?0:.3,1)));
a.angle = angle;
a.pitch = pitch;
a.target = target;
a.master = self;
a.ReactionTime += Random[Sparkster](-2,2);
}
numpt = Random[Sparkster](20,30);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*3;
let s = Spawn("BiosparkSpark",level.Vec3Offset(pos,tdir*FRandom[ExploS](0,1)+ofs));
s.vel = ofs*FRandom[ExploS](-.1,.2);
s.scale *= FRandom[ExploS](.5,.8);
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
if ( (special1%2) || bAMBUSH )
{
A_StartSound("biospark/zap",CHAN_WEAPON,0,.5,.6);
Spawn("SparkBeamLight",level.Vec3Offset(pos,tdir/2));
}
if ( bAMBUSH ) return;
let b = Spawn(GetClass(),invoker.nextpos);
b.frame = 1;
b.angle = atan2(invoker.nextdir.y,invoker.nextdir.x);
b.pitch = asin(-invoker.nextdir.z);
b.target = target;
b.special1 = special1+1;
b.bMISSILEMORE = bMISSILEMORE;
b.bMISSILEEVENMORE = bMISSILEEVENMORE;
}
action void A_Fade()
{
A_FadeOut(.05);
let t = new("BiosparkTracer");
t.hitlist.Clear();
t.ignoreme = target;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
t.ShootThroughList.Clear();
t.Trace(pos,CurSector,x,speed,0);
for ( int i=0; i<t.hitlist.Size(); i++ )
{
if ( (t.hitlist[i].hitactor is 'BiosparkHitbox') && t.hitlist[i].hitactor.target )
{
let s = Spawn("BiosparkComboImpact",t.hitlist[i].hitactor.pos);
s.target = target;
s.angle = atan2(t.hitlist[i].x.y,t.hitlist[i].x.x);
s.pitch = asin(-t.hitlist[i].x.z);
t.hitlist[i].hitactor.target.Destroy();
}
else
{
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,t.hitlist[i].x,15000);
t.hitlist[i].hitactor.DamageMobj(self,target,int(GetMissileDamage(0,0)*alpha),'Plasma',DMG_THRUSTLESS);
if ( t.hitlist[i].hitactor && t.hitlist[i].hitactor.bISMONSTER && !Random[Sparkster](0,3) )
t.hitlist[i].hitactor.Howl();
}
}
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
Default
{
Obituary "$O_SPARKSTER";
RenderStyle "Add";
DamageFunction 15;
Speed 256;
Radius .1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+INTERPOLATEANGLES;
+NOTELEPORT;
+FOILINVUL;
+NOINTERACTION;
}
States
{
Spawn:
XZW1 # 1;
XZW1 # 1 A_Trace();
XZW1 # 1 A_Spread();
XZW1 # 1 A_Fade();
Wait;
Dummy:
XZW1 AB -1;
Stop;
}
}
Class BiosparkChildBeam : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0;
Alpha .4;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+INTERPOLATEANGLES;
+NOTELEPORT;
+ROLLSPRITE;
+ROLLCENTER;
+NOINTERACTION;
}
override void Tick()
{
if ( isFrozen() ) return;
A_FadeOut(.02);
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XZW1 A -1 Bright NoDelay
{
return FindState("StarterDev")+Random[Sparkster](0,11)*2;
}
Stop;
TrailSpawn:
XZW2 A -1 Bright
{
return FindState("TrailerDev")+Random[Sparkster](0,11)*2;
}
Stop;
StarterDev:
#### # 20 Bright;
XZW1 B -1 Bright;
Stop;
#### # 20 Bright;
XZW1 C -1 Bright;
Stop;
#### # 20 Bright;
XZW1 D -1 Bright;
Stop;
#### # 20 Bright;
XZW1 E -1 Bright;
Stop;
#### # 20 Bright;
XZW1 F -1 Bright;
Stop;
#### # 20 Bright;
XZW1 G -1 Bright;
Stop;
#### # 20 Bright;
XZW1 H -1 Bright;
Stop;
#### # 20 Bright;
XZW1 I -1 Bright;
Stop;
#### # 20 Bright;
XZW1 J -1 Bright;
Stop;
#### # 20 Bright;
XZW1 K -1 Bright;
Stop;
#### # 20 Bright;
XZW1 L -1 Bright;
Stop;
#### # 20 Bright;
XZW1 M -1 Bright;
Stop;
TrailerDev:
#### # 20 Bright;
XZW2 B -1 Bright;
Stop;
#### # 20 Bright;
XZW2 C -1 Bright;
Stop;
#### # 20 Bright;
XZW2 D -1 Bright;
Stop;
#### # 20 Bright;
XZW2 E -1 Bright;
Stop;
#### # 20 Bright;
XZW2 F -1 Bright;
Stop;
#### # 20 Bright;
XZW2 G -1 Bright;
Stop;
#### # 20 Bright;
XZW2 H -1 Bright;
Stop;
#### # 20 Bright;
XZW2 I -1 Bright;
Stop;
#### # 20 Bright;
XZW2 J -1 Bright;
Stop;
#### # 20 Bright;
XZW2 K -1 Bright;
Stop;
#### # 20 Bright;
XZW2 L -1 Bright;
Stop;
#### # 20 Bright;
XZW2 M -1 Bright;
Stop;
}
}
Class BiosparkArc : Actor
{
Vector3 nextpos, nextdir;
action void A_Trace()
{
let t = new("CandyBeamTracer");
t.hitlist.Clear();
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
t.ShootThroughList.Clear();
t.Trace(pos,CurSector,x,speed,TRACE_HitSky);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
}
for ( int i=0; i<t.hitlist.Size(); i++ )
{
SWWMUtility.DoKnockback(t.hitlist[i].hitactor,-t.hitlist[i].x,GetMissileDamage(0,0)*1000);
t.hitlist[i].hitactor.DamageMobj(self,target,GetMissileDamage(0,0),'Plasma',DMG_THRUSTLESS);
if ( t.hitlist[i].hitactor && t.hitlist[i].hitactor.bISMONSTER && !Random[Sparkster](0,3) )
t.hitlist[i].hitactor.Howl();
}
Vector3 normal = -t.Results.HitVector, dir = t.Results.HitVector;
if ( t.Results.HitType == TRACE_HitWall )
{
normal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
if ( !t.Results.Side ) normal *= -1;
t.Results.HitLine.RemoteActivate(target,t.Results.Side,SPAC_Impact,t.Results.HitPos);
dir -= 2*normal*(dir dot normal);
}
else if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.ffloor ) normal = -t.Results.ffloor.top.Normal;
else normal = t.Results.HitSector.floorplane.Normal;
dir -= 2*normal*(dir dot normal);
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.ffloor ) normal = -t.Results.ffloor.bottom.Normal;
else normal = t.Results.HitSector.ceilingplane.Normal;
dir -= 2*normal*(dir dot normal);
}
else
{
t.Results.HitPos = level.Vec3Offset(pos,x*speed);
normal *= 0;
}
invoker.nextpos = level.Vec3Offset(t.Results.HitPos,normal);
if ( t.Results.HitType != TRACE_HitNone ) speed = t.Results.Distance; // shortens in minimap
if ( t.Results.HitType == TRACE_HasHitSky )
{
bAMBUSH = true;
speed = t.Results.Distance; // shortens in minimap
return;
}
double a = FRandom[Sparkster](0,360), s = FRandom[Sparkster](0.,.8);
dir = (dir+cos(a)*y*s+sin(a)*z*s).unit();
if ( master )
{
Vector3 dirto = level.Vec3Diff(invoker.nextpos,master.Vec3Offset(0,0,master.height/2));
double dist = dirto.length();
if ( dist > 10 )
{
dirto /= dist;
dir = (dir+.5*dirto*(clamp(1.-(dist/3000.),0.,1.)**1.5)).unit();
}
}
else
{
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,1500);
while ( bt.Next() )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3D(t)) > 1500) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(invoker.nextpos,t.Vec3Offset(0,0,t.height/2));
if ( dir dot dirto < .2 ) continue;
if ( dist > closest ) continue;
closest = dist;
tracer = t;
}
if ( tracer )
{
Vector3 dirto = level.Vec3Diff(invoker.nextpos,tracer.Vec3Offset(0,0,tracer.height/2));
double dist = dirto.length();
if ( dist > 10 )
{
dirto /= dist;
dir = (dir+.5*dirto*(clamp(1.-(dist/1500.),0.,1.)**2.)).unit();
}
}
}
invoker.nextdir = dir;
}
action void A_Spread( Sound arcsnd = "", double attn = 1., int extra = 0 )
{
Vector3 tdir = level.Vec3Diff(pos,invoker.nextpos);
if ( (extra > 1) && !Random[Sparkster](0,3) )
{
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
for ( int i=0; i<2; i++ )
{
let r = Spawn("BiosparkArc",level.Vec3Offset(pos,tdir*FRandom[Sparkster](0,1)));
double a = FRandom[Sparkster](0,360), s = FRandom[Sparkster](0.,1.);
Vector3 sdir = (x+y*cos(a)*s+z*sin(a)*s).unit();
r.angle = atan2(sdir.y,sdir.x);
r.pitch = asin(-sdir.z);
r.target = target;
r.master = master;
r.ReactionTime /= 3;
}
}
if ( (special1 > ReactionTime) || bAMBUSH ) return;
let b = Spawn(GetClass(),invoker.nextpos);
b.angle = atan2(invoker.nextdir.y,invoker.nextdir.x);
b.pitch = asin(-invoker.nextdir.z);
b.target = target;
b.master = master;
b.tracer = tracer;
b.special1 = special1+1;
b.special2 = special2;
if ( (arcsnd != "") && !((special1+special2)%3) && !Random[Sparkster](0,3-extra) ) A_StartSound(arcsnd,CHAN_WEAPON,attenuation:attn);
if ( !((special1+special2)%8) && !Random[Sparkster](0,5) )
{
let p = Spawn((extra>1)?"SparkArcLightBig":(extra>0)?"SparkArcLight":"SparkArcLightSmall",level.Vec3Offset(pos,tdir/2));
p.args[3] = int(speed*8);
}
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
frame = Random[Sparkster](0,11);
if ( !special1 ) special2 = Random[Sparkster](0,8);
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
Default
{
Obituary "$O_SPARKSTER";
RenderStyle "Add";
DamageFunction 6;
ReactionTime 10;
Speed 16;
Radius .1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+INTERPOLATEANGLES;
+NOTELEPORT;
+FOILINVUL;
+NOINTERACTION;
}
States
{
Spawn:
XZW1 # 0 Bright;
XZW1 # 1 Bright A_Trace();
XZW1 # 1 Bright A_Spread("biospark/bigarc",.6,1);
XZW1 # 1 Bright A_FadeOut(.05);
Wait;
}
}
Class BiosparkArcSmall : BiosparkArc
{
Default
{
ReactionTime 10;
DamageFunction 2;
Speed 8;
}
States
{
Spawn:
XZW1 # 0 Bright;
XZW1 # 1 Bright A_Trace();
XZW1 # 1 Bright A_Spread("biospark/arc",2.,0);
XZW1 # 1 Bright A_FadeOut(.1);
Wait;
}
}
Class BiosparkArcBig : BiosparkArc
{
Default
{
ReactionTime 10;
DamageFunction 18;
Speed 64;
}
States
{
Spawn:
XZW1 # 0 Bright;
XZW1 # 1 Bright A_Trace();
XZW1 # 1 Bright A_Spread("biospark/hugearc",.4,2);
XZW1 # 1 Bright A_FadeOut(.025);
Wait;
}
}
Class BiosparkSpark : Actor
{
Default
{
RenderStyle "Add";
Scale .8;
Radius .1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
+NOINTERACTION;
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
SSPK A 1 Bright
{
A_SetScale(scale.x*specialf1);
A_FadeOut(specialf2);
}
Wait;
}
}
Class BiosparkCore : Actor
{
Vector3 oldvel;
double anglevel, pitchvel, rollvel;
Default
{
Obituary "$O_SPARKSTER";
PROJECTILE;
BounceType "Hexen";
+CANBOUNCEWATER;
+USEBOUNCESTATE;
+DONTBOUNCEONSKY;
+NODAMAGETHRUST;
+NOTELEPORT;
+INTERPOLATEANGLES;
+FORCEXYBILLBOARD;
-NOGRAVITY;
Speed 30;
Gravity .1;
BounceFactor 1.;
WallBounceFactor 1.;
Radius 2;
Height 4;
ReactionTime 25;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
anglevel = FRandom[Sparkster](-8,8);
rollvel = FRandom[Sparkster](-8,8);
pitchvel = FRandom[Sparkster](-8,8);
}
override void Tick()
{
oldvel = vel;
Super.Tick();
}
void A_HandleBounce()
{
bHITOWNER = true;
Vector3 HitNormal = -vel.unit();
F3DFloor ff;
if ( BlockingFloor )
{
// find closest 3d floor for its normal
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
{
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
ff = BlockingFloor.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.top.Normal;
else HitNormal = BlockingFloor.floorplane.Normal;
}
else if ( BlockingCeiling )
{
// find closest 3d floor for its normal
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
{
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
ff = BlockingCeiling.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.bottom.Normal;
else HitNormal = BlockingCeiling.ceilingplane.Normal;
}
else if ( BlockingLine )
{
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
int wside = SWWMUtility.PointOnLineSide(pos.xy,BlockingLine);
if ( !wside ) HitNormal *= -1;
}
else if ( BlockingMobj )
{
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
}
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
vel = (vel dot HitNormal)*HitNormal*FRandom[Spreadgun](-1.8,-1.)+vel;
vel += (FRandom[Spreadgun](-.4,.4),FRandom[Spreadgun](-.4,.4),FRandom[Spreadgun](-.4,.4));
vel *= .6;
anglevel = FRandom[Sparkster](-16,16);
rollvel = FRandom[Sparkster](-16,16);
pitchvel = FRandom[Sparkster](-16,16);
// slam jam
A_StartSound("hellblazer/bounce",CHAN_VOICE,CHANF_OVERLAP,max(0.,(vel.length()/30.-.2))**.5);
if ( (vel.length() < 5) && (pos.z <= floorz) )
{
ClearBounce();
ExplodeMissile();
}
}
void A_Deploy()
{
A_AlertMonsters(swwm_uncapalert?0:700);
A_QuakeEx(7,7,7,8,0,1400,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
A_StartSound("biospark/deploy",CHAN_ITEM,attenuation:.7);
A_StartSound("biospark/deploy",CHAN_WEAPON,attenuation:.5);
let s = Spawn("BigBiospark",pos);
s.target = target;
Vector3 dir = vel.unit();
s.angle = atan2(dir.y,dir.x);
s.pitch = asin(-dir.z);
s.special1 = special1;
s.bMISSILEMORE = bMISSILEMORE;
}
void A_SparkExplode()
{
bNOGRAVITY = true;
A_SetRenderStyle(1.,STYLE_Add);
A_AlertMonsters(swwm_uncapalert?0:5000);
SWWMUtility.DoExplosion(self,120,120000,150,60);
A_QuakeEx(6,6,6,16,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.8);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](16,32);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](3,12);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](1,4);
s.scale *= 2.8;
s.alpha *= .4;
}
numpt = Random[ExploS](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArc",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,5);
}
Spawn("BiosparkExplLight",pos);
}
void A_SparkSubExpl()
{
special2++;
if ( special2 > 30 ) return;
double factor = (30-special2)/30.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,0.,-5000*factor,300*invfct);
SWWMUtility.DoExplosion(self,40*factor,0.,150*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8)*factor;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(4,5,2)*int(Random[ExploS](40,50)*factor));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 4.*factor;
s.alpha *= factor;
s.bAMBUSH = true;
}
numpt = int(Random[ExploS](4,8));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = (FRandom[ExploS](5,10)+60*invfct);
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("BiosparkSpark",spos);
s.vel = ofs*FRandom[ExploS](-.03,.08);
s.scale *= FRandom[ExploS](.8,1.2)*factor;
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
if ( special2%2 ) return;
numpt = int(Random[ExploS](0,5)*factor);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn("BiosparkArc",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
s.ReactionTime += Random[Sparkster](0,int(5*factor));
}
}
States
{
Spawn:
XZW1 A 1
{
A_CountDown();
angle += invoker.anglevel;
pitch += invoker.pitchvel;
roll += invoker.rollvel;
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.1,.4);
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = pvel+vel*.1;
s.SetShade(Color(4,5,2)*Random[ExploS](40,50));
s.A_SetRenderStyle(s.alpha*.4,STYLE_AddShaded);
s.scale *= 2.;
s.bAMBUSH = true;
}
Wait;
Bounce:
XZW1 A 0 A_HandleBounce();
Goto Spawn;
Death:
TNT1 A 0 A_Deploy();
TNT1 A 0 A_SparkExplode();
XEX2 ABCDEFGHIJKLMNOPQRS 1 Bright A_SparkSubExpl();
TNT1 A 1
{
A_SparkSubExpl();
A_FadeOut(.05);
}
Wait;
}
}
Class SparksterMag : SWWMCasing
{
Default
{
Mass 10;
BounceFactor 0.4;
WallBounceFactor 0.4;
BounceSound "explodium/mag";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
States
{
Death:
XZW1 BC -1
{
pitch = 0;
angle = FRandom[Junk](0,360);
roll = 0;
frame = RandomPick[Junk](1,2);
}
Stop;
}
}