Partial implementation of Fuck Your Shit rounds, currently in progress. Added various shader effects to some powerups, and to player damage. Added custom view effects to player death, disabled "face attacker" because it looks weird with model-based players. Added "untouchable" spree tracking to the Stats tab. Implemented "emergency reboot system" for people who want a less shameful form of the Resurrect cheat. Cooldown for consecutive reboots can be configured. Rebalanced armors. Small language string corrections. Adjusted pickup model sizes of some weapons. Fixed missing punch sound (damn typos). Fix targetter always displaying voodoo dolls. Fix uptime breaking when loading saves, now based on total playtime rather than gametic. Readjusted Spreadgun ammo availability. Flush HUD interpolators alongside messages, fixes things such as the score VERY slowly counting up when loading a save. Spare armors now only get auto-used on pickup if there is NO armor available of that type. Added some extra visual effects to punching walls and non-bleeding actors. Slightly altered the melee range so it's not as awkward. Fixed punching not using flesh sounds for bleeding actors. Pusher primary now drags the player towards their target, like the Chainsaw. Fixed the player having no pain sounds whatsoever. Fixed stair step anchoring not working. The damage dealt when walljumping on a monster now also gets boosted by the Ragekit.
641 lines
18 KiB
Text
641 lines
18 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;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
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](5,10);
|
|
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](4,12);
|
|
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](4,8);
|
|
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;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
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,20);
|
|
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,12);
|
|
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](10,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
|
|
{
|
|
bool canpickup;
|
|
Vector3 oldvel;
|
|
double oldangle, oldpitch;
|
|
Actor lasthit;
|
|
|
|
Default
|
|
{
|
|
Obituary "$O_PUSHER";
|
|
Speed 50;
|
|
Radius 10;
|
|
Height 10;
|
|
DamageFunction int(3*vel.length());
|
|
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_LOOPING,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,5,data:d);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
angle = oldangle;
|
|
pitch = oldpitch;
|
|
ClearBounce();
|
|
special1 = 1;
|
|
ExplodeMissile(BlockingLine,BlockingMobj);
|
|
}
|
|
}
|
|
void A_HandleBounce()
|
|
{
|
|
Vector3 HitNormal = -vel.unit();
|
|
F3DFloor ff;
|
|
if ( BlockingFloor )
|
|
{
|
|
// find closest 3d floor for its normal
|
|
for ( int i=0; i<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
|
|
ff = CurSector.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<CurSector.Get3DFloorCount(); i++ )
|
|
{
|
|
if ( !(CurSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
|
|
ff = CurSector.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 ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
|
|
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;
|
|
// 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(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);
|
|
bNOGRAVITY = true;
|
|
}
|
|
else pitch = 0;
|
|
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.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);
|
|
SWWMHandler.DoKnockback(target,vel.unit(),85000);
|
|
return damage;
|
|
}
|
|
override void Touch( Actor toucher )
|
|
{
|
|
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");
|
|
Destroy();
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A 1 A_Reorient();
|
|
Wait;
|
|
Bounce:
|
|
XZW1 A 0 A_HandleBounce();
|
|
Goto Spawn;
|
|
Death:
|
|
XZW1 A -1 A_BecomePickup();
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class PusherWeapon : SWWMWeapon
|
|
{
|
|
double chargelevel, vibe;
|
|
|
|
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-61,by-10,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
|
|
int chg = clamp(ChargeInter?ChargeInter.GetValue():int(chargelevel*100),0,100);
|
|
int cw = int(chg*56./100.);
|
|
Screen.DrawTexture(ChargeBar,false,bx-59,by-8,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRight,cw);
|
|
}
|
|
|
|
override void HudTick()
|
|
{
|
|
if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(int(chargelevel*100),.5,1,50);
|
|
ChargeInter.Update(int(chargelevel*100));
|
|
}
|
|
|
|
override bool ReportHUDAmmo()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
action void A_PusherDrill()
|
|
{
|
|
invoker.chargelevel = clamp(invoker.chargelevel+FRandom[Pusher](-.1,.2),.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(-1.);
|
|
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*z);
|
|
FLineTraceData d;
|
|
LineTrace(angle,60,pitch,TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
A_QuakeEx(3,3,3,7,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
|
|
A_AlertMonsters(1200);
|
|
int dmg = int(3+invoker.chargelevel*2);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
|
|
self.angle += clamp(diff,-5.,5.);
|
|
SWWMHandler.DoKnockback(d.HitActor,d.HitDir,8500);
|
|
d.HitActor.A_QuakeEx(4,4,4,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.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_ITEMEXTRA,CHANF_OVERLAP);
|
|
}
|
|
// move towards target
|
|
vel.xy += Vec2To(d.HitActor).unit();
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
int numpt = Random[Pusher](3,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("SWWMViewSmoke",origin);
|
|
SWWMViewSmoke(s).ofs = (10,2,-3);
|
|
s.target = self;
|
|
s.special1 = 1;
|
|
s.scale *= 1.4;
|
|
s.alpha *= .1;
|
|
}
|
|
A_AlertMonsters(500);
|
|
}
|
|
|
|
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));
|
|
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);
|
|
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*z);
|
|
FLineTraceData d;
|
|
LineTrace(angle,80,pitch,TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
|
|
bool gone = false;
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
A_QuakeEx(8,8,8,12,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
|
|
A_AlertMonsters(1600);
|
|
int dmg = int(80*invoker.chargelevel);
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
|
|
self.angle += clamp(diff,-5.,5.);
|
|
SWWMHandler.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.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_ITEMEXTRA,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);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
if ( !gone )
|
|
{
|
|
int numpt = Random[Pusher](3,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("SWWMViewSmoke",origin);
|
|
SWWMViewSmoke(s).ofs = (10,2,-3);
|
|
s.target = self;
|
|
s.special1 = 2;
|
|
s.scale *= 1.4;
|
|
s.alpha *= .1;
|
|
}
|
|
}
|
|
A_AlertMonsters(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+.035);
|
|
if ( invoker.chargelevel >= .5 ) invoker.vibe = min(1.,invoker.vibe+.02);
|
|
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*z);
|
|
int numpt = Random[Pusher](3,5);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("SWWMViewSmoke",origin);
|
|
SWWMViewSmoke(s).ofs = (10,2,-3);
|
|
s.target = self;
|
|
s.special1 = 1;
|
|
s.scale *= 1.4;
|
|
s.alpha *= .1*(invoker.chargelevel-invoker.vibe);
|
|
}
|
|
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";
|
|
Weapon.UpSound "pusher/select";
|
|
Weapon.SlotNumber 1;
|
|
Weapon.SelectionOrder 2800;
|
|
Stamina 10000;
|
|
}
|
|
|
|
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;
|
|
A_StartSound("pusher/start",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_StartSound("pusher/motor",CHAN_WEAPONEXTRA,CHANF_LOOPING,pitch:1.5);
|
|
}
|
|
XZW2 M 2;
|
|
XZW2 N 0 A_StartSound("pusher/drill",CHAN_WEAPON,CHANF_LOOPING);
|
|
Hold:
|
|
XZW2 N 1
|
|
{
|
|
A_WeaponOffset(FRandom[Pusher](-1,1),32+FRandom[Pusher](-1,1));
|
|
A_Recoil(1.);
|
|
}
|
|
XZW2 O 1 A_PusherDrill();
|
|
XZW2 P 1
|
|
{
|
|
A_WeaponOffset(FRandom[Pusher](-1,1),32+FRandom[Pusher](-1,1));
|
|
A_Recoil(1.);
|
|
}
|
|
XZW2 Q 1 A_PusherDrill();
|
|
XZW2 R 1
|
|
{
|
|
A_WeaponOffset(FRandom[Pusher](-1,1),32+FRandom[Pusher](-1,1));
|
|
A_Recoil(1.);
|
|
}
|
|
XZW2 S 1 A_PusherDrill();
|
|
XZW2 N 2
|
|
{
|
|
if ( player.cmd.buttons&BT_ATTACK )
|
|
return ResolveState("Hold");
|
|
invoker.chargelevel *= .4;
|
|
A_Recoil(.5);
|
|
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_LOOPING,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 { RemoveInventory(invoker); }
|
|
Stop;
|
|
Reload:
|
|
Zoom:
|
|
XZW2 A 2 A_StartSound("pusher/checkout",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW3 XYZ 2;
|
|
XZW4 ABCDEFGHIJKLMNOPQR 2;
|
|
XZW4 STUVWXYZ 3;
|
|
Goto Ready;
|
|
User1:
|
|
XZW2 A 2 A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
|
|
XZW5 ABCD 2;
|
|
XZW5 EFGH 1;
|
|
XZW5 I 0 A_Melee(70);
|
|
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;
|
|
}
|
|
}
|