swwmgz_m/zscript/swwm_danmaku.zsc
2021-01-23 18:21:10 +01:00

1178 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 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;
}
}