swwmgz_m/zscript/swwm_shot.zsc
Marisa Kirisame 9995a5ebf0 Sandwich can only be eaten if at 500 hp or lower.
Rebalances to ammo spawns.
Hellblazer gives 3 ammo instead of 6.
Recolored Hellblazer explosion sprites to be more Nokron-tinted.
Rebalances to ammo max amounts.
Rebalanced damages across the board (notable nerf of Golden Shells, they're no longer "I win" ammo)
Fix certain hitscan attacks not splashing in liquids.
Fix blood drops sometimes going out of bounds.
Don't stack refresher regen time if multiple copies were auto-used.
Pusher can now propel you underwater.
2020-12-21 01:18:16 +01:00

2953 lines
79 KiB
Text

// Blackmann "Rhino Stopper" Spreadgun (from Instant Action 3, also planned for Zanaveth Ultra Suite 2)
// Slot 3, replaces Shotgun, Ethereal Crossbow, Serpent Staff
Class RedShellCasing : SWWMCasing
{
Default
{
BounceSound "spreadgun/casing";
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
heat = 0;
}
}
Class GreenShellCasing : RedShellCasing {}
Class WhiteShellCasing : RedShellCasing {}
Class BlueShellCasing : RedShellCasing {}
Class BlackShellCasing : RedShellCasing {}
Class PurpleShellCasing : RedShellCasing {}
Class GoldShellCasing : RedShellCasing
{
Default
{
BounceSound "spreadgun/gcasing";
}
}
Class SpreadgunTracer : LineTracer
{
Actor ignoreme;
Array<HitListEntry> hitlist;
Array<Line> shootthroughlist;
Array<WaterHit> waterhitlist;
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
int amt = SWWMDamageAccumulator.GetAmount(Results.HitActor);
// getgibhealth isn't clearscope, fuck
int gibhealth = -int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor);
if ( Results.HitActor.GibHealth != int.min ) gibhealth = -abs(Results.HitActor.GibHealth);
// if gibbed, go through without dealing more damage
if ( Results.HitActor.health-amt <= gibhealth ) return TRACE_Skip;
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
hitlist.Push(ent);
// go right on through if dead
if ( Results.HitActor.health-amt <= 0 ) return TRACE_Skip;
// stap
return TRACE_Stop;
}
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 SpreadSlugTracer : SpreadgunTracer
{
double penetration; // please don't laugh
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
if ( (Results.HitActor.Health >= int(penetration)) || Results.HitActor.bNODAMAGE )
{
ent.hitdamage = int(penetration);
penetration = 0;
}
else
{
ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration));
penetration = max(0,penetration-ent.hitdamage);
}
hitlist.Push(ent);
if ( penetration <= 0 ) return TRACE_Stop;
return TRACE_Skip;
}
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 SpreadImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:200);
A_StartSound("spreadgun/pellet",CHAN_VOICE,CHANF_DEFAULT,.4,4.);
A_SprayDecal("TinyPock",-20);
int numpt = Random[Spreadgun](2,4)-special1;
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8))).unit()*FRandom[Spreadgun](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.scale *= .6;
s.special1 = Random[Spreadgun](0,1);
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
}
numpt = Random[Spreadgun](-1,3)-special1;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](2,4)-special1;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](1,4);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class SlugImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:500);
A_StartSound("spreadgun/slug",CHAN_VOICE,CHANF_DEFAULT,1.,2.);
A_SprayDecal("Pock",-20);
int numpt = Random[Spreadgun](5,10)-special1;
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Spreadgun](-.4,.4),FRandom[Spreadgun](-.4,.4),FRandom[Spreadgun](-.4,.4))).unit()*FRandom[Spreadgun](.4,2.);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.scale *= 1.4;
s.special1 = Random[Spreadgun](0,2);
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
}
numpt = Random[Spreadgun](2,5)-special1;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](4,8)-special1;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class DragonBreathPuff : Actor
{
Vector2 initsc;
Default
{
RenderStyle "Add";
Scale 0.5;
Alpha 0.35;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOINTERACTION;
+NOTELEPORT;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
scale *= FRandom[Spreadgun](.8,1.);
alpha *= FRandom[Spreadgun](.8,1.);
roll = FRandom[Spreadgun](0,360);
SetState(FindState("Spawn")+Random[Spreadgun](0,19));
initsc = scale;
}
override void Tick()
{
if ( isFrozen() ) return;
A_FadeOut((waterlevel>0)?.1:.02);
scale += initsc*.2;
}
States
{
Spawn:
XFLM ABCDEFGHIJKLMNOPQRST -1 Bright;
Stop;
}
}
Class DragonBreathArm : Actor
{
Vector3 oldvel;
Default
{
Obituary "$O_SPREADGUN_WHITE";
DamageType 'Fire';
PROJECTILE;
+THRUACTORS;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+USEBOUNCESTATE;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
-NOGRAVITY;
Gravity 0.15;
BounceFactor 1.0;
Radius 4;
Height 4;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
reactiontime = Random[ExploS](18,24);
vel = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch))*FRandom[ExploS](16.,32.);
let l = Spawn("PaletteLight",pos);
l.Args[3] = int(60+60*(ReactionTime/20.));
l.ReactionTime = ReactionTime+2;
l.target = self;
}
override void Tick()
{
oldvel = vel;
Super.Tick();
}
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(pos,BlockingMobj.pos);
if ( (pos.x+radius) <= (BlockingMobj.pos.x-BlockingMobj.radius) )
HitNormal = (-1,0,0);
else if ( (pos.x-radius) >= (BlockingMobj.pos.x+BlockingMobj.radius) )
HitNormal = (1,0,0);
else if ( (pos.y+radius) <= (BlockingMobj.pos.y-BlockingMobj.radius) )
HitNormal = (0,-1,0);
else if ( (pos.y-radius) >= (BlockingMobj.pos.y+BlockingMobj.radius) )
HitNormal = (0,1,0);
else if ( pos.z >= (BlockingMobj.pos.z+BlockingMobj.height) )
HitNormal = (0,0,1);
else if ( (pos.z+height) <= BlockingMobj.pos.z )
HitNormal = (0,0,-1);
}
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
vel = .8*((vel dot HitNormal)*HitNormal*(-1.8+FRandom[Spreadgun](.0,.6))+vel);
bHITOWNER = true;
}
States
{
Spawn:
TNT1 A 1
{
if ( waterlevel > 0 ) ReactionTime -= 2;
let p = Spawn("DragonBreathPuff",pos);
p.alpha *= .6+.4*(ReactionTime/20.);
p.scale *= 3.5-2.5*(ReactionTime/20.);
if ( !(ReactionTime%2) )
SWWMUtility.DoExplosion(self,1+(reactiontime/2),1000+200*reactiontime,150-6*reactiontime,flags:DE_HOWL,ignoreme:bHITOWNER?null:target);
double spd = vel.length();
vel = (vel*.4+(FRandom[ExploS](-.2,.2),FRandom[ExploS](-.2,.2),FRandom[ExploS](-.2,.2))).unit()*spd;
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
if ( !(ReactionTime%2) )
{
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = pvel+vel*.2;
s.SetShade(Color(1,1,1)*Random[ExploS](96,192));
s.special1 = Random[ExploS](2,4);
s.scale *= 2.4;
s.alpha *= .1+.2*(ReactionTime/20.);
int numpt = Random[Spreadgun](-2,4);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = vel+(FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,4);
let s2 = Spawn("SWWMSpark",pos);
s2.scale *= .4;
s2.vel = pvel;
}
}
A_CountDown();
}
Wait;
Bounce:
TNT1 A 0 A_HandleBounce();
Goto Spawn;
}
}
Class SaltTracer : LineTracer
{
Actor ignore;
Array<Line> ShootThroughList;
Array<WaterHit> WaterHitList;
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignore ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class SaltLight : PaletteLight
{
Default
{
Tag "SaltExpl,1";
ReactionTime 30;
Args 0,0,0,240;
}
}
Class SaltLight2 : PaletteLight
{
Default
{
Tag "SaltExpl";
ReactionTime 30;
Args 0,0,0,70;
}
}
Class SaltImpact : Actor
{
Default
{
Obituary "$O_SPREADGUN_BLUE";
DamageType "Electric";
RenderStyle "Add";
Radius 0.1;
Height 0;
Scale 1.8;
+NOGRAVITY;
+NOBLOCKMAP;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+FOILINVUL;
+NOINTERACTION;
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( args[0] >= 1 ) return StringTable.Localize("$O_WALLBUSTER_BLUE");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_AlertMonsters(swwm_uncapalert?0:6000);
SWWMUtility.DoExplosion(self,12+special2*4,15000,100,40);
A_QuakeEx(3,3,3,10,0,250,"",QF_RELATIVE|QF_SCALEDOWN,falloff:150,rollintensity:0.2);
A_StartSound("spreadgun/salt",CHAN_VOICE,attenuation:.35);
A_SprayDecal("ShockMarkSmall",-172);
A_SprayDecal("SaltMark",-172);
Scale *= FRandom[ExploS](0.8,1.1);
int numpt = Random[ExploS](5,9)-special1;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,6);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,3,4)*Random[ExploS](48,63));
s.special1 = Random[ExploS](1,2);
s.scale *= 2.4;
s.A_SetRenderStyle(.4,STYLE_AddShaded);
}
numpt = Random[ExploS](3,9)-special1;
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,6);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](3,6)-special1;
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("SWWMChip",pos);
s.vel = pvel;
}
Spawn("SaltLight2",pos);
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
TNT1 A 0 NoDelay A_Jump(256,"Expl1","Expl2","Expl3");
Expl1:
KSX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
Stop;
Expl2:
KSX2 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
Stop;
Expl3:
KSX3 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
Stop;
}
}
Class SaltBeam : Actor
{
Default
{
Obituary "$O_SPREADGUN_BLUE";
DamageType "Electric";
RenderStyle "Add";
Radius 0.1;
Height 0;
Stamina 9;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOTELEPORT;
+ROLLSPRITE;
+ROLLCENTER;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FOILINVUL;
+NOINTERACTION;
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( args[1] >= 1 ) return StringTable.Localize("$O_WALLBUSTER_BLUE");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
void SpreadOut()
{
special1 = 1;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
let t = new("SaltTracer");
t.ignore = target;
t.Trace(pos,cursector,x,32,TRACE_HitSky);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
}
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=4; i<t.Results.Distance; i+=8 )
{
if ( Random[Spreadgun](0,Stamina) ) continue;
let b = Actor.Spawn("SWWMHalfSmoke",level.Vec3Offset(pos,x*i));
b.Scale *= FRandom[Spreadgun](.6,.8);
b.special1 = Random[Spreadgun](1,2);
b.A_SetRenderStyle(.3,STYLE_AddShaded);
b.SetShade(Color(1,3,4)*Random[Spreadgun](48,63));
b.vel += x*FRandom[Spreadgun](-.5,3);
}
if ( t.Results.HitType != TRACE_HitNone )
{
if ( (args[1] == 2) || swwm_omnibust ) BusterWall.Bust(t.Results,85+Accuracy*10,target,x,t.Results.HitPos.z);
if ( t.Results.HitType == TRACE_HitActor )
{
SWWMUtility.DoKnockback(t.Results.HitActor,x,25000);
t.Results.HitActor.DamageMobj(self,target,20+Accuracy*4,'Salt',DMG_THRUSTLESS);
if ( t.Results.HitActor && t.Results.HitActor.bIsMonster && !Random[Spreadgun](0,3) )
t.Results.HitActor.Howl();
}
Vector3 norm = -x;
if ( t.Results.HitType == TRACE_HitWall )
{
norm = (t.Results.HitLine.delta.y,-t.Results.HitLine.delta.x,0).unit();
if ( t.Results.Side ) norm *= -1;
t.Results.HitLine.RemoteActivate(tracer,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
else if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.ffloor ) norm = -t.Results.ffloor.top.Normal;
else norm = t.Results.HitSector.floorplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.ffloor ) norm = -t.Results.ffloor.bottom.Normal;
else norm = t.Results.HitSector.ceilingplane.Normal;
}
if ( t.Results.HitType != TRACE_HasHitSky )
{
let i = Spawn("SaltImpact",level.Vec3Offset(t.Results.HitPos,norm*4));
i.angle = atan2(norm.y,norm.x);
i.pitch = asin(-norm.z);
i.target = target;
i.special1 = (Stamina-9)/4;
i.special2 = Accuracy;
i.args[0] = args[1];
}
return;
}
else if ( (args[0] > 20) && !Random[Spreadgun](0,800/args[0]) )
{
let i = Spawn("SaltImpact",level.Vec3Offset(pos,x*32));
i.angle = atan2(x.y,x.x);
i.pitch = asin(-x.z);
i.target = target;
i.special1 = (Stamina-9)/4;
i.special2 = Accuracy;
i.args[0] = args[1];
return;
}
// next beam
if ( !(special2%4) && !Random[Spreadgun](0,Stamina) )
Spawn("SaltLight",level.Vec3Offset(pos,x*16));
let next = Spawn("SaltBeam",level.Vec3Offset(pos,x*32));
double a = FRandom[Spreadgun](0,360), s = FRandom[Spreadgun](0,.06);
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
next.angle = atan2(dir.y,dir.x);
next.pitch = asin(-dir.z);
next.target = target;
next.special2 = (special2+1)%10;
next.args[0] = args[0]+1;
next.args[1] = args[1];
next.SetStateLabel("TrailSpawn");
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( !Random[Spreadgun](0,3) )
A_StartSound("spreadgun/salttrail",CHAN_VOICE,CHANF_DEFAULT,.3,4.);
}
override void Tick()
{
if ( isFrozen() ) return;
A_FadeOut(.04);
if ( Random[Spreadgun](-2,args[2]/10) == 0 )
SWWMUtility.DoExplosion(self,4+Accuracy,5000,32,flags:DE_HOWL,ignoreme:target);
if ( ((special2%4) || args[2]) && !special1 ) SpreadOut();
args[2]++;
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[Spreadgun](0,11)*2;
}
Stop;
TrailSpawn:
XZW2 A -1 Bright
{
return FindState("TrailerDev")+Random[Spreadgun](0,11)*2;
}
Stop;
StarterDev:
#### # 25 Bright;
XZW1 B -1 Bright;
Stop;
#### # 25 Bright;
XZW1 C -1 Bright;
Stop;
#### # 25 Bright;
XZW1 D -1 Bright;
Stop;
#### # 25 Bright;
XZW1 E -1 Bright;
Stop;
#### # 25 Bright;
XZW1 F -1 Bright;
Stop;
#### # 25 Bright;
XZW1 G -1 Bright;
Stop;
#### # 25 Bright;
XZW1 H -1 Bright;
Stop;
#### # 25 Bright;
XZW1 I -1 Bright;
Stop;
#### # 25 Bright;
XZW1 J -1 Bright;
Stop;
#### # 25 Bright;
XZW1 K -1 Bright;
Stop;
#### # 25 Bright;
XZW1 L -1 Bright;
Stop;
#### # 25 Bright;
XZW1 M -1 Bright;
Stop;
TrailerDev:
#### # 25 Bright;
XZW2 B -1 Bright;
Stop;
#### # 25 Bright;
XZW2 C -1 Bright;
Stop;
#### # 25 Bright;
XZW2 D -1 Bright;
Stop;
#### # 25 Bright;
XZW2 E -1 Bright;
Stop;
#### # 25 Bright;
XZW2 F -1 Bright;
Stop;
#### # 25 Bright;
XZW2 G -1 Bright;
Stop;
#### # 25 Bright;
XZW2 H -1 Bright;
Stop;
#### # 25 Bright;
XZW2 I -1 Bright;
Stop;
#### # 25 Bright;
XZW2 J -1 Bright;
Stop;
#### # 25 Bright;
XZW2 K -1 Bright;
Stop;
#### # 25 Bright;
XZW2 L -1 Bright;
Stop;
#### # 25 Bright;
XZW2 M -1 Bright;
Stop;
}
}
Class OnFireLight : DynamicLight
{
OnFire of;
override void Tick()
{
Super.Tick();
if ( !of || !of.victim )
{
Destroy();
return;
}
Args[0] = clamp(of.Amount*4,0,255);
Args[1] = clamp(of.Amount*2,0,160);
Args[2] = clamp(of.Amount/2,0,24);
Args[3] = int(max(of.victim.default.radius,of.victim.default.height)*(of.victim.scale.x+of.victim.scale.y)*1.2+40+clamp(of.amount/5,0,120));
SetOrigin(of.Victim.Vec3Offset(0,0,of.Victim.Height/2),true);
}
}
Class OnFire : Actor
{
OnFire prevfire, nextfire;
Actor victim, instigator, lite;
int amount, cnt, delay;
double oangle;
override void OnDestroy()
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd )
{
hnd.fires_cnt--;
if ( !prevfire )
{
hnd.fires = nextfire;
if ( nextfire ) nextfire.prevfire = null;
}
else
{
prevfire.nextfire = nextfire;
if ( nextfire ) nextfire.prevfire = prevfire;
}
}
Super.OnDestroy();
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !victim )
{
A_StopSound(CHAN_5);
Destroy();
return;
}
SetOrigin(victim.pos,false);
if ( victim.waterlevel > 0 )
{
if ( lite ) lite.Destroy();
amount -= int(victim.waterlevel**2);
}
if ( (victim.Health <= 0) || ((victim is 'FlamingChunk') && !victim.bMISSILE) ) amount = min(amount,100);
if ( !(level.maptime%6) )
amount--;
if ( victim.player ) amount -= int(abs(actor.deltaangle(victim.angle,oangle))/30);
oangle = victim.angle;
if ( amount < -30 )
{
A_StopSound(CHAN_5);
Destroy();
return;
}
if ( cnt > 0 ) cnt--;
else
{
cnt = min(10,30-int(29*(min(1.,amount/500.)**3.)));
if ( victim.bSHOOTABLE && (victim.Health > 0) && (amount > 0) )
{
int flg = DMG_THRUSTLESS;
if ( victim is 'Centaur' ) flg |= DMG_FOILINVUL; // you're on fire, that shield is worthless
victim.DamageMobj(self,instigator,clamp(int(amount*.06),1,20),'Fire',flg); // need to use this actor as inflictor to have a proper obituary
if ( victim.bISMONSTER && !Random[FlameT](0,3) )
victim.Howl();
}
if ( !victim )
{
A_StopSound(CHAN_5);
Destroy();
return;
}
}
double mult = max(victim.radius,victim.height)/30.;
if ( victim is 'FlamingChunk' ) mult *= 20.-victim.special1*3.;
if ( delay > 0 ) delay--;
if ( (level.maptime+special1)%6 ) return;
A_SoundVolume(CHAN_5,min(1.,mult*amount/80.));
int numpt = clamp(int(Random[FlameT](2,4)*amount*.01),1,4);
numpt = int(clamp(numpt*mult**.5,1,3));
for ( int i=0; i<numpt; i++ )
{
Vector3 pos = victim.Vec3Offset(FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](-victim.radius,victim.radius)*0.8,FRandom[FlameT](victim.height*0.2,victim.height*0.8));
double ang = FRandom[FlameT](0,360);
double pt = FRandom[FlameT](-90,90);
if ( amount > 0 )
{
let c = victim.Spawn("OnFireTrail",pos);
c.special1 = Random[FlameT](-2,2);
c.scale *= max(.3,mult*0.5);
c.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.5,2.)*c.scale.x;
}
if ( !(i%2) )
{
let s = victim.Spawn("SWWMHalfSmoke",pos);
s.scale *= max(1.,1.6*mult);
s.alpha *= min(amount+30,100)*.01;
s.vel = victim.vel*0.5+(cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[FlameT](.2,.6)*s.scale.x;
}
}
if ( amount <= 0 ) return;
// spread to nearby actors
let bt = BlockThingsIterator.Create(victim);
while ( bt.Next() )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || (t.Health <= 0) || (t == victim) || ((t == instigator) && (delay > 0)) || (victim.Distance3D(t) > victim.radius+t.radius+40) || !victim.CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
int amt = max(1,amount/10);
OnFire of = IsOnFire(t);
if ( of )
{
amt = min(5,amt);
if ( instigator ) of.instigator = instigator;
of.amount = min(500,of.amount+amt);
of.cnt = min(of.cnt,5);
}
else Apply(t,instigator,amt);
}
}
static OnFire Apply( Actor victim, Actor instigator, int amount, int delay = 0 )
{
if ( amount <= 0 ) return null;
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return null;
OnFire t;
for ( t=hnd.fires; t; t=t.nextfire )
{
if ( t.victim != victim ) continue;
if ( instigator ) t.instigator = instigator;
t.amount = min(500,t.amount+amount);
t.cnt = min(t.cnt,5);
return t;
}
t = OnFire(Spawn("OnFire",victim.pos));
t.victim = victim;
t.instigator = instigator;
t.amount = min(500,amount);
t.cnt = 1;
t.special1 = Random[FlameT](0,10);
t.A_StartSound("spreadgun/flame",CHAN_5,CHANF_LOOP);
double mult = max(victim.radius,victim.height)/30.;
if ( victim is 'FlamingChunk' ) mult *= 20.-victim.special1*3.;
t.A_SoundVolume(CHAN_5,min(1.,mult*amount/80.));
// for chunks
t.delay = delay;
t.lite = Actor.Spawn("OnFireLight",victim.pos);
OnFireLight(t.lite).of = t;
t.oangle = victim.angle;
// append
t.nextfire = hnd.fires;
if ( hnd.fires ) hnd.fires.prevfire = t;
hnd.fires = t;
hnd.fires_cnt++;
return t;
}
static OnFire IsOnFire( Actor victim )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return null;
OnFire t;
for ( t=hnd.fires; t; t=t.nextfire )
{
if ( t.victim != victim ) continue;
if ( t.amount <= 0 ) return null;
return t;
}
return null;
}
Default
{
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOEXTREMEDEATH;
+NOINTERACTION;
Obituary "$O_SPREADGUN_BLACK";
}
}
Class OnFireTrailLight : PaletteLight
{
Default
{
Tag "HellExpl";
Args 0,0,0,40;
ReactionTime 40;
}
override void Tick()
{
Super.Tick();
Args[0] /= 10;
Args[1] /= 10;
Args[2] /= 10;
Args[3] += 3;
if ( !target || (target.waterlevel > 0) )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
}
}
Class OnFireTrail : Actor
{
override void PostBeginPlay()
{
Super.PostBeginPlay();
Scale.x *= RandomPick[ExploS](-1,1);
Scale.y *= RandomPick[ExploS](-1,1);
roll = FRandom[ExploS](0,360);
}
action void A_Flame()
{
special1++;
if ( waterlevel > 0 )
vel *= .9;
else
{
vel *= .98;
vel.z += .1+.2*abs(scale.x);
}
if ( waterlevel > 0 )
{
let s = Spawn("SWWMSmoke",pos);
s.vel = (FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2));
s.vel += vel*.3;
s.alpha *= alpha*2;
s.scale *= .5+abs(scale.x)*(.5+special1/6.);
Destroy();
return;
}
if ( !Random[FlameT](0,int(40*(default.alpha-alpha))) )
{
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = (FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2),FRandom[FlameT](-.2,.2));
s.vel += vel*.3;
s.alpha *= alpha*1.5;
s.scale *= .5+abs(scale.x)*(.5+special1/6.);
}
}
override void Tick()
{
if ( isFrozen() ) return;
SetOrigin(level.Vec3Offset(pos,vel),true);
UpdateWaterLevel();
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
Default
{
RenderStyle "Add";
Speed 2;
Radius 4;
Height 4;
Alpha .6;
Scale .8;
+NOBLOCKMAP;
+NOGRAVITY;
+NOFRICTION;
+SLIDESONWALLS;
+NOTELEPORT;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
+DROPOFF;
+NOBLOCKMONST;
+DONTSPLASH;
+NOINTERACTION;
}
States
{
Spawn:
XFLM ABCDEFGHIJKLMNOPQRST 1 Bright
{
A_Flame();
A_SetScale(scale.x*0.98);
A_FadeOut(0.02);
}
Wait;
}
}
Class FlamingChunk : Actor
{
double rollvel;
OnFire myfire;
Vector3 oldvel;
int deadtimer;
Actor lasthit;
override void PostBeginPlay()
{
Super.PostBeginPlay();
rollvel = FRandom[FlameT](10,30)*RandomPick[FlameT](-1,1);
Scale *= FRandom[FlameT](.8,1.2);
if ( waterlevel <= 0 ) myfire = OnFire.Apply(self,target,int(200*scale.x),special1?0:6);
frame = Random[FlameT](0,5);
}
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
{
if ( target != lasthit )
{
OnFire.Apply(target,self.target,myfire?myfire.Amount:1);
lasthit = target;
}
return damage;
}
override void Tick()
{
oldvel = vel;
Super.Tick();
if ( isFrozen() ) return;
if ( InStateSequence(CurState,ResolveState("Death")) )
{
deadtimer++;
if ( deadtimer > 300 ) A_FadeOut(0.05);
return;
}
}
void A_HandleBounce()
{
bHITOWNER = true;
lasthit = null;
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 ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
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 = .7*((vel dot HitNormal)*HitNormal*FRandom[FlameT](-1.1,-1.6)+vel);
gravity = .35;
if ( (vel.length() < 2) && (pos.z <= floorz) )
{
ClearBounce();
ExplodeMissile();
}
rollvel = FRandom[FlameT](10,30)*RandomPick[FlameT](-1,1);
// see if we can split up
if ( (oldvel.length() > 5) && (scale.x > .1) && !Random[FlameT](0,2+special1*2) )
{
int npt = Random[FlameT](2,3);
for ( int i=0; i<npt; i++ )
{
let s = FlamingChunk(Spawn("FlamingChunk",pos));
Vector3 dir = (vel*.04+(FRandom[FlameT](-1,1),FRandom[FlameT](-1,1),FRandom[FlameT](-1,1))).unit();
s.special1 = special1+1;
s.vel = dir*vel.length()*FRandom[FlameT](.9,1.3);
s.angle = atan2(dir.y,dir.x);
s.pitch = asin(-dir.z);
s.target = target;
s.scale = scale*(1./npt);
}
Destroy();
}
}
Default
{
Obituary "$O_SPREADGUN_BLACK";
DamageFunction 1;
DamageType 'Fire';
Scale .5;
Radius 2;
Height 2;
Speed 30;
Gravity 0.15;
PROJECTILE;
-NOGRAVITY;
+ROLLSPRITE;
+ROLLCENTER;
+EXPLODEONWATER;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+NODAMAGETHRUST;
+INTERPOLATEANGLES;
+RIPPER;
+BLOODLESSIMPACT;
+NOTELEPORT;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+USEBOUNCESTATE;
BounceFactor 1.0;
}
States
{
Spawn:
JUNK # 1 { roll += rollvel; }
Wait;
Bounce:
JUNK # 0 A_HandleBounce();
Goto Spawn;
Death:
JUNK # -1 { bMOVEWITHSECTOR = true; }
Stop;
Dummy:
JUNK ABCDEF -1;
Stop;
}
}
Class BallImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:500);
A_QuakeEx(3,3,3,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3);
A_StartSound("spreadgun/ball",CHAN_VOICE);
A_SprayDecal("WallCrack",-20);
int numpt = Random[Spreadgun](5,10);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8),FRandom[Spreadgun](-.8,.8))).unit()*FRandom[Spreadgun](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Spreadgun](128,192));
}
numpt = Random[Spreadgun](4,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Spreadgun](4,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class TheBall : Actor
{
double heat;
int deadtimer;
Vector3 oldvel;
Actor lasthit;
Default
{
Obituary "$O_SPREADGUN_PURPLE";
PROJECTILE;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+CANBOUNCEWATER;
+USEBOUNCESTATE;
+DONTBOUNCEONSKY;
+NODAMAGETHRUST;
-NOGRAVITY;
Speed 80;
Gravity 0.1;
BounceFactor 1.0;
Radius 2;
Height 2;
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( special1 >= 1 ) return StringTable.Localize("$O_WALLBUSTER_PURPLE");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override int SpecialMissileHit( Actor victim )
{
if ( (vel.length() <= 5) || ((victim == target) && !bHITOWNER) || (victim == lasthit) || (!victim.bSHOOTABLE && !victim.bSOLID) )
return 1;
// check if we should rip or bounce
// girthitude
double girth = (victim.radius+victim.height)/2.*max(50,victim.mass)*(victim.health/double(victim.GetSpawnHealth()));
// how hard this damn thing is going to slam
double slamforce = vel.length()*350.+heat*120;
int dmg = int(vel.length()*4.+heat*40);
bool is_schutt = victim.bSHOOTABLE;
// critical hit!
bool crit = false;
if ( is_schutt && !Random[Spreadgun](0,9) )
{
Spawn("SWWMItemFog",pos);
int whichclonk = Random[Spreadgun](1,11);
String snd = String.Format("misc/clonk%d",whichclonk);
A_AlertMonsters(swwm_uncapalert?0:2500);
A_StartSound(snd,CHAN_VOICE,CHANF_OVERLAP,1.,.2);
A_StartSound(snd,CHAN_VOICE,CHANF_OVERLAP,1.,.2);
victim.A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.);
victim.A_StartSound(snd,CHAN_DAMAGE,CHANF_OVERLAP,1.,.2);
slamforce *= 4;
dmg *= 4;
vel *= 1.1;
let numpt = Random[Spreadgun](20,30);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](1,8);
let s = Spawn("SWWMSpark",pos);
s.scale *= 3.;
s.alpha *= .2;
s.vel = pvel;
}
crit = true;
}
SWWMUtility.DoKnockback(victim,vel.unit(),slamforce);
bool bleeds = (victim && !victim.bINVULNERABLE && !victim.bNOBLOOD && !victim.bDORMANT && is_schutt);
int newdmg = victim.DamageMobj(self,target,dmg,'Concussion',crit?(DMG_THRUSTLESS|DMG_FOILINVUL):DMG_THRUSTLESS); // crits ignore invulnerability
Vector3 dir = -vel.unit();
// slam jam
if ( bleeds )
{
A_StartSound("spreadgun/ballf",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/30.)**.5);
if ( victim )
{
victim.A_StartSound("spreadgun/ballf",CHAN_DAMAGE,CHANF_OVERLAP,(vel.length()/30.)**.5);
victim.TraceBleed((newdmg>0)?newdmg:dmg,self);
victim.SpawnBlood(pos,atan2(dir.y,dir.x),dmg);
}
}
else
{
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/30.)**.5);
if ( victim ) victim.A_StartSound("spreadgun/ball",CHAN_DAMAGE,CHANF_OVERLAP,(vel.length()/30.)**.5);
if ( vel.length() > 15. )
{
let s = Spawn("BallImpact",pos);
s.angle = atan2(dir.y,dir.x);
s.pitch = asin(-dir.z);
}
}
if ( crit )
SWWMUtility.DoExplosion(self,dmg/2,25000,150,80,ignoreme:target);
// only rip shootables
if ( (slamforce > girth) && is_schutt )
{
vel *= .7;
return 1;
}
// force bounce
BlockingMobj = victim;
A_HandleBounce();
lasthit = victim;
// pretend to pass through
return 1;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("pusher/fly",CHAN_WEAPON,CHANF_LOOP,.6,3.,2.);
heat = 1.;
}
override void Tick()
{
oldvel = vel;
Super.Tick();
if ( isFrozen() ) return;
if ( InStateSequence(CurState,ResolveState("Death")) )
{
deadtimer++;
if ( deadtimer > 300 )
{
let numpt = Random[Spreadgun](3,6);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1),FRandom[Spreadgun](-1,1)).unit()*FRandom[Spreadgun](1,8);
let s = Spawn("SWWMSmallSmoke",pos);
s.scale *= 3.;
s.alpha *= .2;
s.vel = pvel;
}
Destroy();
}
return;
}
heat -= 0.004+0.0004*vel.length();
A_SoundVolume(CHAN_WEAPON,vel.length()/75.);
if ( heat <= 0 ) return;
let s = Spawn("SWWMHalfSmoke",pos);
s.alpha *= heat;
}
void A_HandleBounce()
{
bHITOWNER = true;
lasthit = null;
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;
if ( (oldvel.length() > 15) && swwm_balluse )
{
int locknum = SWWMUtility.GetLineLock(BlockingLine);
// remotely activate unlocked lines (that aren't exits)
if ( (!locknum || (target && target.CheckKeys(locknum,false,true))) && !SWWMUtility.IsExitLine(BlockingLine) )
BlockingLine.RemoteActivate(target,wside,SPAC_Use,pos);
}
}
else if ( BlockingMobj )
{
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
}
// send the needed data for a bust
if ( (special1 == 2) || swwm_omnibust )
{
int dmg = int(oldvel.length()*4.2+heat*80);
BusterWall.ProjectileBust(self,dmg,oldvel.unit());
}
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
double bcefact = .9;
if ( BlockingMobj )
{
bcefact *= .7;
if ( !BlockingMobj.bINVULNERABLE && !BlockingMobj.bNOBLOOD && !BlockingMobj.bDORMANT )
bcefact *= .6;
}
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 *= bcefact;
// slam jam
if ( !BlockingMobj )
{
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,max(0.,(vel.length()/30.-.2))**.5);
if ( vel.length() > 15 )
{
let s = Spawn("BallImpact",pos);
s.angle = atan2(HitNormal.y,HitNormal.x);
s.pitch = asin(-HitNormal.z);
}
}
gravity = .35;
if ( (vel.length() < 5) && (pos.z <= floorz) )
{
ClearBounce();
ExplodeMissile();
}
}
States
{
Spawn:
XZW1 A -1;
Stop;
Bounce:
XZW1 A 0 A_HandleBounce();
Goto Spawn;
Death:
XZW1 A -1
{
bMOVEWITHSECTOR = true;
A_StopSound(CHAN_WEAPON);
}
Stop;
}
}
Class GExploLight : PaletteLight
{
Default
{
ReactionTime 80;
Args 0,0,0,220;
}
}
Class GExploLight2 : PaletteLight
{
Default
{
ReactionTime 60;
Args 0,0,0,120;
}
}
Class GoldenImpact : Actor
{
Default
{
DamageType "Explodium";
RenderStyle "Add";
Radius 0.1;
Height 0;
Scale 8.;
+NOGRAVITY;
+NOBLOCKMAP;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+FOILINVUL;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_AlertMonsters(swwm_uncapalert?0:40000);
SWWMUtility.DoExplosion(self,777,40000,600,500,DE_EXTRAZTHRUST);
A_QuakeEx(9,9,9,40,0,5000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollintensity:1.5);
A_StartSound("spreadgun/goldexpl",CHAN_VOICE,attenuation:.3);
A_StartSound("spreadgun/goldexpl",CHAN_WEAPON,attenuation:.15);
A_SprayDecal("WumboRocketBlast",-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,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](1,9);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224)+Color(30,25,0));
s.special1 = Random[ExploS](3,5);
s.scale *= 3.;
s.alpha *= .8;
}
numpt = Random[ExploS](8,12);
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](6,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](2,24);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](12,24);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](8,40);
let s = Spawn("FancyConfetti",pos);
s.scale *= 3.;
s.bAMBUSH = true;
s.vel = pvel;
}
Spawn("GExploLight",pos);
}
action void A_GoldSpread()
{
special1++;
if ( (special1%3) || (special1 > 20) ) return;
FLineTraceData d;
Vector3 HitNormal;
Vector3 origin;
double ang, pt;
for ( int i=0; i<6; i++ )
{
double totaldist = 30+(special1**2.)*1.2;
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,90);
origin = pos;
while ( totaldist > 0 )
{
LineTrace(ang,totaldist,pt,TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y,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;
}
totaldist -= d.Distance;
if ( totaldist > 0 )
{
Vector3 bounced = d.HitDir-(1.2*hitnormal*(d.HitDir dot HitNormal));
ang = atan2(bounced.y,bounced.x);
pt = asin(-bounced.z);
origin = d.HitLocation+hitnormal;
}
}
let p = Spawn("GoldenSubImpact",d.HitLocation+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = target;
}
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX1 AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]] 1 Bright A_GoldSpread();
TNT1 A 1
{
A_GoldSpread();
if ( special1 > 20 ) Destroy();
}
Wait;
}
}
Class GoldenSubImpact : Actor
{
Default
{
DamageType "Explodium";
RenderStyle "Add";
Scale 6.;
Alpha .8;
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+FOILINVUL;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMUtility.DoExplosion(self,77,30000,400,300,DE_EXTRAZTHRUST);
A_QuakeEx(7,7,7,20,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollintensity:.8);
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](3,8);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,9);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224)+Color(30,25,0));
s.special1 = Random[ExploS](2,3);
s.scale *= 2.2;
s.alpha *= .5;
}
numpt = Random[ExploS](4,6);
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](2,3);
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,18);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = Random[ExploS](2,5);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](8,24);
let s = Spawn("FancyConfetti",pos);
s.scale *= 2.;
s.bAMBUSH = true;
s.vel = pvel;
}
Spawn("GExploLight2",pos);
}
action void A_GoldSubSpread()
{
special1++;
if ( (special1%2) || (special1 > 10) ) return;
FLineTraceData d;
Vector3 HitNormal;
Vector3 origin;
double ang, pt;
for ( int i=0; i<3; i++ )
{
double totaldist = 20+(special1**2.)*.8;
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,90);
origin = pos;
while ( totaldist > 0 )
{
LineTrace(ang,totaldist,pt,TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y,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;
}
totaldist -= d.Distance;
if ( totaldist > 0 )
{
Vector3 bounced = d.HitDir-(1.2*hitnormal*(d.HitDir dot HitNormal));
ang = atan2(bounced.y,bounced.x);
pt = asin(-bounced.z);
origin = d.HitLocation+hitnormal;
}
}
let p = Spawn("GoldenSubSubImpact",d.HitLocation+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = target;
}
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright A_GoldSubSpread();
TNT1 A 1
{
A_GoldSubSpread();
if ( special1 > 10 ) Destroy();
}
Stop;
}
}
Class GoldenSubSubImpact : Actor
{
Default
{
DamageType "Explodium";
RenderStyle "Add";
Scale 3.;
Alpha .6;
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+NODAMAGETHRUST;
+FORCERADIUSDMG;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+FOILINVUL;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
SWWMUtility.DoExplosion(self,7,20000,200,100,DE_EXTRAZTHRUST);
A_QuakeEx(4,4,4,15,0,1000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollintensity:.4);
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](1,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224)+Color(30,25,0));
s.special1 = Random[ExploS](0,2);
s.scale *= 1.6;
s.alpha *= .2;
}
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
Stop;
}
}
Class Spreadgun : SWWMWeapon
{
bool fired; // shell was used
Class<Ammo> loadammo, nextammo; // currently loaded shell, next shell to load
transient ui TextureID WeaponBox, AmmoIcon[7], LoadedIcon[7];
transient ui Font TewiFont;
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( loadammo is 'RedShell' ) return StringTable.Localize("$O_SPREADGUN_RED");
if ( loadammo is 'GreenShell' ) return StringTable.Localize("$O_SPREADGUN_GREEN");
if ( loadammo is 'WhiteShell' ) return StringTable.Localize("$O_SPREADGUN_WHITE");
if ( loadammo is 'BlueShell' ) return StringTable.Localize("$O_SPREADGUN_BLUE");
if ( loadammo is 'BlackShell' ) return StringTable.Localize("$O_SPREADGUN_BLACK");
if ( loadammo is 'PurpleShell' ) return StringTable.Localize("$O_SPREADGUN_PURPLE");
if ( loadammo is 'GoldShell' ) return StringTable.Localize("$O_SPREADGUN_GOLD");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
if ( !WeaponBox )
{
WeaponBox = TexMan.CheckForTexture("graphics/HUD/SpreadgunDisplay.png",TexMan.Type_Any);
AmmoIcon[0] = TexMan.CheckForTexture("graphics/HUD/RedShell.png",TexMan.Type_Any);
AmmoIcon[1] = TexMan.CheckForTexture("graphics/HUD/GreenShell.png",TexMan.Type_Any);
AmmoIcon[2] = TexMan.CheckForTexture("graphics/HUD/WhiteShell.png",TexMan.Type_Any);
AmmoIcon[3] = TexMan.CheckForTexture("graphics/HUD/BlueShell.png",TexMan.Type_Any);
AmmoIcon[4] = TexMan.CheckForTexture("graphics/HUD/BlackShell.png",TexMan.Type_Any);
AmmoIcon[5] = TexMan.CheckForTexture("graphics/HUD/PurpleShell.png",TexMan.Type_Any);
AmmoIcon[6] = TexMan.CheckForTexture("graphics/HUD/GoldShell.png",TexMan.Type_Any);
LoadedIcon[0] = TexMan.CheckForTexture("graphics/HUD/LoadedRedShell.png",TexMan.Type_Any);
LoadedIcon[1] = TexMan.CheckForTexture("graphics/HUD/LoadedGreenShell.png",TexMan.Type_Any);
LoadedIcon[2] = TexMan.CheckForTexture("graphics/HUD/LoadedWhiteShell.png",TexMan.Type_Any);
LoadedIcon[3] = TexMan.CheckForTexture("graphics/HUD/LoadedBlueShell.png",TexMan.Type_Any);
LoadedIcon[4] = TexMan.CheckForTexture("graphics/HUD/LoadedBlackShell.png",TexMan.Type_Any);
LoadedIcon[5] = TexMan.CheckForTexture("graphics/HUD/LoadedPurpleShell.png",TexMan.Type_Any);
LoadedIcon[6] = TexMan.CheckForTexture("graphics/HUD/LoadedGoldShell.png",TexMan.Type_Any);
}
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
Screen.DrawTexture(WeaponBox,false,bx-54,by-43,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int ox = 6;
int oy = 11;
for ( int i=0; i<7; i++ )
{
Screen.DrawTexture(AmmoIcon[i],false,bx-ox,by-oy,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0));
String astr = String.Format("%3d",Owner.CountInv(types[i]));
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-ox-(TewiFont.StringWidth(astr)+1),by-oy-2,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(types[i]==nextammo)?Color(0,0,0,0):Color(128,0,0,0));
oy += 10;
if ( i == 3 )
{
oy = 21;
ox = 33;
}
}
for ( int i=0; i<7; i++ )
{
if ( loadammo != types[i] ) continue;
Screen.DrawTexture(LoadedIcon[i],false,bx-48,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,fired?Color(128,0,0,0):Color(0,0,0,0));
break;
}
}
override bool ReportHUDAmmo()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
for ( int i=0; i<7; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true;
return !fired;
}
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
if ( (firemode == PrimaryFire) || (firemode == AltFire) )
{
if ( !fired ) return true;
for ( int i=0; i<7; i++ ) if ( Owner.CountInv(types[i]) > 0 ) return true;
return false;
}
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
}
override bool UsesAmmo( Class<Ammo> kind )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
for ( int i=0; i<7; i++ ) if ( kind is types[i] ) return true;
return false;
}
action void A_SelectUnloadState()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
static const statelabel primedstates[] = {"UnloadRed", "UnloadGreen", "UnloadWhite", "UnloadBlue", "UnloadBlack", "UnloadPurple", "UnloadGold"};
static const statelabel firedstates[] = {"UnloadRedFired", "UnloadGreenFired", "UnloadWhiteFired", "UnloadBlueFired", "UnloadBlackFired", "UnloadPurpleFired", "UnloadGoldFired"};
int amidx = 0;
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
amidx = i;
break;
}
if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState(primedstates[amidx]));
else player.SetPSprite(PSP_WEAPON,invoker.FindState(firedstates[amidx]));
A_Overlay(-9999,"UnloadDummy");
A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP);
}
action void A_SelectLoadState()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
static const statelabel primedstates[] = {"LoadRed", "LoadGreen", "LoadWhite", "LoadBlue", "LoadBlack", "LoadPurple", "LoadGold"};
static const statelabel firedstates[] = {"LoadRedFired", "LoadGreenFired", "LoadWhiteFired", "LoadBlueFired", "LoadBlackFired", "LoadPurpleFired", "LoadGoldFired"};
int amidx = 0;
for ( int i=0; i<7; i++ )
{
if ( invoker.nextammo != types[i] ) continue;
amidx = i;
break;
}
if ( !invoker.fired ) player.SetPSprite(PSP_WEAPON,invoker.FindState(primedstates[amidx]));
else player.SetPSprite(PSP_WEAPON,invoker.FindState(firedstates[amidx]));
if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
{
let amo = FindInventory(invoker.nextammo);
if ( amo && (amo.Amount > 0) ) amo.Amount--;
}
A_Overlay(-9999,"LoadDummy");
}
action void A_DropShell()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
static const Class<Actor> casetypes[] = {"RedShellCasing","GreenShellCasing","WhiteShellCasing","BlueShellCasing","BlackShellCasing","PurpleShellCasing","GoldShellCasing"};
if ( !invoker.fired )
{
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
let amo = FindInventory(types[i]);
if ( !amo )
{
amo = Inventory(Spawn(types[i]));
amo.AttachToOwner(self);
amo.Amount = 0;
}
if ( amo.Amount < amo.MaxAmount ) amo.Amount++;
else if ( !sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) )
Spawn(types[i],Vec3Angle(5,angle,height/2));
break;
}
}
else
{
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-10*z);
let c = Spawn(casetypes[i],origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.2,.2)+y*FRandom[Junk](-.2,.2)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
break;
}
}
}
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool large = false )
{
if ( swwm_omnibust )
{
// Wall busting
int bustdmg = dmg;
if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration);
BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector,t.Results.HitPos.z);
}
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
t.ShootThroughList[i].Activate(self,0,SPAC_PCross);
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
}
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Spawn(large?"InvisibleSplasher":"SmolInvisibleSplasher",t.WaterHitList[i].hitpos);
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bc) ) continue;
let b = Actor.Spawn("SWWMBubble",level.Vec3Offset(origin,dir*i));
b.Scale *= FRandom[Boolet](.1,.3);
}
for ( int i=0; i<t.HitList.Size(); i++ )
{
int realdmg = dmg?dmg:t.HitList[i].HitDamage;
SWWMDamageAccumulator.Accumulate(t.HitList[i].HitActor,realdmg,invoker,self,'shot',!large&&!swwm_shotgib);
SWWMUtility.DoKnockback(t.HitList[i].HitActor,t.HitList[i].x+(0,0,0.025),mm*FRandom[Spreadgun](0.4,1.2));
if ( t.HitList[i].HitActor.bNOBLOOD || t.HitList[i].HitActor.bDORMANT || t.HitList[i].HitActor.bINVULNERABLE )
{
let p = Spawn(impact,t.HitList[i].HitLocation);
p.angle = atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180;
p.pitch = asin(t.HitList[i].x.z);
}
else
{
t.HitList[i].HitActor.TraceBleed(realdmg,self);
t.HitList[i].HitActor.SpawnBlood(t.HitList[i].HitLocation,atan2(t.HitList[i].x.y,t.HitList[i].x.x)+180,realdmg);
if ( large ) t.HitList[i].HitActor.A_StartSound("spreadgun/slugf",CHAN_DAMAGE,CHANF_OVERLAP,1.,2.);
else t.HitList[i].HitActor.A_StartSound("spreadgun/pelletf",CHAN_DAMAGE,CHANF_OVERLAP,.4,4.);
}
}
if ( (t.Results.HitType != TRACE_HitNone) && (t.Results.HitType != TRACE_HasHitSky) && (t.Results.HitType != TRACE_HitActor) )
{
Vector3 hitnormal = -t.Results.HitVector;
if ( t.Results.HitType == TRACE_HitFloor )
{
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.top.Normal;
else hitnormal = t.Results.HitSector.floorplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.bottom.Normal;
else hitnormal = t.Results.HitSector.ceilingplane.Normal;
}
else if ( t.Results.HitType == TRACE_HitWall )
{
hitnormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
if ( !t.Results.Side ) hitnormal *= -1;
}
let p = Spawn(impact,t.Results.HitPos+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
if ( t.Results.HitType == TRACE_HitFloor ) p.CheckSplash(40);
if ( t.Results.HitLine ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
}
override Vector3 GetTraceOffset()
{
return (10.,2.,-2.);
}
action void A_FireShell()
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
static const statelabel flashes[] = {"FlashRed","FlashGreen","FlashWhite","FlashBlue","FlashBlack","FlashPurple","FlashGold"};
static const String sounds[] = {"spreadgun/redfire","spreadgun/greenfire","spreadgun/whitefire","spreadgun/bluefire","spreadgun/blackfire","spreadgun/purplefire","spreadgun/goldfire"};
static const int louds[] = {800,1000,1100,1200,1400,600,2500};
static const int quakes[] = {3,4,2,4,3,1,6};
static const Color cols[] = {Color(40,255,192,64),Color(36,255,192,80),Color(64,255,160,32),Color(48,32,176,255),Color(72,255,128,16),Color(24,255,224,96),Color(96,255,224,16)};
for ( int i=0; i<7; i++ )
{
if ( invoker.loadammo != types[i] ) continue;
A_SWWMFlash(flashes[i]);
A_StartSound(sounds[i],CHAN_WEAPON,CHANF_OVERLAP,attenuation:.6);
A_AlertMonsters(swwm_uncapalert?0:louds[i]);
A_QuakeEx(quakes[i],quakes[i],quakes[i],9,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.2*quakes[i]);
A_ZoomFactor(1.-quakes[i]*.04,ZOOM_INSTANT);
A_ZoomFactor(1.);
A_PlayerFire();
SWWMHandler.DoFlash(self,cols[i],5);
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-2*z);
Vector3 x2, y2, z2;
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
double a, s;
Vector3 dir;
SpreadgunTracer st;
SpreadSlugTracer sst;
switch ( i )
{
case 1:
sst = new("SpreadSlugTracer");
sst.ignoreme = self;
sst.penetration = 120.;
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.01);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
sst.hitlist.Clear();
sst.shootthroughlist.Clear();
sst.waterhitlist.Clear();
sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky);
ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1,true);
for ( int i=0; i<3; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= 0.5;
}
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .8;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.);
}
for ( int i=0; i<10; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .2;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
SWWMUtility.DoKnockback(self,-x,25000.);
break;
case 2:
for ( int j=0; j<3; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.24);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
let p = Spawn("DragonBreathArm",origin);
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
}
for ( int i=0; i<5; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= .2;
}
for ( int i=0; i<15; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<12; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMUtility.DoKnockback(self,-x,13000.);
break;
case 3:
for ( int j=0; j<8; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.8);
let b = Spawn("SaltBeam",level.Vec3Offset(origin,y*cos(a)*s+z*sin(a)*s));
b.target = self;
b.angle = atan2(x2.y,x2.x);
b.pitch = asin(-x2.z);
}
for ( int i=0; i<9; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,3,4)*Random[Spreadgun](32,63));
s.A_SetRenderStyle(.2,STYLE_AddShaded);
}
for ( int i=0; i<16; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.SetShade(Color(1,3,4)*Random[Spreadgun](32,63));
s.A_SetRenderStyle(.3,STYLE_AddShaded);
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<20; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMUtility.DoKnockback(self,-x,23000.);
break;
case 4:
for ( int j=0; j<10; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.2);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
let p = Spawn("FlamingChunk",origin);
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed*FRandom[Spreadgun](.9,1.3);
}
for ( int i=0; i<12; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](48,128));
s.alpha *= .2;
}
for ( int i=0; i<30; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= 1.3;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](48,128));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,12.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<20; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,12.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMUtility.DoKnockback(self,-x,14000.);
break;
case 5:
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.03);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
let b = Spawn("TheBall",origin);
b.target = self;
b.angle = atan2(dir.y,dir.x);
b.pitch = asin(-dir.z);
b.vel = dir*b.speed;
for ( int i=0; i<4; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= 0.4;
}
for ( int i=0; i<8; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .6;
s.alpha *= .25;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.);
}
for ( int i=0; i<8; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .2;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
SWWMUtility.DoKnockback(self,-x,9500.);
break;
case 6:
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.01);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
FLineTraceData d;
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
SWWMBulletTrail.DoTrail(self,origin,dir,10000,2,true);
if ( d.HitType != TRACE_HitNone )
{
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;
}
let p = Spawn("SlugImpact",d.HitLocation+hitnormal);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
let b = Spawn("GoldenImpact",d.HitLocation+hitnormal*4.);
b.angle = atan2(hitnormal.y,hitnormal.x);
b.pitch = asin(-hitnormal.z);
b.target = self;
}
for ( int i=0; i<3; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= 0.5;
}
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .8;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.);
}
for ( int i=0; i<10; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .2;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<50; i++ )
{
let s = Spawn("FancyConfetti",origin);
s.bAMBUSH = true;
s.vel += vel*.5+x*FRandom[Spreadgun](1.,20.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMUtility.DoKnockback(self,-x,30000.);
break;
default:
st = new("SpreadgunTracer");
st.ignoreme = self;
for ( int j=0; j<20; j++ )
{
a = FRandom[Spreadgun](0,360);
s = FRandom[Spreadgun](0,.22);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
st.hitlist.Clear();
st.shootthroughlist.Clear();
st.waterhitlist.Clear();
st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky);
ProcessTraceHit(st,origin,dir,5,7000,bc:5);
}
for ( int i=0; i<9; i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
SWWMViewSmoke(s).ofs = (15,3,-3);
s.target = self;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.alpha *= .2;
}
for ( int i=0; i<16; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Spreadgun](96,192));
s.vel += vel*.5+x*FRandom[Spreadgun](3.,5.)+y*FRandom[Spreadgun](-1,1)+z*FRandom[Spreadgun](-1,1);
}
for ( int i=0; i<20; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Spreadgun](4.,8.)+y*FRandom[Spreadgun](-2,2)+z*FRandom[Spreadgun](-2,2);
}
SWWMUtility.DoKnockback(self,-x,20000.);
break;
}
break;
}
A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP);
invoker.fired = true;
}
action void A_LoadShell()
{
A_StartSound("spreadgun/shellin",CHAN_WEAPON,CHANF_OVERLAP);
invoker.loadammo = invoker.nextammo;
}
action void A_Prime()
{
if ( invoker.fired )
{
A_StartSound("spreadgun/hammer",CHAN_WEAPON,CHANF_OVERLAP);
invoker.fired = false;
}
}
override void AttachToOwner( Actor other )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
Super.AttachToOwner(other);
if ( !loadammo ) loadammo = "RedShell";
for ( int i=0; i<7; i++ )
{
Ammo a = Ammo(other.FindInventory(types[i]));
if ( !a ) continue;
nextammo = types[i];
return;
}
nextammo = AmmoType1;
}
action void A_SwitchAmmoType( bool rev = false )
{
static const Class<Ammo> types[] = {"RedShell","GreenShell","WhiteShell","BlueShell","BlackShell","PurpleShell","GoldShell"};
int cur = 0, next = 0;
for ( int i=0; i<7; i++ )
{
if ( invoker.nextammo != types[i] ) continue;
cur = i;
break;
}
int ridx = -1;
if ( rev )
{
// check backwards from what we currently had
for ( int i=cur; i>=0; i-- )
{
if ( CountInv(types[i]) <= 0 ) continue;
ridx = i;
break;
}
if ( ridx == -1 )
{
// check forwards instead, but avoid golden shells
for ( int i=0; i<6; i++ )
{
if ( CountInv(types[i]) <= 0 ) continue;
ridx = i;
break;
}
}
if ( ridx != -1 ) next = ridx;
}
else
{
for ( int i=0; i<7; i++ )
{
ridx = (i+cur+1)%7;
if ( CountInv(types[ridx]) <= 0 ) continue;
next = ridx;
break;
}
}
if ( invoker.nextammo != types[next] ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL);
invoker.nextammo = types[next];
A_WeaponReady(WRF_NOFIRE);
}
action void A_AltHold()
{
A_WeaponReady(WRF_NOFIRE);
if ( player.cmd.buttons&BT_ALTATTACK ) return;
if ( !invoker.fired ) player.SetPSPrite(PSP_WEAPON,invoker.FindState("Ready"));
else player.SetPSPrite(PSP_WEAPON,invoker.FindState("ReadyFired"));
}
override void ModifyDropAmount( int dropamount )
{
Super.ModifyDropAmount(dropamount);
// toss some ammo while we're at it
if ( Random[Spreadgun](0,1) )
A_DropItem(Random[Spreadgun](0,2)?"RedShell":"GreenShell",Random[Spreadgun](1,2));
}
Default
{
Tag "$T_SPREADGUN";
Inventory.PickupMessage "$I_SPREADGUN";
Obituary "$O_SPREADGUN";
Inventory.Icon "graphics/HUD/Icons/W_Spreadgun.png";
Weapon.UpSound "spreadgun/select";
Weapon.SlotNumber 3;
Weapon.SelectionOrder 500;
Weapon.AmmoType1 "RedShell";
Weapon.AmmoGive1 1;
//SWWMWeapon.SwapWeapon "PuntzerBeta";
SWWMWeapon.DropAmmoType "Clip";
Stamina 15000;
+SWWMWEAPON.NOFIRSTGIVE;
Radius 10;
Height 24;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Deselect:
XZW2 A 1
{
A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP);
return A_JumpIf(invoker.fired,"DeselectFired");
}
XZW2 BCDEFGHI 1;
XZW2 I -1 A_FullLower();
Stop;
DeselectFired:
XZW2 Z 1;
XZW3 ABCDEFGH 1;
XZW3 H -1 A_FullLower();
Stop;
Select:
XZW2 I 1
{
A_FullRaise();
return A_JumpIf(invoker.fired,"SelectFired");
}
XZW2 JKLMNOPQ 1;
Goto Ready;
SelectFired:
XZW3 HIJKLMNOP 1;
Goto ReadyFired;
Ready:
XZW2 A 1
{
if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true);
int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1;
if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) && (invoker.loadammo != invoker.nextammo) )
flg |= WRF_ALLOWRELOAD;
A_WeaponReady(flg);
}
Wait;
ReadyFired:
XZW2 Z 1
{
if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true);
int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1;
if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) )
flg |= WRF_ALLOWRELOAD;
else flg |= WRF_NOPRIMARY;
A_WeaponReady(flg);
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) )
invoker.CheckAmmo(EitherFire,true);
}
Wait;
Fire:
#### # 1
{
if ( invoker.fired ) return ResolveState("Reload");
A_FireShell();
return ResolveState(null);
}
XZW2 RSTU 1;
XZW2 VWXY 2;
Goto ReadyFired;
AltFire:
#### # 1 A_SwitchAmmoType();
#### # 1 A_AltHold();
Wait;
Reload:
#### # 1
{
A_PlayerReload();
A_SelectUnloadState();
}
Stop;
UnloadDummy: // overlay with shared functions for all unload anims
TNT1 A 11;
TNT1 A 14 A_StartSound("spreadgun/open",CHAN_WEAPON,CHANF_OVERLAP);
TNT1 A 1 A_DropShell();
Stop;
UnloadRedFired:
XZW2 Z 2;
XZW3 QRST 2;
XZW3 UVWXYZ 1;
XZW4 ABCDEFGH 1;
XZW8 M 1;
Goto Reload2;
UnloadGreenFired:
XZW2 Z 2;
XZW4 IJKL 2;
XZW4 MNOPQRSTUVWXYZ 1;
XZW9 T 1;
Goto Reload2;
UnloadWhiteFired:
XZW2 Z 2;
XZW5 ABCD 2;
XZW5 EFGHIJKLMNOPQR 1;
XZWB A 1;
Goto Reload2;
UnloadBlueFired:
XZW2 Z 2;
XZW5 STUV 2;
XZW5 WXYZ 1;
XZW6 ABCDEFGHIJ 1;
XZWC H 1;
Goto Reload2;
UnloadBlackFired:
XZW2 Z 2;
XZW6 KLMN 2;
XZW6 OPQRSTUVWXYZ 1;
XZW7 AB 1;
XZWD O 1;
Goto Reload2;
UnloadPurpleFired:
XZW2 Z 2;
XZW7 CDEF 2;
XZW7 GHIJKLMNOPQRST 1;
XZWE V 1;
Goto Reload2;
UnloadGoldFired:
XZW2 Z 2;
XZW7 UVWX 2;
XZW7 YZ 1;
XZW8 ABCDEFGHIJKL 1;
XZWG C 1;
Goto Reload2;
UnloadRed:
XZW2 A 2;
XZWK JKLM 2;
XZWK NOPQRSTUVWXYZ 1;
XZWL A 1;
XZWP F 1;
Goto Reload2;
UnloadGreen:
XZW2 A 2;
XZWL BCDE 2;
XZWL FGHIJKLMNOPQRS 1;
XZWQ M 1;
Goto Reload2;
UnloadWhite:
XZW2 A 2;
XZWL TUVW 2;
XZWL XYZ 1;
XZWM ABCDEFGHIJK 1;
XZWR T 1;
Goto Reload2;
UnloadBlue:
XZW2 A 2;
XZWM LMNO 2;
XZWM PQRSTUVWXYZ 1;
XZWN ABC 1;
XZWT A 1;
Goto Reload2;
UnloadBlack:
XZW2 A 2;
XZWN DEFG 2;
XZWN HIJKLMNOPQRSTU 1;
XZWU H 1;
Goto Reload2;
UnloadPurple:
XZW2 A 2;
XZWN VWXY 2;
XZWN Z 1;
XZWO ABCDEFGHIJKLM 1;
XZWV O 1;
Goto Reload2;
UnloadGold:
XZW2 A 2;
XZWO NOPQ 2;
XZWO RSTUVWXYZ 1;
XZWP ABCDE 1;
XZWW V 1;
Goto Reload2;
Reload2:
#### # 1 A_SelectLoadState();
Stop;
LoadDummy: // overlay with shared functions for all load anims
TNT1 A 9;
TNT1 A 12 A_LoadShell();
TNT1 A 2 A_StartSound("spreadgun/close",CHAN_WEAPON,CHANF_OVERLAP);
TNT1 A 2 A_Prime();
TNT1 A 1 { invoker.PlayUpSound(self); }
Stop;
LoadRedFired:
XZW8 MNOPQRSTUVWXYZ 1;
XZW9 ABCDEFGHIJKLMNOPQRS 1;
Goto Ready;
LoadGreenFired:
XZW9 TUVWXYZ 1;
XZWA ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
Goto Ready;
LoadWhiteFired:
XZWB ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
XZWC ABCDEFG 1;
Goto Ready;
LoadBlueFired:
XZWC HIJKLMNOPQRSTUVWXYZ 1;
XZWD ABCDEFGHIJKLMN 1;
Goto Ready;
LoadBlackFired:
XZWD OPQRSTUVWXYZ 1;
XZWE ABCDEFGHIJKLMNOPQRSTU 1;
Goto Ready;
LoadPurpleFired:
XZWE VWXYZ 1;
XZWF ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
XZWG AB 1;
Goto Ready;
LoadGoldFired:
XZWG CDEFGHIJKLMNOPQRSTUVWXYZ 1;
XZWH ABCDEFGHI 1;
Goto Ready;
LoadRed:
XZWP FGHIJKLMNOPQRSTUVWXYZ 1;
XZWQ ABCDEFGHIJKL 1;
Goto Ready;
LoadGreen:
XZWQ MNOPQRSTUVWXYZ 1;
XZWR ABCDEFGHIJKLMNOPQRS 1;
Goto Ready;
LoadWhite:
XZWR TUVWXYZ 1;
XZWS ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
Goto Ready;
LoadBlue:
XZWT ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
XZWU ABCDEFG 1;
Goto Ready;
LoadBlack:
XZWU HIJKLMNOPQRSTUVWXYZ 1;
XZWV ABCDEFGHIJKLMN 1;
Goto Ready;
LoadPurple:
XZWV OPQRSTUVWXYZ 1;
XZWW ABCDEFGHIJKLMNOPQRSTU 1;
Goto Ready;
LoadGold:
XZWW VWXYZ 1;
XZWX ABCDEFGHIJKLMNOPQRSTUVWXYZ 1;
XZWY AB 1;
Goto Ready;
Zoom:
XZW2 A 1
{
A_StartSound("spreadgun/checkgun",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerCheckGun();
return A_JumpIf(invoker.fired,"ZoomFired");
}
XZWH JKLMNOPQRST 1;
XZWH UVWXYZ 2;
XZWI ABC 2;
XZWI DEFGHI 1;
Goto Ready;
ZoomFired:
XZW2 Z 1;
XZWI WXYZ 1;
XZWJ ABCDEFG 1;
XZWJ HIJKLMNOP 2;
XZWJ QRSTUV 1;
Goto ReadyFired;
DummyMelee:
TNT1 A 3
{
A_Parry(9);
A_PlayerMelee(true);
}
TNT1 A 1 A_Melee();
Stop;
User1:
XZW2 A 2
{
A_StartSound("spreadgun/deselect",CHAN_WEAPON,CHANF_OVERLAP);
return A_JumpIf(invoker.fired,"User1Fired");
}
XZWI JK 2;
User1Hold:
XZWI L 1
{
A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP);
A_Overlay(-9999,"DummyMelee");
}
XZWI MNOP 2;
XZWI QR 3;
XZWI S 0 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold");
XZWI S 0 { invoker.PlayUpSound(self); }
XZWI STUV 2;
Goto Ready;
User1Fired:
XZW2 Z 2;
XZWJ WX 2;
User1FiredHold:
XZWJ Y 1
{
A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP);
A_Overlay(-9999,"DummyMelee");
}
XZWJ Z 2;
XZWK ABC 2;
XZWK DE 3;
XZWK F 0 A_JumpIf(player.cmd.buttons&BT_USER1,"User1FiredHold");
XZWK F 0 { invoker.PlayUpSound(self); }
XZWK FGHI 2;
Goto ReadyFired;
FlashRed:
XZWZ A 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 120;
l.target = self;
}
Stop;
FlashGreen:
XZWZ B 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 90;
l.target = self;
}
Stop;
FlashWhite:
XZWZ C 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[1] = 176;
l.args[2] = 32;
l.args[3] = 160;
l.target = self;
}
Stop;
FlashBlue:
XZWZ D 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[0] = 96;
l.args[1] = 224;
l.args[2] = 255;
l.args[3] = 160;
l.target = self;
}
Stop;
FlashBlack:
XZWZ E 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[1] = 144;
l.args[2] = 16;
l.args[3] = 280;
l.target = self;
}
Stop;
FlashPurple:
XZWZ F 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 60;
l.target = self;
}
Stop;
FlashGold:
XZWZ G 2 Bright
{
let l = Spawn("SWWMWeaponLight",pos);
l.args[3] = 300;
l.target = self;
}
Stop;
}
}