swwmgz_m/zscript/swwm_danmaku.zsc
Marisa Kirisame b17fbd9a7d Remove 8+ size variants of shells. Allow dropping of 2/4 golden shells.
Use overlays for Eviscerator casing eject, add random offsets like with Explodium Gun.
Fix Candy Gun not being consistent with new animation changes of Explodium Gun.
Fix Explodium Gun not showing the loaded bullet when checking the chamber.
Give random offsets to Silver Bullet ejection overlays.
Some small texture wrangling.
Add projectile models for Silver Bullet (this will be used in the future).
Remove InterHubAmount from MagAmmo classes (not needed as this property is only used by items with the INVBAR flag).
Reduce BackpackMaxAmount of Ynykron Ammo to 3.
Adjust how Chanceboxes randomize their "dud" probability.
Tweak some of the Chancebox drops.
Set DROPPED flag to false for all Chancebox drops, so they don't get crushed.
Add 25% Crush damage factor to Demolitionist (in a future update, will try to make it so crushers have a chance to break while touching the player).
2021-02-15 23:46:04 +01:00

1207 lines
30 KiB
Text

// Mr. BIG SHOT Industries "Eviscerator" High Load Flak Cannon (from SWWM series)
// Slot 5, replaces Chaingun, Dragon Claw, Hammer of Retribution
Class EvisceratorChunkLight : PointLightAttenuated
{
Default
{
Args 255,224,128,16;
}
override void Tick()
{
Super.Tick();
if ( !EvisceratorChunk(target) || EvisceratorChunk(target).justdied )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
if ( isFrozen() ) return;
double intst = clamp((.7-EvisceratorChunk(target).lifetime)/.7,0.,1.);
args[LIGHT_RED] = int(255*intst);
args[LIGHT_GREEN] = int(224*intst);
args[LIGHT_BLUE] = int(128*intst);
}
}
Class ChunkImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_SprayDecal("WallCrack",-20);
int numpt = Random[Eviscerator](-1,2);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Eviscerator](-.8,.8),FRandom[Eviscerator](-.8,.8),FRandom[Eviscerator](-.8,.8))).unit()*FRandom[Eviscerator](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.scale *= .6;
s.special1 = Random[Eviscerator](0,1);
s.SetShade(Color(1,1,1)*Random[Eviscerator](96,192));
}
numpt = Random[Eviscerator](-2,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1)).unit()*FRandom[Eviscerator](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Eviscerator](-2,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1)).unit()*FRandom[Eviscerator](1,4);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class EvisceratorChunkGlow : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0.;
Scale .25;
+FORCEXYBILLBOARD;
+NOGRAVITY;
+NOBLOCKMAP;
+NOINTERACTION;
+DONTSPLASH;
+NOTELEPORT;
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !EvisceratorChunk(target) || EvisceratorChunk(target).justdied )
{
Scale *= .9;
A_FadeOut();
return;
}
SetOrigin(target.pos,true);
alpha = clamp((.7-EvisceratorChunk(target).lifetime)/.7,0.,1.);
if ( alpha <= 0. ) Destroy();
}
States
{
Spawn:
ETRL A -1 Bright;
Stop;
}
}
Class EvisceratorChunkTrail : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0.;
XScale 8.;
+FORCEXYBILLBOARD;
+NOGRAVITY;
+NOBLOCKMAP;
+NOINTERACTION;
+DONTSPLASH;
+NOTELEPORT;
}
override void Tick()
{
if ( isFrozen() ) return;
A_SetScale(scale.x*(.6+specialf1),scale.y);
A_FadeOut(.1+specialf2);
}
States
{
Spawn:
XZW1 ABCDEFGHIJ -1 Bright;
Stop;
}
}
Class EvisceratorChunk : Actor
{
Actor lasthit;
double anglevel, pitchvel, rollvel;
double lifetime, lifespeed;
Vector3 oldvel;
bool justdied;
int trailcolor;
Default
{
Obituary "$O_EVISCERATOR";
Radius 2;
Height 2;
Speed 50;
DamageFunction int(clamp((vel.length()-8)*.2+max(0,1.-lifetime)*2.,0,15));
DamageType 'shot';
BounceFactor 1.0;
WallBounceFactor 1.0;
PROJECTILE;
+USEBOUNCESTATE;
+BOUNCEONWALLS;
+BOUNCEONFLOORS;
+BOUNCEONCEILINGS;
+ALLOWBOUNCEONACTORS;
+NODAMAGETHRUST;
+DONTBOUNCEONSKY;
+CANBOUNCEWATER;
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
Scale 0.4;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
let l = Spawn("EvisceratorChunkLight",pos);
l.target = self;
let t = Spawn("EvisceratorChunkGlow",pos);
t.target = self;
lifetime = 0;
lifespeed = FRandom[Eviscerator](0.01,0.02);
anglevel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1);
pitchvel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1);
rollvel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1);
scale *= Frandom[Eviscerator](0.8,1.2);
frame = Random[Eviscerator](0,7);
}
override void Tick()
{
static const name tls[] =
{
'HotMetal0', 'HotMetal1', 'HotMetal2', 'HotMetal3',
'HotMetal4', 'HotMetal5', 'HotMetal6', 'HotMetal7'
};
oldvel = vel;
Super.Tick();
// somehow checking the state does not work all the time
// so instead I have to set a bool at the start of XDeath,
// otherwise there is a single puff of smoke at the LAST tic
// of the state, there is no logical explanation for this,
// I guess I can blame graf, randi, or whoever else
if ( isFrozen() || justdied ) return;
lifetime += lifespeed;
if ( waterlevel > 0 ) lifetime = max(.7,lifetime);
A_SetTranslation(tls[clamp(int(lifetime*10),0,7)]);
if ( !Random[Eviscerator](0,2) && (lifetime < .7) )
{
let s = Spawn("SWWMHalfSmoke",pos);
s.vel = .2*vel+(FRandom[Eviscerator](-.1,.1),FRandom[Eviscerator](-.1,.1),FRandom[Eviscerator](-.1,.1));
s.scale *= .5;
s.alpha *= scale.x*max(0,.7-lifetime)*1.5;
}
if ( !InStateSequence(CurState,FindState("Death")) )
{
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
double alph = clamp((.7-lifetime)/.7,0,1.);
if ( alph < 0 ) return;
Vector3 dir = level.Vec3Diff(pos,prev);
double dist = dir.length();
if ( dist < 1. ) return;
dir /= dist;
let t = Spawn("EvisceratorChunkTrail",pos);
t.alpha = alph;
t.scale.y = dist;
t.angle = atan2(dir.y,dir.x);
t.pitch = asin(-dir.z)+90;
t.SetState(t.SpawnState+trailcolor);
if ( trailcolor > 0 )
{
// custom trails last longer
t.specialf1 = .3;
t.specialf2 = -.05;
}
}
}
void A_HandleBounce()
{
Vector3 HitNormal = -vel.unit();
F3DFloor ff;
if ( BlockingFloor )
{
// find closest 3d floor for its normal
for ( int i=0; i<BlockingFloor.Get3DFloorCount(); i++ )
{
if ( !(BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(BlockingFloor.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
ff = BlockingFloor.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.top.Normal;
else HitNormal = BlockingFloor.floorplane.Normal;
}
else if ( BlockingCeiling )
{
// find closest 3d floor for its normal
for ( int i=0; i<BlockingCeiling.Get3DFloorCount(); i++ )
{
if ( !(BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
ff = BlockingCeiling.Get3DFloor(i);
break;
}
if ( ff ) HitNormal = -ff.bottom.Normal;
else HitNormal = BlockingCeiling.ceilingplane.Normal;
}
else if ( BlockingLine )
{
HitNormal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
HitNormal *= -1;
}
else if ( BlockingMobj )
{
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
}
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,GetMissileDamage(0,0),oldvel.unit());
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
Vector3 RealHitNormal = HitNormal;
HitNormal = (HitNormal+(FRandom[Eviscerator](-.1,.1),FRandom[Eviscerator](-.1,.1),FRandom[Eviscerator](-.1,.1))).unit();
if ( (HitNormal dot RealHitNormal) < 0 ) HitNormal *= -.5;
vel = FRandom[Eviscerator](.8,.95)*((vel dot HitNormal)*HitNormal*(FRandom[Eviscerator](-1.8,-1.))+vel);
bHITOWNER = true;
lasthit = null;
if ( (vel.length() > 20) && !Random[Eviscerator](0,2) )
{
let l = Spawn("ChunkImpact",pos);
l.angle = atan2(HitNormal.y,HitNormal.x);
l.pitch = asin(-HitNormal.z);
A_StartSound("eviscerator/hith",CHAN_WEAPON,CHANF_OVERLAP,.5);
}
A_Gravity();
gravity = .35;
anglevel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1)*(vel.length()/speed);
pitchvel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1)*(vel.length()/speed);
rollvel = FRandom[Eviscerator](50,100)*RandomPick[Eviscerator](-1,1)*(vel.length()/speed);
A_StartSound("eviscerator/hit",CHAN_WEAPON,CHANF_OVERLAP,.3);
if ( swwm_extraalert ) A_AlertMonsters(swwm_uncapalert?0:300);
if ( vel.length() < 3 )
{
A_Stop();
ClearBounce();
ExplodeMissile();
}
}
override bool CanCollideWith( Actor other, bool passive )
{
// safer to do here
if ( !(other.bSHOOTABLE && other.bSOLID) || ((vel.length() <= 5) && other.bSHOOTABLE) || ((other == target) && !bHITOWNER) || (other == lasthit) )
return false;
return true;
}
override int SpecialMissileHit( Actor victim )
{
// directly bounce off shootable solids
if ( !victim.bSHOOTABLE )
{
if ( bSOLID )
{
BlockingMobj = victim;
A_HandleBounce();
lasthit = victim;
}
return 1;
}
// with this we can guarantee that the chunk won't just keep on dealing damage
// this is something I wish Unreal's boulders did
lasthit = victim;
// don't knock back if already dead
int oldamt = SWWMDamageAccumulator.GetAmount(victim);
if ( victim.health-oldamt > 0 ) SWWMUtility.DoKnockback(victim,vel.unit(),12000);
// gather damage
int dmg = GetMissileDamage(0,0);
SWWMDamageAccumulator.Accumulate(victim,dmg,self,target,damagetype);
int amt = SWWMDamageAccumulator.GetAmount(victim);
// pass through if it's already dead
// + random chance relative to health
int posthealth = victim.health-amt;
double hratio = posthealth/double(victim.GetSpawnHealth());
if ( (posthealth <= 0) || (FRandom[Eviscerator](hratio,1.) < .7) )
{
if ( !victim.bNOBLOOD && !victim.bDORMANT && !victim.bINVULNERABLE )
{
victim.SpawnBlood(pos,AngleTo(victim),dmg);
A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.1);
}
else
{
let l = Spawn("ChunkImpact",pos);
l.angle = angle+180;
l.pitch = -pitch;
A_StartSound("eviscerator/hith",CHAN_WEAPON,CHANF_OVERLAP,.1);
}
A_Gravity();
gravity = .35;
vel *= .65; // reduce velocity as it rips
return 1;
}
// HACK
if ( !victim.bNOBLOOD && !victim.bDORMANT && !victim.bINVULNERABLE )
{
victim.SpawnBlood(pos,AngleTo(victim),dmg);
A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.1);
ExplodeMissile(null,victim);
}
else
{
BlockingMobj = victim;
A_HandleBounce();
lasthit = victim;
}
if ( swwm_extraalert || !Random[Eviscerator](0,3) ) A_AlertMonsters(swwm_uncapalert?0:900);
return 1;
}
States
{
Spawn:
XZW1 # -1;
Stop;
Bounce:
XZW1 # 0 A_HandleBounce();
Goto Spawn;
Death:
XZW2 # 0
{
pitch = 0;
roll = 0;
bMOVEWITHSECTOR = true;
A_SetTics(Random[Eviscerator](30,50));
}
XZW2 # 1 A_FadeOut();
Wait;
XDeath:
TNT1 A 35 { invoker.justdied = true; }
Stop;
}
}
Class EvisceratorProjSmoke : Actor
{
double lifetime, lifespeed;
Default
{
Radius 0.1;
Height 0;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
lifetime = 0;
lifespeed = FRandom[Eviscerator](0.004,0.008);
}
override void Tick()
{
if ( isFrozen() ) return;
lifetime += lifespeed;
let s = Spawn("SWWMSmoke",pos);
s.vel = (FRandom[Eviscerator](-0.5,0.5),FRandom[Eviscerator](-0.5,0.5),FRandom[Eviscerator](-0.5,0.5));
s.vel.z += 2.;
s.alpha = scale.x;
s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255));
scale.x = max(0,1-lifetime);
if ( scale.x <= 0 ) Destroy();
}
}
Class EvisceratorProjLight : PaletteLight
{
Default
{
Args 0,0,0,140;
ReactionTime 20;
}
}
Class EvisceratorProj : Actor
{
double heat;
Default
{
Obituary "$O_EVISCERATOR";
DamageType 'Exploded';
Radius 4;
Height 4;
Gravity 0.35;
Speed 50;
PROJECTILE;
-NOGRAVITY;
+EXPLODEONWATER;
+HITTRACER;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
if ( waterlevel <= 0 ) vel.z += 3;
heat = 1.5;
}
action void A_EvisExplode()
{
bForceXYBillboard = true;
A_SetRenderStyle(1.0,STYLE_Add);
A_SprayDecal("BigRocketBlast",50);
A_NoGravity();
A_SetScale(3.5);
SWWMUtility.DoExplosion(self,80,120000,200,80);
A_QuakeEx(6,6,6,20,0,1200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.7);
A_StartSound("eviscerator/shell",CHAN_WEAPON,attenuation:.5);
A_StartSound("eviscerator/shell",CHAN_VOICE,attenuation:.3);
A_AlertMonsters(swwm_uncapalert?0:3000);
if ( !Tracer ) Spawn("EvisceratorProjSmoke",pos);
Spawn("EvisceratorProjLight",pos);
Vector3 x, y, z;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
EvisceratorChunk p;
Vector3 spawnofs;
if ( BlockingMobj ) spawnofs = (0,0,0);
else if ( BlockingFloor ) spawnofs = BlockingFloor.floorplane.Normal*4;
else if ( BlockingCeiling ) spawnofs = BlockingCeiling.ceilingplane.Normal*4;
else if ( BlockingLine )
{
spawnofs = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit()*4;
if ( !SWWMUtility.PointOnLineSide(pos.xy,BlockingLine) )
spawnofs *= -1;
}
int trail = 0;
if ( target && target.player ) trail = CVar.GetCVar('swwm_funtrails',target.player).GetInt();
for ( int i=0; i<40; i++ )
{
p = EvisceratorChunk(Spawn("EvisceratorChunk",level.Vec3Offset(pos,spawnofs)));
p.bHITOWNER = true;
a = FRandom[Eviscerator](0,360);
s = FRandom[Eviscerator](0,.4);
Vector3 dir = (x+y*cos(a)*s+z*sin(a)*s).unit();
p.angle = atan2(dir.y,dir.x);
p.pitch = -asin(dir.z);
p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*(p.speed+FRandom[Eviscerator](-5,20));
p.target = target;
if ( trail < 8 ) p.trailcolor = max(0,trail);
else if ( trail == 8 ) p.trailcolor = (i%6)+2;
else if ( trail == 9 )
{
switch ( i%5 )
{
case 0:
case 3:
p.trailcolor = 8;
break;
case 1:
case 4:
p.trailcolor = 9;
break;
case 2:
p.trailcolor = 1;
break;
}
}
}
int numpt = Random[Eviscerator](10,15);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,3);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
s.special1 = Random[ExploS](1,2);
s.scale *= 2.4;
s.alpha *= .4;
}
numpt = Random[Eviscerator](8,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1)).unit()*FRandom[Eviscerator](2,8);
let s = Spawn("SWWMSpark",level.Vec3Offset(pos,spawnofs));
s.vel = pvel;
}
numpt = Random[Eviscerator](8,16);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1),FRandom[Eviscerator](-1,1)).unit()*FRandom[Eviscerator](6,16);
let s = Spawn("SWWMChip",level.Vec3Offset(pos,spawnofs));
s.vel = pvel;
s.scale *= FRandom[Eviscerator](0.9,1.8);
}
Spawn("EvisceratorRing",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,150,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
action void A_SubExpl()
{
special1++;
if ( special1 > 8 ) return;
int numpt = Random[Eviscerator](0,8-special1);
double ang, pt;
for ( int i=0; i<numpt; i++ )
{
ang = FRandom[Eviscerator](0,360);
pt = FRandom[Eviscerator](-90,90);
FLineTraceData d;
Vector3 HitNormal;
LineTrace(ang,FRandom[Eviscerator](10,30)+10*special1,pt,TRF_THRUACTORS,data:d);
hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
else hitnormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
else hitnormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
let p = Spawn("EvisceratorSubExpl",d.HitLocation+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = target;
p.scale *= 2-special1*.1;
p.alpha *= 1-special1*.1;
}
}
States
{
Spawn:
XZW1 A 1
{
invoker.heat -= 0.004+0.0004*vel.length();
if ( invoker.heat > 0 )
{
let s = Spawn("SWWMHalfSmoke",pos);
s.alpha *= heat;
}
}
Wait;
Death:
TNT1 A 0 A_EvisExplode();
XSEX ABCDEFGHIJKLMNOPQRS 2 Bright A_Subexpl();
Stop;
}
}
Class EvisceratorSubExpl : Actor
{
Default
{
RenderStyle "Add";
Scale 2.;
Alpha .6;
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+NOINTERACTION;
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XSEX ABCDEFGHIJKLMNOPQRS 1 Bright;
Stop;
}
}
Class EvisceratorRing : Actor
{
Default
{
RenderStyle "Add";
Scale 4.;
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+NOINTERACTION;
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XRG0 ABCDEFGHIJKLMNOPQRSTUVWX 1 Bright A_SetScale(scale.x*1.01);
Stop;
}
}
Class EvisceratorCasing : SWWMCasing
{
Default
{
Mass 10;
BounceFactor 0.4;
WallBounceFactor 0.4;
BounceSound "eviscerator/casing";
}
States
{
Death:
XZW1 BC -1
{
pitch = roll = 0;
angle = FRandom[Junk](0,360);
frame = RandomPick[Junk](1,2);
}
Stop;
}
}
Class Eviscerator : SWWMWeapon
{
double casex, casey;
bool isfiring;
// barrel is extended
bool extended;
// pending shell load
bool pendingload;
// has shell chambered
bool chambered;
// countdown to loading new shell
int loadtics;
transient ui TextureID WeaponBox, AmmoIcon;
transient ui Font TewiFont;
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/EvisceratorDisplay.png",TexMan.Type_Any);
if ( !AmmoIcon ) AmmoIcon = TexMan.CheckForTexture("graphics/HUD/EvisceratorShell.png",TexMan.Type_Any);
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
Screen.DrawTexture(WeaponBox,false,bx-46,by-16,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
String astr = String.Format("%d",Ammo1.Amount);
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-14-(TewiFont.StringWidth(astr)+1),by-15,astr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawTexture(AmmoIcon,false,bx-14,by-14,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,chambered?Color(0,0,0,0):Color(128,0,0,0));
Screen.DrawText(TewiFont,Font.CR_WHITE,bx-44,by-15,extended?"►":"",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
override bool ReportHUDAmmo()
{
if ( (Ammo1.Amount > 0) || chambered ) return true;
return false;
}
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
{
if ( (firemode == PrimaryFire) || (firemode == AltFire) )
{
if ( (Ammo1.Amount > 0) || chambered ) return true;
return false;
}
return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount);
}
override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon )
{
// add the chambered shell in
if ( chambered ) AmmoGive1++;
return Super.PickupForAmmoSWWM(ownedWeapon);
}
override void DoEffect()
{
Super.DoEffect();
if ( chambered || !pendingload || ((loadtics < 20) && (Ammo1.Amount <= 0)) )
{
loadtics = 0;
return;
}
loadtics++;
if ( (loadtics == 10) && Owner && Owner.player && (Owner.player.ReadyWeapon == self) )
Owner.A_StartSound("eviscerator/load",CHAN_WEAPON,CHANF_OVERLAP);
if ( (loadtics == 20) && !sv_infiniteammo && !Owner.FindInventory('PowerInfiniteAmmo',true) )
Ammo1.Amount = max(0,Ammo1.Amount-1);
if ( loadtics == 25 )
{
pendingload = false;
chambered = true;
}
}
action void A_StartLoad( int delay = 0 )
{
invoker.pendingload = true;
invoker.loadtics = -delay;
}
override Vector3 GetTraceOffset()
{
return (10.,4.,-5.);
}
action void A_EvisceratorFire()
{
let weap = Weapon(invoker);
if ( !weap ) return;
invoker.isfiring = true;
A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(6,6,6,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.5);
A_ZoomFactor(.94,ZOOM_INSTANT);
A_ZoomFactor(1.);
A_SWWMFlash();
A_PlayerFire();
SWWMHandler.DoFlash(self,Color(64,255,224,96),3);
A_AlertMonsters(swwm_uncapalert?0:4500);
Vector3 x, y, z, x2, y2, z2, dir, origin;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMUtility.DoKnockback(self,-x,25000.);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+4*y-5*z);
int trail = CVar.GetCVar('swwm_funtrails',player).GetInt();
for ( int i=0; i<40; i++ )
{
a = FRandom[Eviscerator](0,360);
s = FRandom[Eviscerator](0,invoker.extended?.06:.3);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
let p = EvisceratorChunk(Spawn("EvisceratorChunk",origin));
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed*FRandom[Eviscerator](.9,1.1);
if ( invoker.extended ) p.vel *= 1.4;
if ( trail < 8 ) p.trailcolor = max(0,trail);
else if ( trail == 8 ) p.trailcolor = (i%6)+2;
else if ( trail == 9 )
{
switch ( i%5 )
{
case 0:
case 3:
p.trailcolor = 8;
break;
case 1:
case 4:
p.trailcolor = 9;
break;
case 2:
p.trailcolor = 1;
break;
}
}
}
for ( int i=0; i<8; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.alpha *= .3;
s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255));
s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-1,1)+z*FRandom[Eviscerator](-1,1);
}
for ( int i=0; i<9; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2);
}
}
action void A_EvisceratorAltFire()
{
let weap = Weapon(invoker);
if ( !weap ) return;
invoker.chambered = false;
invoker.isfiring = true;
A_StartSound("eviscerator/altfire",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("eviscerator/fire",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(4,4,4,5,0,10,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.9);
A_ZoomFactor(.91,ZOOM_INSTANT);
A_ZoomFactor(1.);
A_SWWMFlash();
A_PlayerFire();
SWWMHandler.DoFlash(self,Color(16,255,224,96),3);
A_AlertMonsters(swwm_uncapalert?0:4000);
Vector3 x, y, z, x2, y2, z2, dir, origin;
double a, s;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMUtility.DoKnockback(self,-x,32000.);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-5*z);
a = FRandom[Eviscerator](0,360);
s = FRandom[Eviscerator](0,invoker.extended?.003:.02);
dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
let p = Spawn("EvisceratorProj",origin);
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed*(invoker.extended?1.6:.8);
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.special1 = 1;
s.scale *= .9;
s.alpha *= .2;
s.SetShade(Color(1,1,1)*Random[Eviscerator](160,255));
s.vel += vel*.5+x*FRandom[Eviscerator](3.,5.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2);
}
for ( int i=0; i<5; i++ )
{
let s = Spawn("SWWMSpark",origin);
s.scale *= .3;
s.alpha *= .4;
s.vel += vel*.5+x*FRandom[Eviscerator](4.,8.)+y*FRandom[Eviscerator](-2,2)+z*FRandom[Eviscerator](-2,2);
}
}
action void A_EvisceratorEject()
{
Vector3 x, y, z, origin;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*10-y*10-z*10);
let c = Spawn("EvisceratorCasing",origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.5,.5)-y*FRandom[Junk](3,6)-(0,0,FRandom[Junk](4,6));
c.vel += vel*.5;
invoker.chambered = false;
}
action void A_EvisceratorCasingSmoke( Vector3 ofs )
{
Vector3 x, y, z, origin;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
let s = Spawn("SWWMHalfSmoke",origin);
s.scale *= .2;
s.alpha *= .4;
s.speed *= .1;
}
Default
{
Tag "$T_EVISCERATOR";
Inventory.PickupMessage "$I_EVISCERATOR";
Obituary "$O_EVISCERATOR";
Inventory.Icon "graphics/HUD/Icons/W_Eviscerator.png";
Weapon.SlotNumber 5;
Weapon.UpSound "eviscerator/select";
Weapon.SelectionOrder 300;
Stamina 50000;
Weapon.AmmoType1 "EvisceratorShell";
Weapon.AmmoGive1 4;
SWWMWeapon.DropAmmoType "EvisceratorShell";
+WEAPON.EXPLOSIVE;
Radius 20;
Height 32;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Deselect:
XZW2 A 2
{
A_StartSound("eviscerator/deselect",CHAN_WEAPON,CHANF_OVERLAP);
return A_JumpIf(invoker.extended,"DeselectExt");
}
XZW2 BCDEFGH 2;
XZW2 H -1 A_FullLower();
Stop;
DeselectExt:
XZW4 Z 2;
XZW5 ABCDEFG 2;
XZW5 G -1 A_FullLower();
Stop;
Select:
XZW2 H 2
{
invoker.isfiring = false;
A_FullRaise();
return A_JumpIf(invoker.extended,"SelectExt");
}
XZW2 IJKLMNOPQR 2;
Goto Ready;
SelectExt:
XZW5 GHIJKLMNOPQ 2;
Goto ReadyExt;
Ready:
XZW2 A 1
{
invoker.isfiring = false;
int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1;
if ( !invoker.chambered )
{
flg |= WRF_NOFIRE;
// autoloader
if ( !invoker.pendingload )
invoker.pendingload = true;
}
A_WeaponReady(flg);
// avoid the check while still chambering
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) && (invoker.loadtics < 20) )
invoker.CheckAmmo(EitherFire,true);
}
Wait;
ReadyExt:
XZW4 Z 1
{
invoker.isfiring = false;
int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1;
if ( !invoker.chambered )
{
flg |= WRF_NOFIRE;
// autoloader
if ( !invoker.pendingload )
invoker.pendingload = true;
}
A_WeaponReady(flg);
// avoid the check while still chambering
if ( player.cmd.buttons&(BT_ATTACK|BT_ALTATTACK) && (invoker.loadtics < 20) )
invoker.CheckAmmo(EitherFire,true);
}
Wait;
Fire:
XZW2 A 1
{
A_EvisceratorFire();
return A_JumpIf(invoker.extended,"FireExt");
}
XZW3 EFGHIJKLMNOPQR 1;
Goto Eject;
FireExt:
XZW4 Z 1;
XZW6 DEFGHIJKLMNOPQ 1;
Goto EjectExt;
Eject:
XZW2 A 4;
XZW3 STUV 2;
XZW3 W 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 X 1
{
int layer = PSP_WEAPON+1;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"EjectCasing");
A_Overlay(-9999,"EjectSmoke");
}
XZW3 YZ 1;
XZW4 AB 1;
XZW4 C 1
{
A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP);
A_StartLoad();
}
XZW4 DEF 1;
XZW4 GHI 2;
Goto Ready;
EjectExt:
XZW4 Z 4;
XZW6 RSTU 2;
XZW6 V 1 A_StartSound("eviscerator/eject",CHAN_WEAPON,CHANF_OVERLAP);
XZW6 W 1
{
int layer = PSP_WEAPON+1;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"EjectCasing");
A_Overlay(-9999,"EjectSmoke");
}
XZW6 XYZ 1;
XZW7 A 1;
XZW7 B 1
{
A_StartSound("eviscerator/ejectend",CHAN_WEAPON,CHANF_OVERLAP);
A_StartLoad();
}
XZW7 CDE 1;
XZW7 FGH 2;
Goto ReadyExt;
EjectCasing:
XZWB D 1
{
A_OverlayOffset(OverlayID(),0,0);
invoker.casex = FRandom[Eviscerator](-2.,2.);
invoker.casey = FRandom[Eviscerator](-2.,2.);
}
XZWB EFGHIJKLM 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE);
TNT1 A 1 A_EvisceratorEject();
Stop;
EjectSmoke:
TNT1 A 1 A_EvisceratorCasingSmoke((10,2,-3));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-1,-2));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-3,-1.5));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-5,-2));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-7,-3.5));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-8.5,-5));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-10,-9));
TNT1 A 1 A_EvisceratorCasingSmoke((10,-11,-14));
Stop;
AltFire:
XZW2 A 2
{
A_EvisceratorAltFire();
return A_JumpIf(invoker.extended,"AltFireExt");
}
XZW2 STUVW 1;
XZW2 XYZ 2;
XZW3 ABCD 2;
XZW2 A 1 A_StartLoad(5);
Goto Ready;
AltFireExt:
XZW4 Z 2;
XZW5 RSTUV 1;
XZW5 WXY 2;
XZW5 Z 2;
XZW6 ABC 2;
XZW4 Z 1 A_StartLoad(5);
Goto ReadyExt;
Zoom:
XZW2 A 2
{
A_StartSound("eviscerator/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
return A_JumpIf(invoker.extended,"ZoomExt");
}
XZW4 JKLMN 2;
XZW4 O 1 A_StartSound("eviscerator/switch");
XZW4 PQR 1;
XZW4 S 2
{
invoker.extended = !invoker.extended;
A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW4 TUVWY 2;
Goto ReadyExt;
ZoomExt:
XZW4 Z 2;
XZW7 IJK 3;
XZW7 L 1 A_StartSound("eviscerator/switch");
XZW7 MNO 1;
XZW7 P 2
{
invoker.extended = !invoker.extended;
A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW7 QRSTU 2;
Goto Ready;
Reload:
XZW2 A 2
{
A_StartSound("eviscerator/checkgun",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerCheckGun();
return A_JumpIf(invoker.extended,"ReloadExt");
}
XZW7 VWXYZ 2;
XZW8 A 2;
XZW8 BCDEF 3;
XZW8 GHIJK 2;
XZW8 LMNO 3;
XZW8 PQRSTU 2;
XZW8 V 3;
Goto Ready;
ReloadExt:
XZW4 Z 2;
XZW9 MNOPQR 2;
XZW9 STUVW 3;
XZW9 XYZ 2;
XZWA AB 2;
XZWA CDEF 3;
XZWA GHIJKL 2;
XZWA M 3;
Goto ReadyExt;
User1:
XZW2 A 2
{
A_StartSound("eviscerator/meleestart",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerMelee();
return A_JumpIf(invoker.extended,"User1Ext");
}
XZW8 WXY 2;
XZW8 Z 1;
XZW9 AB 1;
XZW9 C 1 A_Parry(9);
XZW9 D 1;
XZW9 E 2 A_Melee(60,"demolitionist/whitm",1.1);
XZW9 FGH 2;
XZW9 I 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
XZW9 JKL 2;
Goto Ready;
User1Ext:
XZW4 Z 2;
XZWA NOP 2;
XZWA QRS 1;
XZWA T 1 A_Parry(9);
XZWA U 1;
XZWA V 2 A_Melee(60,"demolitionist/whitm",1.1);
XZWA WXY 2;
XZWA Z 2 A_StartSound("eviscerator/meleeend",CHAN_WEAPON,CHANF_OVERLAP);
XZWB ABC 2;
Goto ReadyExt;
Flash:
XZWZ A 2
{
let psp = player.GetPSprite(PSP_FLASH);
psp.frame = Random[GunFlash](0,3);
let l = Spawn("SWWMWeaponLight",pos);
l.target = self;
}
Stop;
AltFlash:
XZWZ A 2
{
let psp = player.GetPSprite(PSP_FLASH);
psp.frame = Random[GunFlash](4,7);
let l = Spawn("SWWMWeaponLight",pos);
l.target = self;
l.args[3] -= 20;
}
Stop;
}
}