swwmgz_m/zscript/swwm_jackhammer.zsc
Marisa Kirisame 0156af8431 Rebalances across the board once again.
Fix the incorrect SPAC_PCross behaviour in other spots.
Redo swap weapon implementation. Should now theoretically allow for not just two, but many more swap weapons per slot.
Minor fixups.
2020-12-31 13:58:11 +01:00

738 lines
22 KiB
Text

// Tach-Engine Technologies Microfusion Rotary Hammer aka "Pusher" (planned for unreleased Zanaveth Ultra Suite 2)
// Slot 1, replaces Chainsaw, Gauntlets, Timon's Axe
Class PusherImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3);
A_StartSound("pusher/hit",CHAN_VOICE);
A_SprayDecal("WallCrack",-20);
int numpt = Random[Pusher](4,8);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8))).unit()*FRandom[Pusher](.1,1.2);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Pusher](128,192));
}
numpt = Random[Pusher](2,6);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,8);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Pusher](3,6);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,8);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class BigPusherImpact : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_QuakeEx(5,5,5,15,0,300,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.8);
A_StartSound("pusher/althit",CHAN_VOICE);
A_SprayDecal("ImpactMark",-20);
int numpt = Random[Pusher](8,16);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8),FRandom[Pusher](-.8,.8))).unit()*FRandom[Pusher](.8,2.6);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[Pusher](128,192));
}
numpt = Random[Pusher](4,10);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,12);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[Pusher](8,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[Pusher](-1,1),FRandom[Pusher](-1,1),FRandom[Pusher](-1,1)).unit()*FRandom[Pusher](2,16);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
Destroy();
}
}
Class PusherProjectile : Actor
{
Vector3 oldvel;
double oldangle, oldpitch;
Actor lasthit;
int hittics;
Default
{
Obituary "$O_PUSHER";
Speed 50;
Radius 10;
Height 10;
DamageFunction clamp(int(3*vel.length()),0,150);
DamageType 'Tenderize';
BounceType "Hexen";
BounceFactor 1.0;
WallBounceFactor 1.0;
Gravity 0.3;
PROJECTILE;
+USEBOUNCESTATE;
+CANBOUNCEWATER;
+INTERPOLATEANGLES;
+DONTBOUNCEONSHOOTABLES;
-BOUNCEAUTOOFF;
+NODAMAGETHRUST;
-BOUNCEONUNRIPPABLES;
-ALLOWBOUNCEONACTORS;
+RIPPER;
-NOGRAVITY;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("pusher/fly",CHAN_BODY,CHANF_LOOP,1.,2.);
A_StartSound("pusher/altfire",CHAN_VOICE);
oldvel = vel;
oldangle = angle;
oldpitch = pitch;
}
void A_Reorient()
{
oldvel = vel;
oldangle = angle;
oldpitch = pitch;
if ( vel.length() <= 0. ) return;
Vector3 dir = vel.unit();
angle += clamp(deltaangle(angle,atan2(dir.y,dir.x)),-2,2);
pitch += clamp(deltaangle(pitch,asin(-dir.z)),-2,2);
// cancel if we hit a wall dead-on
FLineTraceData d;
LineTrace(oldangle,30,oldpitch,TRF_THRUACTORS|TRF_NOSKY,5,data:d);
if ( d.HitType != TRACE_HitNone )
{
angle = oldangle;
pitch = oldpitch;
ClearBounce();
special1 = 1;
ExplodeMissile(BlockingLine,BlockingMobj);
}
}
private bool HitSkyLine( Line l, int hitside )
{
if ( !l ) return false;
// if it's onesided, check if it's a Line_Horizon
if ( !l.sidedef[1] ) return (l.special == Line_Horizon);
Sector outs, ins;
if ( hitside )
{
ins = l.backsector;
outs = l.frontsector;
}
else
{
ins = l.frontsector;
outs = l.backsector;
}
// return true if we're in a sector with sky and we hit the upper part of the line
if ( ins.GetTexture(1) != skyflatnum ) return false;
return (outs.ceilingplane.ZAtPoint(pos.xy) <= pos.z);
}
void A_HandleBounce()
{
Vector3 HitNormal = -vel.unit();
F3DFloor ff;
int lineside = 1;
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) )
{
lineside = 0;
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
angle = oldangle;
pitch = oldpitch;
vel = oldvel;
// try to guess if we hit the sky
if ( HitSkyLine(BlockingLine,lineside) || (BlockingCeiling && (ceilingpic == skyflatnum)) || (BlockingFloor && (floorpic == skyflatnum)) )
{
special1 = 0;
ExplodeMissile();
return;
}
// re-do the bounce with our formula
vel = .8*((vel dot HitNormal)*HitNormal*(-1.8+FRandom[Pusher](.0,.8))+vel);
A_StartSound("pusher/bounce",volume:.3);
A_AlertMonsters(swwm_uncapalert?0:300);
if ( vel.length() < 5 )
{
special1 = 0;
ExplodeMissile();
}
}
void A_BecomePickup()
{
if ( special1 )
{
// stuff from direct hit
FLineTraceData d;
LineTrace(angle,40,pitch,0,5,data:d);
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("BigPusherImpact",d.HitLocation+HitNormal*4);
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
bool busted = false;
if ( swwm_omnibust )
{
if ( BusterWall.BustLinetrace(d,100,target,d.HitDir,d.HitLocation.z) )
busted = true;
}
if ( busted ) pitch = 0.;
else bNOGRAVITY = true;
}
else pitch = 0;
gravity = 1.;
ClearBounce();
bSPECIAL = true;
A_SetSize(20,16);
A_ChangeLinkFlags(0);
A_StopSound(CHAN_BODY);
}
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
{
if ( target == lasthit ) return 0;
lasthit = target;
if ( target.bNOBLOOD || target.bDORMANT || target.bINVULNERABLE ) A_StartSound("pusher/althit",CHAN_WEAPON,CHANF_OVERLAP);
else A_StartSound("pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
target.A_QuakeEx(6,6,6,10,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.7);
SWWMUtility.DoKnockback(target,vel.unit(),85000);
return damage;
}
override void Touch( Actor toucher )
{
// cannot pick up swapweapon unless explicitly pressing use
let pw = GetDefaultByType("PusherWeapon");
SWWMWeapon sw;
if ( swwm_swapweapons && (sw = pw.HasSwapWeapon(toucher)) )
{
if ( toucher.CheckLocalView() )
Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),StringTable.Localize("$T_PUSHER")));
return;
}
let w = toucher.FindInventory("PusherWeapon");
if ( toucher.player && w )
{
let psp = toucher.player.GetPSPrite(PSP_WEAPON);
if ( psp && psp.CurState.InStateSequence(w.FindState("AltMiss")) )
return;
}
if ( !toucher.player || !toucher.GiveInventory("PusherWeapon",1) ) return;
if ( toucher.CheckLocalView() )
{
toucher.A_StartSound("misc/w_pkup",CHAN_ITEM,CHANF_NOPAUSE|CHANF_MAYBE_LOCAL);
let w = toucher.FindInventory("PusherWeapon");
if ( w ) w.PrintPickupMessage(true,w.PickupMessage());
}
else toucher.A_StartSound("misc/w_pkup",CHAN_ITEM,CHANF_MAYBE_LOCAL);
toucher.A_SelectWeapon("PusherWeapon");
Spawn("SWWMRedPickupFlash",pos);
Destroy();
}
override bool Used( Actor user )
{
// test vertical range
Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2));
double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2);
if ( abs(diff.z) > rang ) return false;
// if the toucher owns our SwapWeapon, drop it before picking us up
let pw = GetDefaultByType("PusherWeapon");
SWWMWeapon sw;
if ( swwm_swapweapons && (sw = pw.HasSwapWeapon(user)) )
{
bool swapto = false;
if ( sw == user.player.ReadyWeapon ) swapto = true;
user.DropInventory(sw);
// don't autoswitch just yet (hacky)
if ( swapto )
{
user.player.ReadyWeapon = null;
user.player.PendingWeapon = WP_NOCHANGE;
}
}
Touch(user);
return bDestroyed;
}
States
{
Spawn:
XZW1 A 1 A_Reorient();
Wait;
Bounce:
XZW1 A 0 A_HandleBounce();
Goto Spawn;
Death:
XZW1 A 0 A_BecomePickup();
XZW1 A 1 A_JumpIf(pos.z<=floorz,1);
Wait;
XZW1 A -1 A_StartSound("pusher/bounce");
Stop;
}
}
Class PusherWeapon : SWWMWeapon
{
double chargelevel, vibe;
int hitcnt;
transient ui TextureID WeaponBox, ChargeBar;
transient ui DynamicValueInterpolator ChargeInter;
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/PusherDisplay.png",TexMan.Type_Any);
if ( !ChargeBar ) ChargeBar = TexMan.CheckForTexture("graphics/HUD/PusherBar.png",TexMan.Type_Any);
Screen.DrawTexture(WeaponBox,false,bx-60,by-9,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int chg = clamp(ChargeInter?ChargeInter.GetValue():int(chargelevel*100),0,100);
double cw = chg*56./100.;
Screen.DrawTexture(ChargeBar,false,bx-58,by-7,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,cw);
}
override void HudTick()
{
Super.HudTick();
if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(int(chargelevel*100),.5,1,50);
ChargeInter.Update(int(chargelevel*100));
}
override bool ReportHUDAmmo()
{
return true;
}
override Vector3 GetTraceOffset()
{
return (10.,2.,-3.5);
}
action void A_PusherDrill()
{
invoker.chargelevel = clamp(invoker.chargelevel+FRandom[Pusher](-.04,.08),.3,1.);
A_QuakeEx(1,1,1,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.5);
A_WeaponOffset(FRandom[Pusher](-1,1)*2,32+FRandom[Pusher](-1,1)*2);
A_ZoomFactor(1.01,ZOOM_INSTANT);
A_ZoomFactor(1.);
A_Recoil(-cos(pitch));
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-3.5*z);
FLineTraceData d;
LineTrace(angle,60,pitch,TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
SWWMBulletTrail.DoTrail(self,origin,x,60,0);
if ( d.HitType != TRACE_HitNone )
{
A_QuakeEx(2,2,2,7,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
A_AlertMonsters(swwm_uncapalert?0:1200);
int dmg = int(3+invoker.chargelevel*7);
if ( invoker.chargelevel > .4 ) invoker.chargelevel -= FRandom[Pusher](.01,.04);
if ( d.HitType == TRACE_HitActor )
{
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
self.angle += clamp(diff,-5.,5.);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,8500);
d.HitActor.A_QuakeEx(3,3,3,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT || d.HitActor.bINVULNERABLE )
{
let p = Spawn("PusherImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
p.pitch = asin(d.HitDir.z);
}
else
{
d.HitActor.TraceBleed(dmg,invoker);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
d.HitActor.A_StartSound("pusher/meat",CHAN_DAMAGE,CHANF_OVERLAP);
}
// move towards target (unless we're standing on it)
if ( !(self is 'Demolitionist') || (Demolitionist(self).oldencroached != d.HitActor) )
bJUSTATTACKED = true;
}
else
{
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;
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
}
let p = Spawn("PusherImpact",d.HitLocation+HitNormal*4);
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
if ( d.HitType == TRACE_HitFloor ) p.CheckSplash(40);
if ( waterlevel > 2 ) SWWMUtility.DoKnockback(self,-x,30000);
}
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,dmg,self,d.HitDir,d.HitLocation.z);
}
else if ( waterlevel > 2 ) SWWMUtility.DoKnockback(self,-x,18000);
if ( swwm_extraalert || !(invoker.hitcnt%20) ) A_AlertMonsters(swwm_uncapalert?0:500);
invoker.hitcnt++;
}
action void A_PusherAlt()
{
A_StopSound(CHAN_WEAPON);
if ( IsActorPlayingSound(CHAN_WEAPONEXTRA,"pusher/motor") )
A_StartSound("pusher/motorend",CHAN_WEAPONEXTRA,CHANF_DEFAULT,pitch:1.5);
A_StartSound("pusher/stop",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("pusher/altfire",CHAN_WEAPON,CHANF_OVERLAP);
A_ZoomFactor(1.+invoker.chargelevel*.2,ZOOM_INSTANT);
A_ZoomFactor(1.);
A_Recoil(-(2.+4.*invoker.chargelevel)*cos(pitch));
A_QuakeEx(2+int(invoker.chargelevel*2),2+int(invoker.chargelevel*2),2+int(invoker.chargelevel*2),3+int(invoker.chargelevel*6),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.3+invoker.chargelevel*.7);
A_WeaponOffset(0,32);
A_Overlay(-9999,null);
A_PlayerFire();
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-3.5*z);
FLineTraceData d;
LineTrace(angle,80,pitch,TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
SWWMBulletTrail.DoTrail(self,origin,x,80,0);
bool gone = false;
if ( d.HitType != TRACE_HitNone )
{
A_QuakeEx(8,8,8,12,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
A_AlertMonsters(swwm_uncapalert?0:1600);
int dmg = int(150*invoker.chargelevel);
if ( d.HitType == TRACE_HitActor )
{
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
self.angle += clamp(diff,-5.,5.);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,85000);
d.HitActor.A_QuakeEx(9,9,9,15,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT || d.HitActor.bINVULNERABLE )
{
let p = Spawn("BigPusherImpact",d.HitLocation-d.HitDir*4);
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
p.pitch = asin(d.HitDir.z);
}
else
{
d.HitActor.TraceBleed(dmg,invoker);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
d.HitActor.A_StartSound("pusher/altmeat",CHAN_DAMAGE,CHANF_OVERLAP);
}
}
else
{
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;
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
}
let p = Spawn("BigPusherImpact",d.HitLocation+HitNormal*4);
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
if ( d.HitType == TRACE_HitFloor ) p.CheckSplash(40);
}
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,dmg,self,d.HitDir,d.HitLocation.z);
}
else if ( !Random[Pusher](0,3-int(invoker.chargelevel*3)) )
{
// didn't hit anything, randomly slip off
player.SetPSprite(PSP_WEAPON,ResolveState("AltMiss"));
A_StopSound(CHAN_WEAPON);
A_StopSound(CHAN_WEAPONEXTRA);
let p = Spawn("PusherProjectile",origin);
p.angle = angle;
p.pitch = pitch;
p.vel = x*p.speed*invoker.chargelevel;
p.target = self;
SWWMUtility.DoKnockback(self,x,85000.);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
invoker.chargelevel = 0.;
}
action void A_BeginCharge()
{
invoker.chargelevel = invoker.vibe = 0.;
A_WeaponOffset(0,32);
A_QuakeEx(1,1,1,33,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.5);
A_Overlay(-9999,"Dummy");
}
action void A_ChargeUp()
{
invoker.chargelevel = min(1.,invoker.chargelevel+.025);
if ( invoker.chargelevel >= .5 ) invoker.vibe = min(1.,invoker.vibe+.02);
A_WeaponOffset(FRandom[Pusher](-1,1)*(invoker.chargelevel-invoker.vibe),32+FRandom[Pusher](-1,1)*(invoker.chargelevel-invoker.vibe));
if ( !(player.cmd.buttons&BT_ALTATTACK) && (invoker.chargelevel > .3) )
player.SetPSprite(PSP_WEAPON,ResolveState("AltRelease"));
}
Default
{
Tag "$T_PUSHER";
Inventory.PickupMessage "$I_PUSHER";
Obituary "$O_PUSHER";
Inventory.Icon "graphics/HUD/Icons/W_Pusher.png";
Weapon.UpSound "pusher/select";
Weapon.SlotNumber 1;
Weapon.SlotPriority 2.;
Weapon.SelectionOrder 1500;
Stamina 10000;
+WEAPON.MELEEWEAPON;
Radius 20;
Height 32;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Select:
XZW2 G 2 A_FullRaise();
XZW2 HIJKL 2;
Goto Ready;
Ready:
XZW2 A 1 A_WeaponReady(WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1);
Wait;
Fire:
XZW2 A 2
{
A_WeaponOffset(0,32);
invoker.chargelevel = .2;
invoker.hitcnt = 0;
A_StartSound("pusher/start",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("pusher/motor",CHAN_WEAPONEXTRA,CHANF_LOOP,pitch:1.5);
}
XZW2 M 2;
XZW2 N 0 A_StartSound("pusher/drill",CHAN_WEAPON,CHANF_LOOP);
Hold:
XZW2 N 1
{
A_WeaponOffset(FRandom[Pusher](-1,1),32+FRandom[Pusher](-1,1));
A_Recoil(cos(pitch));
}
XZW2 O 1 A_PusherDrill();
XZW2 P 1
{
A_WeaponOffset(FRandom[Pusher](-1,1),32+FRandom[Pusher](-1,1));
A_Recoil(cos(pitch));
}
XZW2 Q 1 A_PusherDrill();
XZW2 R 1
{
A_WeaponOffset(FRandom[Pusher](-1,1),32+FRandom[Pusher](-1,1));
A_Recoil(cos(pitch));
}
XZW2 S 1 A_PusherDrill();
XZW2 N 2
{
if ( player.cmd.buttons&BT_ATTACK )
return ResolveState("Hold");
invoker.chargelevel *= .4;
A_Recoil(.5*cos(pitch));
A_ClearRefire();
A_WeaponOffset(0,32);
A_StopSound(CHAN_WEAPON);
A_StartSound("pusher/stop",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("pusher/motorend",CHAN_WEAPONEXTRA,CHANF_DEFAULT,pitch:1.5);
return ResolveState(null);
}
XZW2 T 2 { invoker.chargelevel = 0.; }
Goto Ready;
Dummy:
TNT1 A 1 A_ChargeUp();
Wait;
AltFire:
XZW2 A 2
{
A_StartSound("pusher/start",CHAN_WEAPON,CHANF_OVERLAP);
A_StartSound("pusher/motor",CHAN_WEAPONEXTRA,CHANF_LOOP,pitch:1.5);
A_StartSound("pusher/pullback",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW2 UVW 2;
XZW2 X 0 A_BeginCharge();
XZW2 XYZ 3;
XZW3 ABCDEFG 3;
XZW3 H 0
{
A_QuakeEx(1,1,1,8,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.5);
A_StartSound("pusher/motorend",CHAN_WEAPONEXTRA,CHANF_DEFAULT,pitch:1.5);
}
XZW3 H 3;
Wait;
AltRelease:
#### # 1 A_PusherAlt();
XZW5 R 2;
XZW3 IJKL 2;
XZW3 MNOP 3;
Goto Ready;
AltMiss:
XZW3 QRSTUVW 1;
TNT1 A -1
{
let nw = player.mo.PickNextWeapon();
// gross hack (don't prioritize Deep Impact if we have something better than it)
if ( nw is 'DeepImpact' )
{
player.ReadyWeapon = nw;
nw = player.mo.PickNextWeapon();
player.ReadyWeapon = invoker;
}
if ( nw != invoker ) player.PendingWeapon = nw;
RemoveInventory(invoker);
}
Stop;
Reload:
Zoom:
XZW2 A 2
{
A_StartSound("pusher/checkout",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerCheckGun();
}
XZW3 XYZ 2;
XZW4 ABCDEFGHIJKLMNOPQR 2;
XZW4 STUVWXYZ 3;
Goto Ready;
User1:
XZW2 A 2
{
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerMelee();
}
XZW5 ABC 2;
XZW5 D 1 A_Parry(9);
XZW5 EFGH 1;
XZW5 I 0 A_Melee(70,"demolitionist/whitl",1.1);
XZW5 IJ 2;
XZW5 K 2 { invoker.PlayUpSound(self); }
XZW5 LM 2;
XZW5 NOPQ 3;
Goto Ready;
Deselect:
XZW2 B 2 A_StartSound("pusher/deselect",CHAN_WEAPON,CHANF_OVERLAP);
XZW2 CDEF 2;
XZW2 F -1 A_FullLower();
Stop;
}
}