1419 lines
34 KiB
Text
1419 lines
34 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);
|
|
Vector3 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;
|
|
}
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
for ( int i=0; i<cbt.hitlist.Size(); i++ )
|
|
{
|
|
if ( !cbt.hitlist[i].hitactor || (cbt.hitlist[i].hitactor == target) ) continue;
|
|
SWWMUtility.DoKnockback(cbt.hitlist[i].hitactor,cbt.hitlist[i].x,12000);
|
|
let p = SWWMPuff.Setup(cbt.hitlist[i].hitlocation,cbt.hitlist[i].x,self,target,cbt.hitlist[i].hitactor);
|
|
cbt.hitlist[i].hitactor.DamageMobj(p,target,44,'Mortal',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
|
|
if ( mrc && (!cbt.hitlist[i].hitactor || (cbt.hitlist[i].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 = -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();
|
|
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
|
|
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 = 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);
|
|
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;
|
|
for ( int i=0; i<candidates.Size(); i++ )
|
|
{
|
|
if ( !SWWMUtility.SphereIntersect(t,candidates[i].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);
|
|
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;
|
|
}
|
|
}
|
|
|