swwmgz_m/zscript/swwm_deepdarkimpact.zsc
Marisa Kirisame aabc9de051 0.9.10b release (oh boy where do I start):
- New fun options implemented (omnibusting, unlimited fuel, party time)
 - Biospark Carbine gets a requested nerf
 - Candygun combo fire has been buffed (watch out for that splash damage)
 - All powerup effects are additive (stacc 'em)
 - Automap hud respects gzdoom's cvars for toggling certain elements
 - Automap hud shows stats and times in gold when 100% / under par
 - Weapons and ammo are no longer affected by skill amount modifiers, for balance (and to avoid any weird glitches)
 - Sorting improvements for menu (weapons by slot, ammo by weapons, other items by value, etc.)
 - Grilled Cheese Sandwich now saves you from lethal falls properly
 - Blown kisses instakill nazis
 - Added non-violent Keen replacement (based on "Less mean-spirited Keen replacement" by SiFi270)
 - Added gib deaths for hell nobles, pinkies, cacos, revs and viles (sprites by Amuscaria and Ryan Cordell)
 - Blown kisses can activate use switches
 - Gestures can be chained by pressing a gesture button while another is playing
 - Fixed Grilled Cheese Sandwich not avoiding telefrags properly (now also works with voodoo dolls)
 - More precise weapon kill tracking (fixes some ragekit quirks)
 - Merge both DLC weaponsets, removing redundant weapons (see FuturePlans.md)
 - Discarded some collectables for the next updates, to save time
 - Preparation work for collectables update, including some (partial) lore files
 - Remove ammo fabricators from store, makes no sense to have them when you can just buy ammo directly
 - Cosmetic Boss Brain sprite replacements, just for fun
 - 10 more intermission tips, because yes
 - Added option to reduce distance at which enemy healthbars are picked
 - Various minor bugfixes and adjustments (and also some tiny typo fixes)
 - Ragekit now heals over time and with each hit (so it's more rewarding to go wild)
 - PNG optimization pass (again lol)
 - Fix crouched gestures having no facial animation
2020-10-05 23:29:28 +02:00

564 lines
15 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
{
Obituary "$O_DEEPIMPACT";
Radius 2;
Height 4;
DamageFunction 100;
DamageType 'AirRip';
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;
}
Vector3 dirto = level.Vec3Offset(pos,target.Vec3Offset(0,0,target.Height/2.));
dirto /= dirto.length();
SWWMUtility.DoKnockback(target,vel.unit()*.6+dirto*.4,60000);
return damage;
}
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;
SWWMUtility.DoExplosion(self,GetMissileDamage(0,0),0,60,ignoreme:target);
bAMBUSH = false;
tcnt++;
if ( tcnt < 2 ) return;
tcnt = 0;
Spawn("AirRingLight",pos);
}
void A_Splode()
{
A_AlertMonsters(8000);
if ( target && SWWMUtility.SphereIntersect(target,pos,120) )
{
// push away
Vector3 dir = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.height/2));
double dist = dir.length();
dir /= dist;
double mm = 8000000./max(20.,dist);
if ( (target.pos.z > target.floorz) && target.TestMobjZ() ) mm *= 1.6;
SWWMUtility.DoKnockback(target,dir,mm);
}
SWWMUtility.DoExplosion(self,0,100000,150,ignoreme:target);
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;
}
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,100,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
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;
+NOINTERACTION;
Radius 0.1;
Height 0;
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
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-36,by-54,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
int chg = clamp(ChargeInter?ChargeInter.GetValue():clipcount,0,100);
int ct = int(((by-2)-(chg*50./100.))*hs.y);
Screen.DrawTexture(AmmoBar,false,bx-7,by-52,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ClipTop,ct);
Screen.DrawText(TewiFont,Font.CR_FIRE,bx-35,by-13,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_AlertMonsters(300);
A_PlayerFire();
invoker.clipcount = max(0,invoker.clipcount-3);
Vector3 x, y, z, dir;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMUtility.DoKnockback(self,-x,2000.);
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;
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 = (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*120,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);
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);
}
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/150.,0.,1.))),target,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.);
}
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 = 5000.*(1.-clamp(avgdist/200.,0.,1.));
if ( !list[i].a.bDONTTHRUST && (list[i].a.mass < LARGE_MASS) )
list[i].a.vel += ((avgdir+(0,0,.2))*dmg/max(50,list[i].a.mass));
list[i].a.DamageMobj(invoker,self,int(dmg/350.),'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*1.5*(m.vel.unit()*.2+rdir).unit();
Vector3 ndir = m.vel.unit();
m.angle = atan2(ndir.y,ndir.x);
m.pitch = asin(-ndir.z);
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_StartSound("deepimpact/altfire",CHAN_WEAPON,CHANF_OVERLAP,attenuation:.5);
A_AlertMonsters(8000);
A_PlayerFire();
invoker.clipcount = 0;
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMUtility.DoKnockback(self,-x,42000.);
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;
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_WEAK";
Inventory.Icon "graphics/HUD/Icons/W_DeepImpact.png";
Weapon.UpSound "deepimpact/select";
Weapon.SlotNumber 1;
Weapon.SelectionOrder 2000;
Stamina 6000;
DeepImpact.ClipCount 100;
+WEAPON.MELEEWEAPON;
+WEAPON.WIMPY_WEAPON;
}
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<100,"Reload");
XZW2 A 0 A_BeginCharge();
XZW2 A 1 A_ChargeUp();
XZW2 V 1 A_ChargeUp();
Wait;
AltRelease:
XZW2 V 1 A_AltBullet();
XZW2 W 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);
A_PlayerCheckGun();
}
XZW3 WXYZ 3;
XZW4 AB 4;
XZW4 CD 3;
XZW4 EFG 2;
Goto Ready;
User1:
XZW2 A 2
{
A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerMelee();
}
XZW4 H 2 A_StartSound("demolitionist/reloadbeg",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 I 2;
XZW4 J 1 A_Parry(9);
XZW4 KLM 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);
XZW2 BCDEFGHI 2;
XZW2 I -1 A_FullLower();
Stop;
}
}