410 lines
13 KiB
Text
410 lines
13 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 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, double 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_BumpFOV(1.01);
|
|
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(4+invoker.chargelevel*8);
|
|
if ( invoker.chargelevel > .4 ) invoker.chargelevel -= FRandom[Pusher](.01,.03);
|
|
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);
|
|
let p = SWWMPuff.Setup(d.HitLocation,d.HitDir,invoker,self,d.HitActor);
|
|
dmg = d.HitActor.DamageMobj(p,self,dmg,'Tenderize',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
|
|
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,50000);
|
|
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_BumpFOV(1.+invoker.chargelevel*.2);
|
|
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(240*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);
|
|
let p = SWWMPuff.Setup(d.HitLocation,d.HitDir,invoker,self,d.HitActor);
|
|
dmg = d.HitActor.DamageMobj(p,self,dmg,'Tenderize',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
|
|
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"));
|
|
invoker.bNODEATHDESELECT = true; // prevent any glitching that could happen if the sequence is interrupted
|
|
A_StopSound(CHAN_WEAPON);
|
|
A_StopSound(CHAN_WEAPONEXTRA);
|
|
A_StartSound("pusher/miss",CHAN_WEAPON,CHANF_OVERLAP);
|
|
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"));
|
|
}
|
|
|
|
override void MarkPrecacheSounds()
|
|
{
|
|
Super.MarkPrecacheSounds();
|
|
MarkSound("pusher/select");
|
|
MarkSound("pusher/deselect");
|
|
MarkSound("pusher/motor");
|
|
MarkSound("pusher/motorend");
|
|
MarkSound("pusher/start");
|
|
MarkSound("pusher/drill");
|
|
MarkSound("pusher/hit1");
|
|
MarkSound("pusher/hit2");
|
|
MarkSound("pusher/hit3");
|
|
MarkSound("pusher/meat1");
|
|
MarkSound("pusher/meat2");
|
|
MarkSound("pusher/meat3");
|
|
MarkSound("pusher/end");
|
|
MarkSound("pusher/checkout");
|
|
MarkSound("pusher/pullback");
|
|
MarkSound("pusher/altfire1");
|
|
MarkSound("pusher/altfire2");
|
|
MarkSound("pusher/miss");
|
|
MarkSound("pusher/althit1");
|
|
MarkSound("pusher/althit2");
|
|
MarkSound("pusher/altmeat1");
|
|
MarkSound("pusher/altmeat2");
|
|
MarkSound("pusher/fly");
|
|
MarkSound("pusher/bounce1");
|
|
MarkSound("pusher/bounce2");
|
|
MarkSound("pusher/bounce3");
|
|
}
|
|
|
|
Default
|
|
{
|
|
//$Title Pusher
|
|
//$Group Weapons
|
|
//$Sprite graphics/HUD/Icons/W_Pusher.png
|
|
//$Icon weapon
|
|
Tag "$T_PUSHER";
|
|
Inventory.PickupMessage "$I_PUSHER";
|
|
Obituary "$O_PUSHER";
|
|
SWWMWeapon.Tooltip "$TT_PUSHER";
|
|
SWWMWeapon.GetLine "getpusherweapon";
|
|
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;
|
|
//+WEAPON.NOAUTOSWITCHTO;
|
|
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_WEAPONEXTRA2,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_WEAPONEXTRA2);
|
|
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
|
|
{
|
|
invoker.bNODEATHDESELECT = false;
|
|
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);
|
|
invoker.Destroy();
|
|
}
|
|
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,1.2,1.2);
|
|
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;
|
|
}
|
|
}
|