swwmgz_m/zscript/weapons/swwm_jackhammer_fx.zsc

376 lines
10 KiB
Text

// Pusher projectiles and effects
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](1,3);
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](0,2);
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](1,3);
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;
bool bUsePickup;
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 )
{
if ( toucher.player && swwm_usetopickup && !bUsePickup )
return;
// 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;
}
}
bUsePickup = true;
Touch(user);
bUsePickup = false;
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;
}
}