swwmgz_m/zscript/swwm_deepdarkimpact.zsc
Marisa Kirisame 79ff46d1a4 Eviscerator now finally implemented.
Deep Impact alt can now be used at any moment, but its potential damage will be lower.
In addition, the Deep Impact now deals rip damage only once per target, and on top deals slightly reduced damage to bosses.
2020-03-12 23:03:10 +01:00

491 lines
13 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 AirBullet : FastProjectile
{
int tcnt;
Actor lasthit;
Default
{
Radius 2;
Height 4;
DamageFunction 100;
Speed 400;
PROJECTILE;
+FORCERADIUSDMG;
+NODAMAGETHRUST;
+RIPPER;
}
override void PostBeginPlay()
{
A_StartSound("deepimpact/bullet",CHAN_BODY,CHANF_LOOPING,1.,1.5);
}
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
{
if ( target.bBOSS ) damage = int(damage*.4);
if ( !bAMBUSH )
{
if ( target == lasthit ) return 0;
lasthit = target;
}
SWWMHandler.DoKnockback(target,vel.unit(),10000*specialf1);
return int(damage*specialf1);
}
override void Effect()
{
let r = Spawn("AirBulletRing",pos);
r.angle = angle;
r.pitch = pitch;
r.roll = FRandom[Impact](0,360);
r.scale *= .3;
r.alpha *= .6;
int numpt = Random[ExploS](2,4);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.1,.8);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel+vel.unit()*3.;
s.SetShade(Color(1,1,1)*Random[ExploS](240,255));
s.special1 = Random[ExploS](1,2);
s.scale *= 1.8;
s.alpha *= .2;
}
bAMBUSH = true;
A_Explode(GetMissileDamage(0,0),80,0);
bAMBUSH = false;
A_AlertMonsters(400);
tcnt++;
if ( tcnt < 2 ) return;
tcnt = 0;
Spawn("AirRingLight",pos);
}
void A_Splode()
{
A_AlertMonsters();
A_QuakeEx(6,6,6,20,0,250,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
A_StartSound("deepimpact/bullethit",CHAN_VOICE,CHANF_DEFAULT,1.,.3);
A_SprayDecal("ImpactMark");
Spawn("AirBulletLight",pos);
int numpt = Random[ExploS](10,20);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
let s = Spawn("SWWMSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,1,1)*Random[ExploS](240,255));
s.special1 = Random[ExploS](1,2);
s.scale *= 2.4;
s.alpha *= .4;
}
numpt = Random[ExploS](8,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,6);
let s = Spawn("SWWMSpark",pos);
s.vel = pvel;
}
numpt = Random[ExploS](6,16);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
}
States
{
Spawn:
TNT1 A -1;
Stop;
Death:
TNT1 A 1 A_Splode();
Stop;
}
}
Class AirBulletLight : PaletteLight
{
Default
{
Tag "ImpactWav";
ReactionTime 5;
Args 0,0,0,150;
}
}
Class AirRingLight : PaletteLight
{
Default
{
Tag "ImpactWav2";
ReactionTime 30;
Args 0,0,0,120;
}
}
Class AirBulletRing : Actor
{
Default
{
RenderStyle "Add";
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
Radius 0.1;
Height 0;
}
States
{
Spawn:
XRG1 ABCDEFGHIJKLMNOPQRSTUVWX 2;
Stop;
}
}
Class DeepTracer : LineTracer
{
Actor ignoreme;
Array<Actor> hitlist;
Array<double> hitdist;
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
hitlist.Push(Results.HitActor);
hitdist.Push(Results.Distance);
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;
}
Class DeepImpact : SWWMWeapon
{
int clipcount;
double charge;
bool charging;
transient ui TextureID WeaponBox, AmmoBar;
transient ui DynamicValueInterpolator ChargeInter;
transient ui Font TewiFont;
Property ClipCount : clipcount;
override void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
{
if ( !WeaponBox ) WeaponBox = TexMan.CheckForTexture("graphics/HUD/DeepImpactDisplay.png",TexMan.Type_Any);
if ( !TewiFont ) TewiFont = Font.GetFont('TewiShaded');
if ( !AmmoBar ) AmmoBar = TexMan.CheckForTexture("graphics/HUD/DeepImpactBar.png",TexMan.Type_Any);
Screen.DrawTexture(WeaponBox,false,bx-37,by-55,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int chg = clamp(ChargeInter?ChargeInter.GetValue():clipcount,0,100);
int ct = int(((by-3)-(chg*50./100.))*hs.y);
Screen.DrawTexture(AmmoBar,false,bx-8,by-53,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ClipTop,ct);
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-36,by-14,String.Format("%3d%%",chg),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
override void HudTick()
{
if ( !ChargeInter ) ChargeInter = DynamicValueInterpolator.Create(clipcount,.5,1,25);
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(100);
}
action void A_ChargeUp()
{
invoker.charge += 1./TICRATE;
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"));
}
}
action void A_DryFire()
{
let weap = Weapon(invoker);
if ( !weap ) return;
A_StartSound("deepimpact/dryfire",CHAN_WEAPON,CHANF_OVERLAP,.5);
A_AlertMonsters(70);
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[Impact](5,7);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Impact](-.1,.1),FRandom[Impact](-.1,.1),FRandom[Impact](-.1,.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_QuakeEx(1,1,1,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.05);
A_StartSound("deepimpact/fire",CHAN_WEAPON,CHANF_OVERLAP);
A_Recoil(.05);
A_AlertMonsters(300);
invoker.clipcount = max(0,invoker.clipcount-3);
Vector3 x, y, z, dir;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-3*z);
DeepTracer t = new("DeepTracer");
t.ignoreme = self;
Array<THitList> list;
list.Clear();
int rings = 1;
for ( double i=0; i<1.; i+=.05 )
{
for ( int j=0; j<360; j+=(360/rings) )
{
dir = (x+y*cos(j)*i+z*sin(j)*i).unit();
t.hitlist.Clear();
t.hitdist.Clear();
t.Trace(origin,level.PointInSector(origin.xy),dir,150-i*50,0);
for ( int i=0; i<t.hitlist.Size(); i++ )
{
int inl = -1;
for ( int k=0; k<list.Size(); k++ )
{
if ( list[k].a != t.hitlist[i] ) continue;
inl = k;
break;
}
if ( inl == -1 )
{
THitList l = new("THitList");
l.a = t.hitlist[i];
inl = list.Push(l);
}
list[inl].nhits++;
list[inl].avgdir += dir;
list[inl].avgdist += t.hitdist[i];
}
if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
}
rings += 5;
}
for ( int i=0; i<list.Size(); i++ )
{
Vector3 avgdir = list[i].avgdir/list[i].nhits;
double avgdist = list[i].avgdist/list[i].nhits;
double dmg = 4000.*(1.-clamp(avgdist/300.,0.,1.));
if ( !list[i].a.bCANNOTPUSH && (list[i].a.mass < LARGE_MASS) )
list[i].a.vel += ((avgdir+(0,0,.35))*dmg/max(50,list[i].a.mass));
list[i].a.DamageMobj(self,self,int(dmg/500.),'Push',DMG_THRUSTLESS);
}
let ti = ThinkerIterator.Create("Actor");
Actor m;
while ( m = Actor(ti.Next()) )
{
if ( !m.bMISSILE ) continue;
double rdist = level.Vec3Diff(origin,m.pos).length();
Vector3 rdir = level.Vec3Diff(origin,m.pos).unit();
if ( LineTrace(atan2(rdir.y,rdir.x),rdist,asin(-rdir.z),TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y) || (rdist > 150) || (rdir dot x < 0.7) ) continue;
m.speed = m.vel.length();
m.vel = m.speed*(m.vel.unit()*.2+rdir).unit();
if ( m.target == self ) continue;
if ( m.bSEEKERMISSILE ) m.tracer = m.target;
m.target = self;
}
int numpt = Random[Impact](7,12);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Impact](-.2,.2),FRandom[Impact](-.2,.2),FRandom[Impact](-.2,.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_AltBullet()
{
let weap = Weapon(invoker);
if ( !weap ) return;
A_StopSound(CHAN_WEAPONEXTRA);
A_QuakeEx(6,6,6,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.7);
A_Recoil(4.);
A_StartSound("deepimpact/altfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.5);
A_AlertMonsters();
invoker.clipcount = 0;
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+2*y-3*z);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.002);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
Vector3 dir = (x2+y2*cos(a)*s+z2*sin(a)*s).unit();
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;
p.specialf1 = invoker.charge;
invoker.charge = 0;
int numpt = Random[Impact](14,18);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (x+(FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.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+20);
A_StartSound("deepimpact/reload",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(1,1,1,7,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.05);
}
action void A_NoCrank()
{
A_StartSound("deepimpact/noreload",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(1,1,1,2,0,1,"",QF_RELATIVE|QF_SCALEUP,rollIntensity:.05);
}
Default
{
Tag "$T_DEEPIMPACT";
Inventory.PickupMessage "$I_DEEPIMPACT";
Obituary "$O_DEEPIMPACT";
Weapon.UpSound "deepimpact/select";
Weapon.SlotNumber 1;
Weapon.SelectionOrder 3000;
Stamina 6000;
DeepImpact.ClipCount 100;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Select:
XZW2 I 2 A_FullRaise();
XZW2 JKLMNOP 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 4;
Goto Ready;
Fire:
XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"DryFire");
XZW2 A 1 A_Air();
XZW2 QR 2;
XZW2 STUA 3;
Goto Ready;
AltFire:
XZW2 A 0 A_JumpIf(invoker.ClipCount<=0,"DryFire");
XZW2 A 0 A_JumpIf(invoker.ClipCount<=3,"Fire");
XZW2 A 0 A_BeginCharge();
XZW2 A 1 A_ChargeUp();
Wait;
AltRelease:
XZW2 A 1 A_AltBullet();
XZW2 VW 1;
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;
ReloadHold:
XZW3 K 2 A_Crank();
XZW3 LM 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;
Goto Ready;
NoReload:
XZW2 A 2 A_StartSound("deepimpact/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 HIJ 2;
NoReloadHold:
XZW3 K 2 A_NoCrank();
XZW3 L 2;
XZW3 M 5;
XZW3 L 3;
XZW3 K 2 A_StartSound("deepimpact/reloadend",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 RSTUV 2;
Goto Ready;
Zoom:
XZW2 A 3 A_StartSound("deepimpact/checkout",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 WXYZ 3;
XZW4 AB 4;
XZW4 CD 3;
XZW4 EFG 2;
Goto Ready;
User1:
XZW2 A 3 A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 H 4 A_StartSound("demolitionist/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 I 3;
XZW4 J 2;
XZW4 KLM 1;
XZW4 N 3 A_Melee(60);
XZW4 O 3 { invoker.PlayUpSound(self); }
XZW4 PQRSTUV 2;
Goto Ready;
Deselect:
XZW2 A 2 A_StartSound("deepimpact/deselect",CHAN_WEAPON,CHANF_OVERLAP);
XZW2 BCDEFGHI 2;
XZW2 I -1 A_FullLower();
Stop;
}
}