swwmgz_m/zscript/weapons/swwm_deepdarkimpact.zsc

488 lines
14 KiB
Text

// Dr. Locke's Mighty Wolf Breath Airgun aka "Deep Impact" Airblaster (from SWWM series)
// Slot 1, replaces Fist, Staff, Hexen starting weapons
Class DeepTracer : LineTracer
{
Array<Actor> hitlist;
Array<double> hitdist;
Array<double> hitposx, hitposy, hitposz;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor.bSHOOTABLE )
{
hitlist.Push(Results.HitActor);
hitdist.Push(Results.Distance);
hitposx.Push(Results.HitPos.x);
hitposy.Push(Results.HitPos.y);
hitposz.Push(Results.HitPos.z);
return TRACE_Skip;
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class THitList
{
Actor a;
int nhits;
Vector3 avgdir;
double avgdist;
Vector3 avgpos;
}
Class DeepImpact : SWWMWeapon
{
int clipcount;
double charge;
bool charging;
int wastecycle; // for easter egg
transient ui SmoothDynamicValueInterpolator ChargeInter;
transient int failtime;
Property ClipCount : clipcount;
override void HudTick()
{
Super.HudTick();
if ( !ChargeInter ) ChargeInter = SmoothDynamicValueInterpolator.Create(clipcount,.5);
ChargeInter.Update(clipcount);
}
override bool ReportHUDAmmo()
{
return (ClipCount>0);
}
action void A_BeginCharge()
{
invoker.charge = 0;
A_StartSound("deepimpact/charge",CHAN_WEAPONEXTRA);
A_QuakeEx(2.,2.,2.,35,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.2);
A_AlertMonsters(swwm_uncapalert?0:100);
}
action void A_ChargeUp()
{
invoker.charge += 1./GameTicRate;
A_WeaponOffset(FRandom[Impact](-1,1)*invoker.charge,32+FRandom[Impact](-1,1)*invoker.charge);
if ( invoker.charge >= (invoker.clipcount*.01) )
{
A_WeaponOffset(0,32);
player.SetPSprite(PSP_WEAPON,ResolveState("AltRelease"));
}
}
override Vector3 GetTraceOffset( int index )
{
return (10.,2.,-3.);
}
action void A_DryFire()
{
let weap = Weapon(invoker);
if ( !weap ) return;
A_BumpView(-.05);
A_QuakeEx(.1,.1,.1,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.05);
A_StartSound("deepimpact/dryfire",CHAN_WEAPON,CHANF_OVERLAP,.5);
A_AlertMonsters(swwm_uncapalert?0:70);
Vector3 x = SWWMUtility.GetPlayerViewDir(self);
Vector3 origin = SWWMUtility.GetFireOffset(self,10,2,-3);
int numpt = Random[Impact](5,7);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+SWWMUtility.Vec3FromAngles(FRandom[Impact](0,360),FRandom[Impact](-90,90))*.1).unit()*FRandom[Impact](.1,.4);
let s = Spawn("SWWMSmoke",origin);
s.vel = pvel;
s.scale *= .3;
s.alpha *= .05;
s.SetShade(Color(1,1,1)*Random[Impact](192,255));
}
}
action void A_Air()
{
let weap = Weapon(invoker);
if ( !weap ) return;
A_BumpFOV(.995);
A_BumpView(-.2);
A_QuakeEx(.8,.8,.8,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.05);
A_StartSound("deepimpact/fire",CHAN_WEAPON,CHANF_OVERLAP);
A_AlertMonsters(swwm_uncapalert?0:300);
A_PlayerFire();
invoker.clipcount = max(0,invoker.clipcount-3);
Vector3 dir;
let [x, y, z] = SWWMUtility.GetPlayerAxes(self);
SWWMUtility.DoKnockback(self,-x,2000.);
Vector3 origin = SWWMUtility.GetFireOffset(self,10,2,-3);
DeepTracer t = new("DeepTracer");
Array<THitList> list;
list.Clear();
int rings = 1;
int wallhits = 0;
Vector3 hitnormal;
Vector3 wnorm = (0,0,0);
for ( double i=0; i<.35; i+=.05 )
{
for ( int j=0; j<360; j+=(360/rings) )
{
dir = SWWMUtility.ConeSpread(x,y,z,j,i);
t.hitlist.Clear();
t.hitdist.Clear();
t.hitposx.Clear();
t.hitposy.Clear();
t.hitposz.Clear();
t.Trace(origin,level.PointInSector(origin.xy),dir,250-i*150,0,ignore:self);
SWWMBulletTrail.DoTrail(self,origin,dir,250-i*150,0);
for ( int k=0; k<t.hitlist.Size(); k++ )
{
int inl = -1;
for ( int l=0; l<list.Size(); l++ )
{
if ( list[l].a != t.hitlist[k] ) continue;
inl = l;
break;
}
if ( inl == -1 )
{
THitList l = new("THitList");
l.a = t.hitlist[k];
inl = list.Push(l);
}
list[inl].nhits++;
list[inl].avgdir += dir;
list[inl].avgdist += t.hitdist[k];
list[inl].avgpos += (t.hitposx[k],t.hitposy[k],t.hitposz[k]);
}
if ( t.Results.HitType == TRACE_HitWall )
{
t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
wallhits++;
hitnormal = (-t.Results.HitLine.delta.y,t.Results.HitLine.delta.x,0).unit();
if ( !t.Results.Side ) hitnormal *= -1;
wnorm += (hitnormal*.2-dir)/max(50.,t.Results.Distance);
}
else if ( t.Results.HitType == TRACE_HitFloor )
{
wallhits++;
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.top.Normal;
else hitnormal = t.Results.HitSector.floorplane.Normal;
wnorm += (hitnormal*.2-dir)/max(50.,t.Results.Distance);
let b = Spawn("InvisibleSplasher",t.Results.HitPos);
b.target = self;
}
else if ( t.Results.HitType == TRACE_HitCeiling )
{
wallhits++;
if ( t.Results.FFloor ) hitnormal = -t.Results.FFloor.bottom.Normal;
else hitnormal = t.Results.HitSector.ceilingplane.Normal;
wnorm += (hitnormal*.2-dir)/max(50.,t.Results.Distance);
}
if ( swwm_omnibust ) BusterWall.Bust(t.Results,int(5*(1.-clamp(t.Results.Distance/250.,0.,1.))),self,t.Results.HitVector,t.Results.HitPos.z);
}
rings += 5;
}
if ( wallhits > 0 )
{
wnorm /= wallhits;
if ( (pos.z > floorz) && TestMobjZ() ) wnorm *= 2.4;
SWWMUtility.DoKnockback(self,wnorm.unit(),wnorm.length()*5000000.);
}
foreach ( l:list )
{
if ( !l.a ) continue;
Vector3 avgdir = l.avgdir/l.nhits;
double avgdist = l.avgdist/l.nhits;
Vector3 avgpos = l.avgpos/l.nhits;
double dmg = 5000.*(1.-clamp(avgdist/250.,0.,1.));
SWWMUtility.DoKnockback(l.a,avgdir,dmg*35.);
let p = SWWMPuff.Setup(avgpos,avgdir,invoker,self,l.a);
l.a.DamageMobj(p,self,int(dmg/250.),'Push',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
}
let st = Demolitionist(self).mystats;
foreach ( s:level.Sectors ) for ( Actor m=s.thinglist; m; m=m.snext )
{
if ( !SWWMUtility.ValidProjectile(m) ) continue;
Vector3 rdir = level.Vec3Diff(origin,m.pos);
double rdist = rdir.length();
if ( rdist <= 0. ) continue;
rdir /= rdist;
if ( LineTrace(atan2(rdir.y,rdir.x),rdist,asin(-rdir.z),TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y) || (rdist > 250) || (rdir dot x < .75) ) continue;
m.speed = m.vel.length();
m.vel = m.speed*1.5*(-m.vel.unit()*.3+rdir+x*.2).unit();
Vector3 ndir = m.vel.unit();
m.angle = atan2(ndir.y,ndir.x);
m.pitch = asin(-ndir.z);
if ( m.bSEEKERMISSILE && (m.target != self) ) m.tracer = m.target;
m.target = self;
if ( !m.FindInventory("ParriedBuff") )
{
let pb = Inventory(Spawn("ParriedBuff"));
pb.AttachToOwner(m);
pb.bAMBUSH = true;
}
if ( st ) st.parries++;
SWWMUtility.AchievementProgressInc("parry",1,player);
}
int numpt = Random[Impact](7,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+SWWMUtility.Vec3FromAngles(FRandom[Impact](0,360),FRandom[Impact](-90,90))*.2).unit()*FRandom[Impact](.5,4);
let s = Spawn("SWWMSmoke",origin);
s.vel = pvel;
s.special1 = 1;
s.scale *= .5;
s.alpha *= .15;
s.SetShade(Color(1,1,1)*Random[Impact](192,255));
}
}
action void A_DryAltFire()
{
invoker.failtime = gametic+32;
A_StartSound("deepimpact/dryaltfire",CHAN_WEAPON,CHANF_OVERLAP);
A_BumpView(-.05);
A_QuakeEx(.1,.1,.1,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.05);
}
action void A_AltBullet()
{
let weap = Weapon(invoker);
if ( !weap ) return;
A_StopSound(CHAN_WEAPONEXTRA);
A_BumpFOV(.85);
A_BumpView(-5);
A_QuakeEx(6.,6.,6.,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.7);
A_StartSound("deepimpact/altfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.5);
A_AlertMonsters(swwm_uncapalert?0:8000);
A_PlayerFire();
invoker.clipcount = 0;
Vector3 x = SWWMUtility.GetPlayerViewDir(self);
SWWMUtility.DoKnockback(self,-x,42000.);
Vector3 origin = SWWMUtility.GetFireOffset(self,10,2,-3);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.002);
let [x2, y2, z2] = SWWMUtility.GetPlayerAxesAutoAimed(self);
Vector3 dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s);
let p = Spawn("AirBullet",origin);
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed;
invoker.charge = 0;
int numpt = Random[Impact](14,18);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+SWWMUtility.Vec3FromAngles(FRandom[Impact](0,360),FRandom[Impact](-90,90))*.5).unit()*FRandom[Impact](1,6);
let s = Spawn("SWWMSmoke",origin);
s.vel = pvel;
s.special1 = 2;
s.scale *= .7;
s.alpha *= .2;
s.SetShade(Color(1,1,1)*Random[Impact](192,255));
}
}
action void A_Crank()
{
invoker.clipcount = min(invoker.default.clipcount,invoker.clipcount+25);
A_StartSound("deepimpact/reload",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(.3,.3,.3,7,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.05);
invoker.wastecycle = 0;
}
action void A_NoCrank()
{
A_StartSound("deepimpact/noreload",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(.2,.2,.2,2,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.05);
invoker.wastecycle++;
let s = Demolitionist(self).mystats;
if ( s.deepegg < 4 )
{
if ( invoker.wastecycle < (10+s.deepegg*10) ) return;
s.deepegg++;
}
else return;
invoker.wastecycle = 0;
if ( player == players[consoleplayer] )
EventHandler.SendInterfaceEvent(consoleplayer,"swwmsetdialogue.CRANK"..1+(s.deepegg-1));
}
override void MarkPrecacheSounds()
{
Super.MarkPrecacheSounds();
MarkSound("deepimpact/fire");
MarkSound("deepimpact/charge");
MarkSound("deepimpact/altfire");
MarkSound("deepimpact/dryfire");
MarkSound("deepimpact/select");
MarkSound("deepimpact/checkout");
MarkSound("deepimpact/deselect");
MarkSound("deepimpact/bullet");
MarkSound("deepimpact/bullethit1");
MarkSound("deepimpact/bullethit2");
MarkSound("deepimpact/reloadbeg");
MarkSound("deepimpact/reloadend");
MarkSound("deepimpact/reload");
MarkSound("deepimpact/noreload");
}
Default
{
Tag "$T_DEEPIMPACT";
Inventory.Icon "graphics/HUD/Icons/W_DeepImpact.png";
Inventory.PickupMessage "$I_DEEPIMPACT";
Obituary "$O_DEEPIMPACT_WEAK";
SWWMWeapon.Tooltip "$TT_DEEPIMPACT";
SWWMWeapon.GetLine "getdeepimpact";
Weapon.UpSound "deepimpact/select";
Weapon.SlotNumber 1;
Weapon.SelectionOrder 2000;
Stamina 6000;
DeepImpact.ClipCount 100;
+WEAPON.MELEEWEAPON;
+WEAPON.WIMPY_WEAPON;
+WEAPON.NOAUTOSWITCHTO;
+SWWMWEAPON.NOSWAPWEAPON;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Select:
XZW2 I 2 A_FullRaise();
XZW2 J 2 A_BumpView(.2,tics:2);
XZW2 KL 2 A_BumpView(-.2,tics:2);
XZW2 MN 2 A_BumpView(-.1,tics:2);
XZW2 OP 2 A_BumpView(.1,tics:2);
Goto Ready;
Ready:
XZW2 A 1 A_WeaponReady(WRF_ALLOWRELOAD|WRF_ALLOWUSER1|WRF_ALLOWZOOM);
Wait;
DryFire:
XZW2 A 1 A_DryFire();
XZW2 QR 2;
XZW2 TU 3;
XZW2 A 2;
Goto Ready;
Fire:
XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"DryFire");
XZW2 A 1 A_Air();
XZW2 QR 2;
XZW2 STU 3;
Goto Ready;
AltFire:
XZW2 A 0 A_JumpIf(invoker.ClipCount<100,"DryAltFire");
XZW2 A 0 A_BeginCharge();
XZW2 A 1 A_ChargeUp();
XZW2 V 1 A_ChargeUp();
Wait;
DryAltFire:
// the useless goddess
XZW2 A 2 A_DryAltFire();
XZW2 Q 4;
XZW2 U 5;
XZW2 A 2;
Goto Ready;
AltRelease:
XZW2 V 1 A_AltBullet();
XZW2 W 1 A_BumpView(.15,tics:15);
XZW2 X 2;
XZW2 YZ 3;
XZW3 AB 5;
XZW3 CD 4;
XZW3 EFG 3;
Goto Ready;
Reload:
XZW2 A 0 A_JumpIf(invoker.clipcount>=invoker.default.clipcount,"NoReload");
XZW2 A 2 A_StartSound("deepimpact/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 HIJ 2 A_BumpView(.1,tics:2);
ReloadHold:
XZW3 K 2
{
A_Crank();
A_BumpView(-.1,tics:2);
}
XZW3 LM 2 A_BumpView(-.2,tics:2);
XZW3 NOPQ 3;
XZW3 K 0 A_JumpIf((player.cmd.buttons&(BT_RELOAD|BT_ALTATTACK))&&(invoker.clipcount<invoker.default.clipcount),"ReloadHold");
XZW3 K 2 A_StartSound("deepimpact/reloadend",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 RSTUV 2 A_BumpView(.1,tics:2);
Goto Ready;
NoReload:
XZW2 A 2 A_StartSound("deepimpact/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 HIJ 2 A_BumpView(.1,tics:2);
NoReloadHold:
XZW3 K 2
{
A_NoCrank();
A_BumpView(-.1,tics:2);
}
XZW3 L 2 A_BumpView(-.2,tics:2);
XZW3 M 5 A_BumpView(-.2,tics:2);
XZW3 L 3;
XZW3 K 2 A_StartSound("deepimpact/reloadend",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 RSTUV 2 A_BumpView(.1,tics:2);
Goto Ready;
Zoom:
XZW2 A 3
{
A_StartSound("deepimpact/checkout",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerCheckGun();
A_BumpView(.2,tics:3);
}
XZW3 WXYZ 3 A_BumpView(-.2,tics:3);
XZW4 AB 4 A_BumpView(-.1,tics:4);
XZW4 CD 3 A_BumpView(.2,tics:3);
XZW4 EFG 2 A_BumpView(.1,tics:2);
Goto Ready;
User1:
XZW2 A 2
{
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerMelee();
}
XZW4 H 2
{
A_BumpView(.5,tics:4);
A_StartSound("demolitionist/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW4 I 2;
XZW4 J 1
{
A_BumpFOV(.98);
A_QuakeEx(.5,.5,.5,8,0,15,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:.2);
A_Parry(9);
}
XZW4 KLM 1 A_BumpView(-1);
XZW4 N 4 A_Melee(60,"demolitionist/whits");
XZW4 O 3 { invoker.PlayUpSound(self); }
XZW4 PQRSTUV 2;
Goto Ready;
Deselect:
XZW2 A 2
{
A_StartSound("deepimpact/deselect",CHAN_WEAPON,CHANF_OVERLAP);
A_BumpView(-.2,tics:2);
}
XZW2 BC 2 A_BumpView(-.1,tics:2);
XZW2 DE 2 A_BumpView(-.1,tics:2);
XZW2 FG 2 A_BumpView(.1,tics:2);
XZW2 H 2 A_BumpView(.2,tics:2);
XZW2 I -1 A_FullLower();
Stop;
}
}