Vanilla Centaurs still take damage from being set on fire while shielded. Expanded CLONK library (11 different stock sounds, not just coconut). Added Dark Souls parry sound easter egg. Add Demolitionist to Doom 2 cast call screen (awkward, but works). Adjust Hexen end screen, fit text better.
2840 lines
75 KiB
Text
2840 lines
75 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 SWWMDamageAccumulator : Thinker
|
|
{
|
|
Actor victim, inflictor, source;
|
|
Array<Int> amounts;
|
|
int total;
|
|
Name type;
|
|
bool dontgib;
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
// so many damn safeguards in this
|
|
if ( !victim )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
int gibhealth = victim.GetGibHealth();
|
|
// おまえはもう死んでいる
|
|
if ( (victim.health-total <= gibhealth) && !dontgib )
|
|
{
|
|
// safeguard for inflictors that have somehow ceased to exist, which apparently STILL CAN HAPPEN
|
|
if ( inflictor ) inflictor.bEXTREMEDEATH = true;
|
|
else type = 'Extreme';
|
|
}
|
|
// make sure accumulation isn't reentrant
|
|
if ( inflictor && (inflictor is 'EvisceratorChunk') ) inflictor.bAMBUSH = true;
|
|
// 何?
|
|
for ( int i=0; i<amounts.Size(); i++ )
|
|
{
|
|
if ( !victim ) break;
|
|
victim.DamageMobj(inflictor,source,amounts[i],type,DMG_THRUSTLESS);
|
|
}
|
|
// clean up
|
|
if ( inflictor )
|
|
{
|
|
if ( inflictor is 'EvisceratorChunk' ) inflictor.bAMBUSH = false;
|
|
inflictor.bEXTREMEDEATH = false;
|
|
}
|
|
Destroy();
|
|
}
|
|
|
|
static void Accumulate( Actor victim, int amount, Actor inflictor, Actor source, Name type, bool dontgib = false )
|
|
{
|
|
if ( !victim ) return;
|
|
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
|
|
SWWMDamageAccumulator a, match = null;
|
|
while ( a = SWWMDamageAccumulator(ti.Next()) )
|
|
{
|
|
if ( a.victim != victim ) continue;
|
|
match = a;
|
|
break;
|
|
}
|
|
if ( !match )
|
|
{
|
|
match = new("SWWMDamageAccumulator");
|
|
match.ChangeStatNum(STAT_USER);
|
|
match.victim = victim;
|
|
match.amounts.Clear();
|
|
}
|
|
match.amounts.Push(amount);
|
|
match.total += amount;
|
|
match.inflictor = inflictor;
|
|
match.source = source;
|
|
match.type = type;
|
|
match.dontgib = dontgib;
|
|
}
|
|
|
|
static clearscope int GetAmount( Actor victim )
|
|
{
|
|
let ti = ThinkerIterator.Create("SWWMDamageAccumulator",STAT_USER);
|
|
SWWMDamageAccumulator a, match = null;
|
|
while ( a = SWWMDamageAccumulator(ti.Next()) )
|
|
{
|
|
if ( a.victim != victim ) continue;
|
|
return a.total;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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_Abort;
|
|
}
|
|
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;
|
|
ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration));
|
|
hitlist.Push(ent);
|
|
penetration = max(0,penetration-ent.hitdamage);
|
|
if ( penetration <= 0 ) return TRACE_Abort;
|
|
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;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_StartSound("spreadgun/pellet",CHAN_VOICE,CHANF_DEFAULT,.4,4.);
|
|
A_SprayDecal("TinyBulletChip",-20);
|
|
int numpt = Random[Spreadgun](2,4);
|
|
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);
|
|
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,5);
|
|
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;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_StartSound("spreadgun/slug",CHAN_VOICE,CHANF_DEFAULT,1.,2.);
|
|
A_SprayDecal("BulletChip",-20);
|
|
int numpt = Random[Spreadgun](6,12);
|
|
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](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("SWWMSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Spreadgun](10,15);
|
|
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;
|
|
}
|
|
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';
|
|
+NOBLOCKMAP;
|
|
+THRUACTORS;
|
|
+BOUNCEONWALLS;
|
|
+BOUNCEONFLOORS;
|
|
+BOUNCEONCEILINGS;
|
|
+USEBOUNCESTATE;
|
|
+MISSILE;
|
|
+NODAMAGETHRUST;
|
|
+FORCERADIUSDMG;
|
|
+NOTELEPORT;
|
|
-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.,24.);
|
|
}
|
|
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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = CurSector.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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CurSector.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(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,.8))+vel);
|
|
bHITOWNER = true;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1
|
|
{
|
|
if ( waterlevel > 0 ) ReactionTime -= 2;
|
|
A_CountDown();
|
|
let p = Spawn("DragonBreathPuff",pos);
|
|
p.alpha *= .6+.4*(ReactionTime/20.);
|
|
p.scale *= 3.5-2.5*(ReactionTime/20.);
|
|
if ( !(ReactionTime%3) )
|
|
{
|
|
let l = Spawn("PaletteLight",pos);
|
|
l.Args[3] = int(90+50*(ReactionTime/20.));
|
|
l.ReactionTime = int(2+8*(ReactionTime/20.));
|
|
l.target = p;
|
|
}
|
|
if ( !(GetAge()%2) )
|
|
{
|
|
SWWMHandler.DoBlast(self,150-6*reactiontime,1000+200*reactiontime,bHITOWNER?null:target);
|
|
A_Explode(1+reactiontime/2,150-6*reactiontime,bHITOWNER?XF_HURTSOURCE:0);
|
|
}
|
|
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("SWWMSmoke",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;
|
|
}
|
|
}
|
|
}
|
|
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 "SaltTail";
|
|
ReactionTime 30;
|
|
Args 0,0,0,120;
|
|
}
|
|
}
|
|
Class SaltLight2 : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "SaltExpl";
|
|
ReactionTime 30;
|
|
Args 0,0,0,70;
|
|
}
|
|
}
|
|
|
|
Class SaltImpact : Actor
|
|
{
|
|
Default
|
|
{
|
|
Obituary "$O_SPREADGUN_BLUE";
|
|
DamageType "Electricity";
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
Scale 1.8;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+NODAMAGETHRUST;
|
|
+FORCERADIUSDMG;
|
|
+FORCEXYBILLBOARD;
|
|
+NOTELEPORT;
|
|
+FOILINVUL;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_AlertMonsters();
|
|
SWWMHandler.DoBlast(self,120,15000);
|
|
A_Explode(25,120);
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
}
|
|
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 "Electricity";
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
Stamina 3;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NODAMAGETHRUST;
|
|
+FORCERADIUSDMG;
|
|
+FOILINVUL;
|
|
}
|
|
|
|
void SpreadOut()
|
|
{
|
|
A_AlertMonsters(1200);
|
|
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);
|
|
for ( int i=0; i<t.WaterHitList.Size(); i++ )
|
|
{
|
|
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
|
|
b.A_CheckTerrain();
|
|
}
|
|
for ( int i=16; i<t.Results.Distance; i+=32 )
|
|
{
|
|
if ( !Random[Spreadgun](0,Stamina) ) continue;
|
|
let b = Actor.Spawn("SWWMSmoke",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 ( t.Results.HitType == TRACE_HitActor )
|
|
{
|
|
SWWMHandler.DoKnockback(t.Results.HitActor,x,25000);
|
|
t.Results.HitActor.DamageMobj(self,target,60,'Salt',DMG_THRUSTLESS);
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
return;
|
|
}
|
|
// next beam
|
|
if ( !(special2%2) && !Random[Spreadgun](0,4) )
|
|
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.SetStateLabel("TrailSpawn");
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_StartSound("spreadgun/salttrail",CHAN_VOICE,CHANF_DEFAULT,.3,4.);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
A_FadeOut(.04);
|
|
if ( Random[Spreadgun](-2,GetAge()/10) == 0 )
|
|
{
|
|
SWWMHandler.DoBlast(self,64,5000,target);
|
|
A_Explode(5,64,0);
|
|
}
|
|
if ( ((special2%5) || GetAge()) && !special1 ) SpreadOut();
|
|
}
|
|
|
|
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
|
|
{
|
|
Actor victim, instigator, lite;
|
|
int amount, cnt, delay;
|
|
double oangle;
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !victim )
|
|
{
|
|
A_StopSound(CHAN_5);
|
|
A_StopSound(CHAN_6);
|
|
A_StopSound(CHAN_7);
|
|
Destroy();
|
|
return;
|
|
}
|
|
SetOrigin(victim.pos,true);
|
|
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);
|
|
A_StopSound(CHAN_6);
|
|
A_StopSound(CHAN_7);
|
|
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*.1),1,30),'Fire',flg); // need to use this actor as inflictor to have a proper obituary
|
|
}
|
|
if ( !victim )
|
|
{
|
|
A_StopSound(CHAN_5);
|
|
A_StopSound(CHAN_6);
|
|
A_StopSound(CHAN_7);
|
|
Destroy();
|
|
return;
|
|
}
|
|
}
|
|
double mult = max(victim.radius,victim.height)/30.;
|
|
if ( victim is 'FlamingChunk' ) mult *= 20.-victim.special1*3.;
|
|
A_SoundVolume(CHAN_5,(mult*amount/80.));
|
|
A_SoundVolume(CHAN_6,(mult*amount/200.));
|
|
A_SoundVolume(CHAN_7,(mult*amount/500.));
|
|
if ( delay > 0 ) delay--;
|
|
if ( (level.maptime+special1)%5 ) return;
|
|
int numpt = clamp(int(Random[FlameT](2,4)*amount*0.02),1,4);
|
|
numpt = int(clamp(numpt*mult**.5,1,5));
|
|
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.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("SWWMSmoke",pos);
|
|
s.scale *= max(1.,1.6*mult);
|
|
s.alpha *= min(amount+30,100)*0.02;
|
|
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) ) continue;
|
|
int amt = max(1,amount/10);
|
|
if ( IsOnFire(t) ) amt = min(5,amt);
|
|
Apply(t,instigator,amt);
|
|
}
|
|
}
|
|
|
|
static OnFire Apply( Actor victim, Actor instigator, int amount, int delay = 0 )
|
|
{
|
|
if ( amount <= 0 ) return null;
|
|
let ti = ThinkerIterator.Create("OnFire");
|
|
OnFire t;
|
|
while ( t = OnFire(ti.Next()) )
|
|
{
|
|
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/flamelow",CHAN_5,CHANF_LOOP,attenuation:2.4);
|
|
t.A_StartSound("spreadgun/flamemed",CHAN_6,CHANF_LOOP,attenuation:1.6);
|
|
t.A_StartSound("spreadgun/flamehigh",CHAN_7,CHANF_LOOP,attenuation:.8);
|
|
double mult = max(victim.radius,victim.height)/30.;
|
|
if ( victim is 'FlamingChunk' ) mult *= 20.-victim.special1*3.;
|
|
t.A_SoundVolume(CHAN_5,(mult*amount/80.));
|
|
t.A_SoundVolume(CHAN_6,(mult*amount/200.));
|
|
t.A_SoundVolume(CHAN_7,(mult*amount/500.));
|
|
// for chunks
|
|
t.delay = delay;
|
|
t.lite = Actor.Spawn("OnFireLight",victim.pos);
|
|
OnFireLight(t.lite).of = t;
|
|
t.oangle = victim.angle;
|
|
return t;
|
|
}
|
|
|
|
static bool IsOnFire( Actor victim )
|
|
{
|
|
let ti = ThinkerIterator.Create("OnFire");
|
|
OnFire t;
|
|
while ( t = OnFire(ti.Next()) )
|
|
{
|
|
if ( t.victim != victim ) continue;
|
|
return (t.amount>0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Default
|
|
{
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOEXTREMEDEATH;
|
|
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()
|
|
{
|
|
if ( waterlevel > 0 )
|
|
vel *= 0.9;
|
|
else
|
|
{
|
|
vel *= 0.98;
|
|
vel.z += 0.2*abs(scale.x);
|
|
}
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let s = Spawn("SWWMSmoke",pos);
|
|
s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2));
|
|
s.vel += vel*0.3;
|
|
s.alpha *= alpha*4;
|
|
s.scale *= 0.5+abs(scale.x)*(.5+GetAge()/6.);
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( !Random[FlameT](0,int(40*(default.alpha-alpha))) )
|
|
{
|
|
let s = Spawn("SWWMSmoke",pos);
|
|
s.vel = (FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2),FRandom[FlameT](-0.2,0.2));
|
|
s.vel += vel*0.3;
|
|
s.alpha *= alpha*4;
|
|
s.scale *= 0.5+abs(scale.x)*(.5+GetAge()/6.);
|
|
}
|
|
}
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Speed 2;
|
|
Radius 4;
|
|
Height 4;
|
|
Alpha 0.3;
|
|
Scale 0.8;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+NOFRICTION;
|
|
+SLIDESONWALLS;
|
|
+ACTIVATEPCROSS;
|
|
+ACTIVATEIMPACT;
|
|
+NOTELEPORT;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+DROPOFF;
|
|
+NOBLOCKMONST;
|
|
+DONTSPLASH;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XFLM ABCDEFGHIJKLMNOPQRST 1 Bright
|
|
{
|
|
A_Flame();
|
|
A_SetScale(scale.x*0.98);
|
|
A_FadeOut(0.01);
|
|
vel.z += 0.1;
|
|
}
|
|
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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = CurSector.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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CurSector.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;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
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";
|
|
+NOBLOCKMAP;
|
|
+BOUNCEONWALLS;
|
|
+BOUNCEONFLOORS;
|
|
+BOUNCEONCEILINGS;
|
|
+USEBOUNCESTATE;
|
|
+DONTBOUNCEONSKY;
|
|
+MISSILE;
|
|
+NODAMAGETHRUST;
|
|
+NOTELEPORT;
|
|
-NOGRAVITY;
|
|
Speed 80;
|
|
Gravity 0.1;
|
|
BounceFactor 1.0;
|
|
Radius 2;
|
|
Height 2;
|
|
}
|
|
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.2+heat*80);
|
|
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_StartSound(snd,CHAN_FOOTSTEP,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_FOOTSTEP,CHANF_OVERLAP,1.,.2);
|
|
slamforce *= 4;
|
|
dmg *= 4;
|
|
vel *= 1.1;
|
|
let numpt = Random[Spreadgun](40,50);
|
|
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;
|
|
}
|
|
SWWMHandler.DoKnockback(victim,vel.unit(),slamforce);
|
|
bool bleeds = (victim && !victim.bINVULNERABLE && !victim.bNOBLOOD && is_schutt);
|
|
dmg = 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);
|
|
victim.A_StartSound("spreadgun/ballf",CHAN_FOOTSTEP,CHANF_OVERLAP,(vel.length()/30.)**.5);
|
|
victim.TraceBleed(dmg,self);
|
|
SpawnBlood(pos,atan2(dir.y,dir.x),dmg);
|
|
}
|
|
else
|
|
{
|
|
A_StartSound("spreadgun/ball",CHAN_VOICE,CHANF_OVERLAP,(vel.length()/30.)**.5);
|
|
victim.A_StartSound("spreadgun/ball",CHAN_FOOTSTEP,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);
|
|
}
|
|
}
|
|
// 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_LOOPING,.6,3.,2.);
|
|
heat = 1.;
|
|
}
|
|
override void Tick()
|
|
{
|
|
oldvel = vel;
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( InStateSequence(CurState,ResolveState("Death")) )
|
|
{
|
|
deadtimer++;
|
|
if ( deadtimer > 300 ) A_FadeOut(0.05);
|
|
return;
|
|
}
|
|
heat -= 0.004+0.0004*vel.length();
|
|
A_SoundVolume(CHAN_WEAPON,vel.length()/75.);
|
|
if ( heat <= 0 ) return;
|
|
let s = Spawn("SWWMSmoke",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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = CurSector.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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CurSector.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 = 1;
|
|
if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
|
{
|
|
wside = 0;
|
|
HitNormal *= -1;
|
|
}
|
|
if ( (oldvel.length() > 15) )
|
|
{
|
|
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();
|
|
}
|
|
// 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 )
|
|
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;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_AlertMonsters();
|
|
SWWMHandler.DoBlast(self,200,120000);
|
|
A_Explode(7777,200);
|
|
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("WumboScorch",-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-(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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_AlertMonsters();
|
|
SWWMHandler.DoBlast(self,200,90000);
|
|
A_Explode(777,200);
|
|
A_QuakeEx(7,7,7,20,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollintensity:.8);
|
|
A_SprayDecal("BigScorch",-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-(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;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_AlertMonsters();
|
|
SWWMHandler.DoBlast(self,200,60000);
|
|
A_Explode(77,200);
|
|
A_QuakeEx(4,4,4,15,0,1000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollintensity:.4);
|
|
A_SprayDecal("Scorch",-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;
|
|
}
|
|
}
|
|
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
|
|
double srecoil;
|
|
|
|
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-55,by-44,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
int ox = 7;
|
|
int oy = 12;
|
|
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 = 22;
|
|
ox = 34;
|
|
}
|
|
}
|
|
for ( int i=0; i<7; i++ )
|
|
{
|
|
if ( loadammo != types[i] ) continue;
|
|
Screen.DrawTexture(LoadedIcon[i],false,bx-49,by-9,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) )
|
|
{
|
|
TakeInventory(invoker.nextammo,1);
|
|
}
|
|
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;
|
|
if ( !GiveInventory(types[i],1) ) 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 )
|
|
{
|
|
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
|
t.ShootThroughList[i].Activate(self,0,SPAC_PCross);
|
|
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);
|
|
SWWMHandler.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.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_BODY,CHANF_OVERLAP,1.,2.);
|
|
else t.HitList[i].HitActor.A_StartSound("spreadgun/pelletf",CHAN_BODY,CHANF_OVERLAP,.4,4.);
|
|
}
|
|
}
|
|
if ( (t.Results.HitType != TRACE_HitNone) && (t.Results.HitType != TRACE_HasHitSky) )
|
|
{
|
|
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.HitLine ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
|
|
}
|
|
}
|
|
|
|
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(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.);
|
|
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 = 250.;
|
|
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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,6000.);
|
|
A_Recoil(2.);
|
|
invoker.srecoil = -.8;
|
|
break;
|
|
case 2:
|
|
for ( int j=0; j<9; 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("DragonBreathArm",origin);
|
|
p.target = self;
|
|
p.angle = atan2(dir.y,dir.x);
|
|
p.pitch = asin(-dir.z);
|
|
}
|
|
for ( int i=0; i<7; 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<20; 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<15; 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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,2000.);
|
|
A_Recoil(.4);
|
|
invoker.srecoil = -.15;
|
|
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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,4000.);
|
|
A_Recoil(1.4);
|
|
invoker.srecoil = -.5;
|
|
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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,3500.);
|
|
A_Recoil(.7);
|
|
invoker.srecoil = -.24;
|
|
break;
|
|
case 5:
|
|
a = FRandom[Spreadgun](0,360);
|
|
s = FRandom[Spreadgun](0,.03);
|
|
let b = Spawn("TheBall",origin);
|
|
b.target = self;
|
|
b.angle = atan2(x2.y,x2.x);
|
|
b.pitch = asin(-x2.z);
|
|
b.vel = x2*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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,2500.);
|
|
A_Recoil(1.);
|
|
invoker.srecoil = -.35;
|
|
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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,12000.);
|
|
A_Recoil(3.6);
|
|
invoker.srecoil = -2.;
|
|
break;
|
|
default:
|
|
st = new("SpreadgunTracer");
|
|
st.ignoreme = self;
|
|
for ( int j=0; j<30; 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,6,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);
|
|
}
|
|
SWWMHandler.DoKnockback(self,-x,4000.);
|
|
A_Recoil(1.4);
|
|
invoker.srecoil = -.5;
|
|
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;
|
|
}
|
|
if ( CountInv(invoker.nextammo) <= 0 ) A_SwitchAmmoType(true);
|
|
}
|
|
|
|
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;
|
|
}
|
|
for ( int i=0; i<7; i++ )
|
|
{
|
|
int ridx;
|
|
if ( rev )
|
|
{
|
|
ridx = cur-i;
|
|
if ( ridx < 0 ) break;
|
|
}
|
|
else ridx = (i+cur+1)%7;
|
|
if ( CountInv(types[ridx]) <= 0 ) continue;
|
|
next = ridx;
|
|
break;
|
|
}
|
|
if ( rev && ((invoker.nextammo == types[next]) || (CountInv(invoker.nextammo) <= 0)) )
|
|
{
|
|
A_SwitchAmmoType(false); // recheck forward
|
|
return;
|
|
}
|
|
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
|
|
A_DropItem(Random[Spreadgun](0,2)?"RedShell":"GreenShell",Random[Spreadgun](1,3));
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$T_SPREADGUN";
|
|
Inventory.PickupMessage "$I_SPREADGUN";
|
|
Obituary "$O_SPREADGUN";
|
|
Weapon.UpSound "spreadgun/select";
|
|
Weapon.SlotNumber 3;
|
|
Weapon.SelectionOrder 2400;
|
|
Weapon.AmmoType1 "RedShell";
|
|
Weapon.AmmoGive1 1;
|
|
Stamina 15000;
|
|
+SWWMWEAPON.NOFIRSTGIVE;
|
|
}
|
|
|
|
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
|
|
{
|
|
int flg = WRF_ALLOWZOOM|WRF_ALLOWUSER1;
|
|
if ( invoker.nextammo && (CountInv(invoker.nextammo) > 0) )
|
|
flg |= WRF_ALLOWRELOAD;
|
|
A_WeaponReady(flg);
|
|
}
|
|
Wait;
|
|
ReadyFired:
|
|
XZW2 Z 1
|
|
{
|
|
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 RS 1;
|
|
XZW2 T 1 A_Recoil(invoker.srecoil);
|
|
XZW2 U 1;
|
|
XZW2 VWXY 2;
|
|
Goto ReadyFired;
|
|
AltFire:
|
|
#### # 1 A_SwitchAmmoType();
|
|
#### # 1 A_AltHold();
|
|
Wait;
|
|
Reload:
|
|
#### # 1 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);
|
|
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);
|
|
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;
|
|
}
|
|
}
|