swwmgz_m/zscript/swwm_danmaku.zsc
Marisa Kirisame 67483877c0 Implemented selling at the store (right click / backspace to toggle).
Implemented excess ammo dropping for weapon pickups (previously it was just sold).
Implemented dropped weapon loaded ammo handling (previously this made ammo simply disappear).
Implemented mag splitting for excess ammo pickups (split 'em into individual rounds).
Dropping a Candygun will drop spares first (if any, they will be empty, too).
2021-01-25 17:32:44 +01:00

1185 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
{
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 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 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;
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));
TNT1 A 1 A_EvisceratorEject();
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;
}
}