swwmgz_m/zscript/dlc1/swwm_mister_fx.zsc

1366 lines
33 KiB
Text

// Mortal Rifle projectiles and effects
Class MisterWeaponLight : SWWMWeaponLight
{
Default
{
args 64,224,255,150;
}
}
Class MisterCasing : SWWMCasing
{
Default
{
BounceSound "mister/casing";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
States
{
Death:
XZW1 BCDE -1
{
bINTERPOLATEANGLES = false;
pitch = roll = 0;
angle = FRandom[Junk](0,360);
frame = RandomPick[Junk](1,4);
}
Stop;
}
}
Class MisterGCasing : SWWMCasing
{
Default
{
Mass 8;
BounceFactor 0.5;
WallBounceFactor 0.5;
BounceSound "mister/gcasing";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
}
Class MisterMag : SWWMCasing
{
Default
{
Mass 10;
BounceFactor 0.4;
WallBounceFactor 0.4;
BounceSound "mister/mag";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
States
{
Death:
XZW1 BC -1
{
bINTERPOLATEANGLES = false;
pitch = roll = 0;
angle = FRandom[Junk](0,360);
frame = RandomPick[Junk](1,2);
}
Stop;
}
}
Class MisterRing : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
Scale 1.5;
+FORCEXYBILLBOARD;
}
States
{
Spawn:
XRG9 ABCDEFGHIJKLMNOPQRSTUVWX 1 Bright A_SetScale(scale.x*1.08);
Stop;
}
}
Class MisterExLight : PaletteLight
{
Default
{
Tag "Cyanblu";
ReactionTime 35;
Args 0,0,0,250;
}
}
Class MisterExLightBig : MisterExLight
{
Default
{
ReactionTime 45;
Args 0,0,0,500;
}
}
Class MisterExLightSmall : MisterExLight
{
Default
{
ReactionTime 25;
Args 0,0,0,120;
}
}
Class MisterExLightTiny : MisterExLight
{
Default
{
ReactionTime 15;
Args 0,0,0,80;
}
}
Class MisterBulletImpactPop : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
Scale 6.;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*.9,scale.y*.9);
A_FadeOut(.15);
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
BLPF C 5 NoDelay Bright
{
Scale *= FRandom[ExploS](0.6,1.8);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
roll = FRandom[Explos](0,360);
}
Stop;
}
}
Class MisterFuzzy : SWWMNonInteractiveActor
{
MisterRailCounter mrc;
Default
{
Obituary "$O_MORTALRIFLE";
DamageType "Mortal";
RenderStyle "Add";
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FOILINVUL;
}
override void PostBeginPlay()
{
special1 += Random[ExploS](4,10);
specialf1 = special1;
vel = SWWMUtility.Vec3FromAngles(angle,pitch)*FRandom[ExploS](8,24);
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,(special2<0)?4:44,3000,80,80,DE_EXTRAZTHRUST|DE_COUNTENEMIES);
if ( mrc ) mrc.nkill += nkill;
special1--;
if ( special1 <= 0 )
{
Destroy();
return;
}
Vector3 dir = vel;
double magvel = dir.length();
magvel *= 1.2;
if ( magvel > 0. )
{
dir /= magvel;
dir += .5*SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90));
dir = dir.unit();
vel = dir*magvel;
}
FLineTraceData d;
Vector3 newpos = pos;
newpos.z = clamp(newpos.z,floorz,ceilingz);
int nstep = 0;
double dist = magvel;
while ( dist > 0 )
{
// safeguard, too many bounces
if ( nstep > MAXBOUNCEPERTIC )
{
Destroy();
return;
}
Vector3 oldpos = newpos;
double ang = atan2(dir.y,dir.x);
double pt = asin(-dir.z);
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
if ( d.HitType != TRACE_HitNone )
{
Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d);
dist -= d.Distance;
// should only happen if we bounced
dir = d.HitDir-(FRandom[Puff](1.5,2.)*hitnormal*(d.HitDir dot hitnormal));
vel = dir*magvel;
newpos = d.HitLocation+dir;
}
else
{
dist = 0.;
newpos = level.Vec3Offset(newpos,dir*magvel);
}
Vector3 traildir = level.Vec3Diff(oldpos,newpos);
double len = traildir.length();
if ( len > 0. )
{
traildir /= len;
for ( double i=0.; i<len; i+=4. )
{
let p = Spawn("MisterFuzzyTrail",level.Vec3Offset(oldpos,traildir*i));
p.vel = dir*FRandom[ExploS](1.,4.);
p.scale *= special1/specialf1;
}
}
nstep++;
}
newpos.z = clamp(newpos.z,floorz,ceilingz);
SetOrigin(newpos,true);
}
}
Class MisterFuzzyTrail : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
Scale 1.5;
Alpha .25;
+FORCEXYBILLBOARD;
}
override void Tick()
{
prev = pos;
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( vel != (0,0,0) )
SetOrigin(level.Vec3Offset(pos,vel),true);
A_SetScale(scale.x*1.05);
A_FadeOut(FRandom[ExploS](.01,.03));
}
States
{
Spawn:
BLPS C -1 Bright;
Stop;
}
}
Class MisterPop : SWWMNonInteractiveActor
{
MisterRailCounter mrc;
Default
{
Obituary "$O_MORTALRIFLE";
DamageType "Mortal";
RenderStyle "Add";
Scale .2;
+FORCEXYBILLBOARD;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FOILINVUL;
+ROLLSPRITE;
+ROLLCENTER;
}
States
{
Spawn:
TNT1 A 1 NoDelay
{
A_SetTics(Random[ExploS](1,15));
Scale *= FRandom[ExploS](.5,1.5);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
roll = FRandom[ExploS](0,360);
}
BLPF C 2 Bright
{
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,4,2000,50,50,DE_EXTRAZTHRUST|DE_COUNTENEMIES);
if ( mrc ) mrc.nkill += nkill;
}
TNT1 A 1
{
let p = Spawn("MisterFuzzyTrail",pos);
p.alpha *= 1.5;
p.scale *= .5;
}
Stop;
}
}
Class MisterBulletImpact : SWWMNonInteractiveActor
{
MisterRailCounter mrc; // simplify code by putting this here
Default
{
Obituary "$O_MORTALRIFLE";
DamageType "Mortal";
RenderStyle "Add";
Scale 1.5;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+FOILINVUL;
}
virtual void A_BulletExplode()
{
A_AlertMonsters(swwm_uncapalert?0:4000,AMF_EMITFROMTARGET);
SWWMUtility.DoExplosion(self,444,80000,150,150,DE_EXTRAZTHRUST);
A_QuakeEx(6,6,6,10,0,400,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:150,rollintensity:.6);
A_StartSound("mister/hitsemi",CHAN_VOICE,attenuation:.3);
A_StartSound("mister/hitsemi",CHAN_WEAPON,attenuation:.2);
A_SprayDecal("RocketBlast",-172);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,8);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](1,3);
s.scale *= 3.5;
s.alpha *= .4;
}
numpt = Random[ExploS](8,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](6,16);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,16);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](15,20);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
}
Spawn("MisterExLight",pos);
Spawn("MisterBulletImpactPop",pos);
}
virtual void A_BulletSubExplode()
{
if ( special1 && (special1 <= 15) )
{
SWWMUtility.DoExplosion(self,44,5000+special1*200,50+special1*10,50+special1*10,DE_EXTRAZTHRUST);
int numpt = Random[ExploS](0,special1);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,16)*special1);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
}
}
special1++;
}
States
{
Spawn:
TNT1 A 0;
XEX7 ABDEGHJKMNPQSTVWYZ\] 1 Bright
{
A_SetScale(scale.x*1.02,scale.y*1.02);
A_BulletSubExplode();
}
Stop;
}
}
Class MisterBuckshotImpact : MisterBulletImpact
{
Default
{
Scale .8;
}
override void A_BulletExplode()
{
A_AlertMonsters(swwm_uncapalert?0:1000,AMF_EMITFROMTARGET);
SWWMUtility.DoExplosion(self,444,8000,80,80,DE_EXTRAZTHRUST);
A_QuakeEx(2,2,2,5,0,200,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:60,rollintensity:.2);
A_StartSound("mister/hitscatter",CHAN_VOICE,attenuation:.35);
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](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,4);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](0,2);
s.scale *= 2.5;
s.alpha *= .3;
}
numpt = Random[ExploS](2,6);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,6);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](-2,4);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
s.special1 -= 3;
s.special2 = -1;
}
Spawn("MisterExLightTiny",pos);
let p = Spawn("MisterBulletImpactPop",pos);
p.A_SetScale(1.5);
}
override void A_BulletSubExplode()
{
if ( special1 && (special1 <= 5) )
{
SWWMUtility.DoExplosion(self,44,600+special1*80,40+special1*12,40+special1*12,DE_EXTRAZTHRUST);
int numpt = Random[ExploS](0,special1/3);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,16)*special1);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
}
}
special1++;
}
States
{
Spawn:
TNT1 A 0;
XEX7 ADGJMPSVY\ 1 Bright
{
A_SetScale(scale.x*1.02,scale.y*1.02);
A_BulletSubExplode();
}
Stop;
}
}
Class MisterStreamImpact : MisterBulletImpact
{
Default
{
Scale 1.;
}
override void A_BulletExplode()
{
A_AlertMonsters(swwm_uncapalert?0:4000,AMF_EMITFROMTARGET);
SWWMUtility.DoExplosion(self,444,150000,120,120,DE_EXTRAZTHRUST);
A_QuakeEx(5,5,5,5,0,300,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:100,rollintensity:.5);
A_StartSound("mister/hitstream",CHAN_VOICE,attenuation:.35);
A_StartSound("mister/hitstream",CHAN_WEAPON,attenuation:.25);
A_SprayDecal("SmallRocketBlast",-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,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,8);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](0,2);
s.scale *= 3.5;
s.alpha *= .4;
}
numpt = Random[ExploS](4,6);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](3,6);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,16);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](4,8);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
s.special1 -= 2;
}
Spawn("MisterExLightSmall",pos);
let p = Spawn("MisterBulletImpactPop",pos);
p.A_SetScale(3.);
}
override void A_BulletSubExplode()
{
if ( special1 && (special1 <= 10) )
{
SWWMUtility.DoExplosion(self,44,2000+special1*50,80+special1*8,80+special1*8,DE_EXTRAZTHRUST);
int numpt = Random[ExploS](0,special1/2);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,16)*special1);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
}
}
special1++;
}
States
{
Spawn:
TNT1 A 0;
XEX7 ACEGIKMOQSUWY[] 1 Bright
{
A_SetScale(scale.x*1.02,scale.y*1.02);
A_BulletSubExplode();
}
Stop;
}
}
Class MisterRailEntryImpact : MisterBulletImpact
{
Default
{
Scale 1.2;
}
override void A_BulletExplode()
{
A_AlertMonsters(swwm_uncapalert?0:2000,AMF_EMITFROMTARGET);
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,444,200000,100,100,DE_EXTRAZTHRUST|DE_COUNTENEMIES);
if ( mrc ) mrc.nkill += nkill;
A_QuakeEx(6,6,6,10,0,400,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:150,rollintensity:.8);
A_StartSound("mister/hitover",CHAN_VOICE,attenuation:.35);
A_SprayDecal("SmallRocketBlast",-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,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,10);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](0,2);
s.scale *= 3.5;
s.alpha *= .4;
}
numpt = Random[ExploS](3,5);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,16);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](3,6);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
s.special1 -= 2;
if ( !mrc ) continue;
MisterFuzzy(s).mrc = mrc;
mrc.effectors.Push(s);
}
Spawn("MisterExLightTiny",pos);
let p = Spawn("MisterBulletImpactPop",pos);
p.A_SetScale(4.);
}
override void A_BulletSubExplode()
{
if ( special1 && (special1 <= 10) )
{
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,44,3000+special1*50,80+special1*4,80+special1*4,DE_EXTRAZTHRUST|DE_COUNTENEMIES);
if ( mrc ) mrc.nkill += nkill;
int numpt = Random[ExploS](0,special1/2);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,16)*special1);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
if ( !mrc ) continue;
MisterPop(p).mrc = mrc;
mrc.effectors.Push(p);
}
}
special1++;
}
States
{
Spawn:
TNT1 A 0;
XEX7 ACEGIKMOQSUWY[] 1 Bright
{
A_SetScale(scale.x*1.02,scale.y*1.02);
A_BulletSubExplode();
}
Stop;
}
}
Class MisterRailExitImpact : MisterBulletImpact
{
Default
{
Scale 1.8;
}
override void A_BulletExplode()
{
A_AlertMonsters(swwm_uncapalert?0:4000,AMF_EMITFROMTARGET);
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,444,80000,200,200,DE_EXTRAZTHRUST|DE_COUNTENEMIES);
if ( mrc ) mrc.nkill += nkill;
A_QuakeEx(8,8,8,15,0,600,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:250,rollintensity:1.2);
A_StartSound("mister/hitover",CHAN_WEAPON,attenuation:.2);
A_SprayDecal("BigRocketBlast",-172);
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](10,15);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,12);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](1,3);
s.scale *= 3.5;
s.alpha *= .4;
}
numpt = Random[ExploS](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](4,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](4,16);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](4,8);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
if ( !mrc ) continue;
MisterFuzzy(s).mrc = mrc;
mrc.effectors.Push(s);
}
Spawn("MisterExLightBig",pos);
let p = Spawn("MisterBulletImpactPop",pos);
p.A_SetScale(8.);
}
override void A_BulletSubExplode()
{
if ( special1 && (special1 <= 20) )
{
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,44,6000+special1*100,150+special1*5,150+special1*5,DE_EXTRAZTHRUST|DE_COUNTENEMIES);
if ( mrc ) mrc.nkill += nkill;
int numpt = Random[ExploS](0,special1/2);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,16)*special1);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
if ( !mrc ) continue;
MisterPop(p).mrc = mrc;
mrc.effectors.Push(p);
}
}
special1++;
}
States
{
Spawn:
TNT1 A 0;
XEX7 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright
{
A_SetScale(scale.x*1.02,scale.y*1.02);
A_BulletSubExplode();
}
Stop;
}
}
Class MisterRailLight : PaletteLight
{
Default
{
Tag "Cyanblu,1";
ReactionTime 50;
Args 0,0,0,250;
}
}
Class MisterRailHit : SWWMNonInteractiveActor
{
MisterRailCounter mrc;
Default
{
Obituary "$O_MORTALRIFLE";
DamageType 'Mortal';
+FORCERADIUSDMG;
+FOILINVUL;
+NODAMAGETHRUST;
}
virtual void Explode( bool thruwall = false )
{
int nhit, nkill;
[nhit, nkill] = SWWMUtility.DoExplosion(self,44,120000,100,100,DE_EXTRAZTHRUST|DE_COUNTENEMIES,ignoreme:target);
if ( mrc ) mrc.nkill += nkill;
}
States
{
Spawn:
TNT1 A 5;
Stop;
}
}
Class MisterRailBeam : SWWMNonInteractiveActor
{
MisterRailCounter mrc;
transient CandyBeamTracer cbt; // from wikipedia
Default
{
Obituary "$O_MORTALRIFLE";
RenderStyle "Add";
RenderRadius 10000.;
+FORCEXYBILLBOARD;
+FOILINVUL;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let b = Spawn("MisterRailChildBeam",pos);
b.frame = frame;
b.scale.y = scale.y;
b.angle = angle;
b.pitch = pitch;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*.85,scale.y);
A_FadeOut(.05);
if ( !cbt ) cbt = new("CandyBeamTracer");
cbt.hitlist.Clear();
Vector3 dir = SWWMUtility.Vec3FromAngles(angle,pitch-90);
cbt.ShootThroughList.Clear();
cbt.Trace(pos,CurSector,dir,scale.y,0);
foreach ( hit:cbt.hitlist )
{
if ( !hit.hitactor || (hit.hitactor == target) ) continue;
SWWMUtility.DoKnockback(hit.hitactor,hit.x,12000);
let p = SWWMPuff.Setup(hit.hitlocation,hit.x,self,target,hit.hitactor);
hit.hitactor.DamageMobj(p,target,44,'Mortal',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
if ( mrc && (!hit.hitactor || (hit.hitactor.Health <= 0)) )
mrc.nkill++;
}
}
States
{
Spawn:
XZW1 A -1 Bright;
Stop;
}
}
Class MisterRailChildBeam : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
Alpha .5;
RenderRadius 10000.;
+FORCEXYBILLBOARD;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*1.05,scale.y);
A_FadeOut(.01);
}
States
{
Spawn:
XZW1 A -1 Bright;
Stop;
}
}
Class MisterGrenadeFlare : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( !master || (master.ReactionTime <= 0) )
{
A_FadeOut();
return;
}
SetOrigin(master.Vec3Offset(0,0,master.height/2),true);
}
States
{
Spawn:
BLPS D -1 Bright;
Stop;
}
}
Class TargetTracer : LineTracer
{
Actor target;
override ETraceStatus TraceCallback()
{
if ( (Results.HitType == TRACE_HitActor) && (Results.HitActor == target) )
return TRACE_Stop;
return TRACE_Skip;
}
}
Class MisterGrenade : Actor
{
Mixin SWWMMissileFix;
Vector3 oldvel;
Default
{
Obituary "$O_MORTALRIFLEALT";
DamageType 'Mortal';
Radius 3;
Height 6;
Speed 40;
BounceFactor 1.;
WallBounceFactor 1.;
Gravity .35;
ReactionTime 100;
PROJECTILE;
-NOGRAVITY;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+FOILINVUL;
+HITTRACER;
+USEBOUNCESTATE;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+THRUACTORS;
+DONTBOUNCEONSKY;
+CANBOUNCEWATER;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
oldvel = vel;
let t = Spawn("MisterGrenadeFlare",pos);
t.master = self;
t.scale *= Radius/4.;
}
override void Tick()
{
oldvel = vel;
Super.Tick();
}
virtual void A_HandleBounce()
{
Vector3 HitNormal = SWWMUtility.GetActorHitNormal(self);
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
vel = FRandom[Mister](.75,1.)*((vel dot HitNormal)*HitNormal*(FRandom[Mister](-2.,-1.6))+vel);
if ( vel.length() < 4. )
{
A_Stop();
ClearBounce();
bINTERPOLATEANGLES = false;
SetStateLabel("Rest");
return;
}
// direct hit from main grenade
if ( !bAMBUSH && (vel dot oldvel < .5) )
{
// ensure we can leave a decal
angle += 180;
pitch = -pitch;
ExplodeMissile();
return;
}
A_StartSound(bAMBUSH?"mister/gbouncesub":"mister/gbounce",CHAN_ITEM,CHANF_OVERLAP);
}
virtual void A_GrenadeTick( bool bRest = false, bool bNoProx = false )
{
int numpt = bAMBUSH?1:3;
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("SWWMHalfSmoke",Vec3Offset(0,0,Height/2));
s.SetShade(Color(1,4,2)*Random[Mister](48,63));
s.scale *= FRandom[Mister](.5,1.1);
s.special1 = Random[Mister](0,2);
s.alpha *= .25;
s.vel += .3*vel;
}
if ( special1 > 0 )
{
ExplodeMissile();
return;
}
ReactionTime--;
if ( ReactionTime <= 0 )
{
ExplodeMissile();
return;
}
// proximity check
if ( bNoProx ) return;
// "safe delay" for main grenade
if ( !bAMBUSH && (ReactionTime > default.ReactionTime-20) ) return;
let bt = BlockThingsIterator.Create(self,120);
while ( bt.Next() )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),bAMBUSH?80:120) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
special1++;
tracer = t;
break;
}
}
// quicksort (seeking candidates)
private int partition_candidates( Array<Actor> a, int l, int h )
{
Actor pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( Distance3DSquared(pv) > Distance3DSquared(a[j]) )
{
i++;
Actor tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Actor tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_candidates( Array<Actor> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_candidates(a,l,h);
qsort_candidates(a,l,p-1);
qsort_candidates(a,p+1,h);
}
virtual void A_GrenadeExplode()
{
ReactionTime = 0;
bForceXYBillboard = true;
bRollSprite = false;
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("BigRocketBlast",50);
A_SetScale(4.);
A_NoGravity();
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
SWWMUtility.DoExplosion(self,444,120000,250,250,DE_EXTRAZTHRUST);
A_QuakeEx(8,8,8,20,0,900,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:300,rollintensity:1.5);
A_StartSound("mister/hitgrenade",CHAN_VOICE,attenuation:.3);
A_StartSound("mister/hitgrenade",CHAN_WEAPON,attenuation:.2);
A_AlertMonsters(swwm_uncapalert?0:3500,AMF_EMITFROMTARGET);
SetOrigin(Vec3Offset(0,0,Height/2),false);
int numpt = Random[ExploS](30,50);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,12);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](1,4);
s.scale *= 3.5;
s.alpha *= .4;
}
numpt = Random[ExploS](12,18);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,16);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](16,24);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
s.special1 += 2;
}
// gather seekable targets
Array<Actor> candidates;
let bt = BlockThingsIterator.Create(self,2000);
let tt = new("TargetTracer");
tt.target = target;
while ( bt.Next() )
{
let t = bt.Thing;
if ( !t || (t == tracer) || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
// don't seek if enemy is too close to shooter
if ( target && SWWMUtility.SphereIntersect(t,target.pos,250) ) continue;
// don't seek if shooter is between us and the enemy
if ( target )
{
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.Height/2));
double distto = dirto.length();
if ( distto > 0 )
{
dirto /= distto;
if ( tt.Trace(pos,CurSector,dirto,distto,0) ) continue;
}
}
// don't seek if enemy is too close to another candidate
// this would cause some of the splash damage potential to go to waste
bool tooclose = false;
foreach ( c:candidates )
{
if ( !SWWMUtility.SphereIntersect(t,c.pos,150) ) continue;
tooclose = true;
break;
}
if ( tooclose ) continue;
candidates.Push(t);
}
// sort by distance
if ( candidates.Size() > 1 )
qsort_candidates(candidates,0,candidates.Size()-1);
// distribute among spawned sub-grenades
int k = 0;
for ( int i=-45; i<=45; i+=45 ) for ( int j=0; j<360; j+=60 )
{
double ang = j;
double pt = i;
Vector3 dir = SWWMUtility.Vec3FromAngles(ang,pt);
let p = MisterSubGrenade(Spawn("MisterSubGrenade",pos));
p.angle = ang;
p.pitch = pt;
p.vel = dir*p.speed;
p.target = target;
p.ReactionTime += Random[Mister](-10,10);
if ( candidates.Size() > 0 )
{
p.seektarget = candidates[k];
k = (k+1)%candidates.Size();
}
}
Spawn("MisterExLightBig",pos);
Spawn("MisterRing",pos);
}
virtual void A_GrenadeSubExplode()
{
if ( special2 && (special2 <= 20) )
{
SWWMUtility.DoExplosion(self,44,5000+special2*500,100+special2*10,100+special2*10,DE_EXTRAZTHRUST);
int numpt = Random[ExploS](special2/2,special2);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,20)*special2);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
}
}
special2++;
}
States
{
Spawn:
XZW1 A 1 A_GrenadeTick();
Wait;
Bounce:
XZW1 A 0 A_HandleBounce();
Goto Spawn;
Rest:
XZW1 A 1 A_GrenadeTick(true);
Wait;
Death:
TNT1 A 0 A_GrenadeExplode();
XEX7 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright A_GrenadeSubExplode();
Stop;
}
}
Class MisterSubGrenade : MisterGrenade
{
Actor seektarget;
Default
{
Radius 2;
Height 4;
Speed 30;
ReactionTime 50;
+AMBUSH;
}
override void A_HandleBounce()
{
Super.A_HandleBounce();
if ( !seektarget || (seektarget.Health < 0) || !CheckSight(seektarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) return;
// "steer" towards seek target
Vector3 dirto = level.Vec3Diff(pos,seektarget.Vec3Offset(0,0,seektarget.Height/2));
double distto = dirto.length();
if ( distto <= 0. ) return;
dirto /= distto;
dirto.z += .1;
double spd = vel.length();
if ( spd <= 0. ) return;
vel /= spd;
vel = (vel*.2+dirto*.8)*spd;
// extra oomph
if ( spd < speed ) vel += dirto*max(20,speed-spd);
}
override void A_GrenadeTick( bool bRest, bool bNoProx )
{
Super.A_GrenadeTick(bRest,seektarget&&(seektarget.Health>0));
if ( !seektarget || (seektarget.Health < 0) ) return;
// check proximity to seek target
if ( SWWMUtility.SphereIntersect(seektarget,level.Vec3Offset(pos,vel),80) )
{
special1++;
tracer = seektarget;
return;
}
if ( !CheckSight(seektarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) return;
// sustain reaction time as long as seek target is visible
ReactionTime++;
// "subtly" steer towards seek target
Vector3 dirto = level.Vec3Diff(pos,seektarget.Vec3Offset(0,0,seektarget.Height/2));
double distto = dirto.length();
if ( distto <= 0. ) return;
dirto /= distto;
double spd = vel.length();
if ( spd <= 0. ) return;
vel /= spd;
vel = (vel*.7+dirto*.3)*spd;
// extra oomph
if ( spd < speed ) vel += dirto*max(.5,speed-spd);
}
override void A_GrenadeExplode()
{
ReactionTime = 0;
bForceXYBillboard = true;
bRollSprite = false;
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("RocketBlast",50);
A_SetScale(2.);
A_NoGravity();
Scale *= FRandom[ExploS](0.8,1.1);
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
SWWMUtility.DoExplosion(self,444,80000,150,150,DE_EXTRAZTHRUST);
A_QuakeEx(5,5,5,10,0,500,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:150,rollintensity:.8);
A_StartSound("mister/hitgrenadesub",CHAN_VOICE,attenuation:.5);
A_StartSound("mister/hitgrenadesub",CHAN_WEAPON,attenuation:.4);
A_AlertMonsters(swwm_uncapalert?0:1500,AMF_EMITFROMTARGET);
SetOrigin(Vec3Offset(0,0,Height/2),false);
int numpt = Random[ExploS](15,25);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,8);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(0,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](1,4);
s.scale *= 2.5;
s.alpha *= .4;
}
numpt = Random[ExploS](6,9);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](5,10);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](8,16);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](6,8);
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("MisterFuzzy",pos);
s.angle = FRandom[ExploS](0,360);
s.pitch = FRandom[ExploS](-90,90);
s.target = target;
s.special1 -= 2;
}
Spawn("MisterExLight",pos);
}
override void A_GrenadeSubExplode()
{
if ( special2 && (special2 <= 10) )
{
SWWMUtility.DoExplosion(self,44,5000+special2*500,100+special2*10,100+special2*10,DE_EXTRAZTHRUST);
int numpt = Random[ExploS](special2/2,special2);
for ( int i=0; i<numpt; i++ )
{
Vector3 np = level.Vec3Offset(pos,SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](2,10)*special2);
if ( !level.IsPointInLevel(np) ) continue;
let p = Spawn("MisterPop",np);
p.target = target;
}
}
special2++;
}
States
{
Death:
TNT1 A 0 A_GrenadeExplode();
XEX7 ACEGIKMOQSUWY[] 1 Bright A_GrenadeSubExplode();
Stop;
}
}