swwmgz_m/zscript/weapons/swwm_splode.zsc
Marisa the Magician 6aedf3e902 Explodium Guns should always reload simultaneously.
Previously this was done "by priority" back when the reload animation
required both hands, but now there's no need for that.
2022-09-26 17:30:54 +02:00

1191 lines
34 KiB
Text

// Munch Innovations Explodium Gun (from SWWM series)
// Slot 2, replaces Pistol, Elven Wand, Hexen starting weapons
Class ExplodiumGun : SWWMWeapon
{
int clipcount;
bool chambered;
bool preinit;
bool firstselect;
double casex, casey;
int deadeyecnt;
Property ClipCount : ClipCount;
override Vector3 GetTraceOffset( int index )
{
return (10.,3.,-2.);
}
override bool HandlePickup( Inventory item )
{
// can't hold both weapons at once
if ( swwm_swapweapons && IsSwapWeapon(item) )
return true;
if ( (item.GetClass() == 'ExplodiumGun') && !item.ShouldStay() )
{
if ( !deathmatch && (Amount+item.Amount > MaxAmount) && (item.Stamina != 0) )
{
// sell excess
int sellprice = abs(item.Stamina)/2;
SWWMScoreObj.Spawn(sellprice,level.Vec3Offset(Owner.pos,SWWMUtility.Vec3FromAngles(FRandom[ScoreBits](0,360),FRandom[ScoreBits](-90,90))*8.+(0,0,Owner.Height/2)));
SWWMCredits.Give(Owner.player,sellprice);
if ( Owner.player )
{
if ( Owner.player == players[consoleplayer] ) Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),GetTag(),sellprice);
else Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRAREM_FEM":"$SWWM_SELLEXTRAREM"),Owner.player.GetUserName(),GetTag(),sellprice);
}
item.bPickupGood = true;
}
// give a spare
if ( Amount < MaxAmount )
{
Amount++;
// if the gun has status info, override our sister
let eg = ExplodiumGun(item);
if ( eg && eg.preinit )
{
DualExplodiumGun(SisterWeapon).chambered = eg.chambered;
DualExplodiumGun(SisterWeapon).clipcount = eg.clipcount;
}
else
{
// otherwise use defaults
DualExplodiumGun(SisterWeapon).chambered = false;
DualExplodiumGun(SisterWeapon).clipcount = DualExplodiumGun(SisterWeapon).default.clipcount;
}
// autoswitch if enabled
if ( !Owner.player.GetNeverSwitch() )
Owner.player.PendingWeapon = SisterWeapon;
// add the oneliner
let demo = Demolitionist(Owner);
if ( demo && demo.mystats && !demo.mystats.GotWeapon(SisterWeapon.GetClass()) && (Owner.player == players[consoleplayer]) && !demo.ingivecheat )
{
SWWMHandler.AddOneliner(SWWMWeapon(SisterWeapon).GetLine,2);
demo.facegrin = true;
}
item.bPickupGood = true;
}
return true;
}
return Weapon.HandlePickup(item);
}
action void A_Schutt()
{
let weap = Weapon(invoker);
if ( !weap ) return;
invoker.chambered = invoker.clipcount;
invoker.clipcount = max(invoker.clipcount-1,0);
A_StartSound("explodium/fire",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(5,5,5,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.5);
A_BumpFOV(.96);
A_SWWMFlash();
SWWMHandler.DoFlash(self,Color(64,255,224,64),3);
A_AlertMonsters(swwm_uncapalert?0:5000);
A_PlayerFire();
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMUtility.DoKnockback(self,-x,4000.);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3*y-2*z);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.002);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
Vector3 dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s);
FLineTraceData d;
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
SWWMBulletTrail.DoTrail(self,origin,dir,10000,2);
if ( d.HitType == TRACE_HitActor )
{
if ( d.HitActor.IsHostile(self) )
{
invoker.deadeyecnt++;
SWWMUtility.AchievementProgress("deadeye",invoker.deadeyecnt,player);
}
int dmg = 15;
// might as well apply explosion on top
if ( dmg >= d.HitActor.Health ) dmg += 25;
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,48000);
let p = SWWMPuff.Setup(d.HitLocation,d.HitDir,invoker,self,d.HitActor);
dmg = d.HitActor.DamageMobj(p,self,dmg,'Explodium',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
if ( !d.HitActor || d.HitActor.bNOBLOOD || d.HitActor.bDORMANT || d.HitActor.bINVULNERABLE )
{
let p = Spawn("SWWMBulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
p.pitch = asin(d.HitDir.z);
p.target = self;
}
else
{
d.HitActor.TraceBleed(dmg,self);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
}
let b = Spawn("ExplodiumBulletImpact",d.HitLocation-d.HitDir*4.);
b.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
b.pitch = asin(d.HitDir.z);
b.target = self;
}
else if ( d.HitType != TRACE_HitNone )
{
invoker.deadeyecnt = 0;
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("SWWMBulletImpact",d.HitLocation+hitnormal*0.01);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = self;
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
let b = Spawn("ExplodiumBulletImpact",d.HitLocation+hitnormal*4.);
b.angle = atan2(hitnormal.y,hitnormal.x);
b.pitch = asin(-hitnormal.z);
b.target = self;
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z);
}
else invoker.deadeyecnt = 0;
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .15;
s.alpha *= .5;
s.speed *= .2;
s.vel += vel*.5+x*FRandom[Explodium](.2,.5);
}
}
action void A_ThrowMag()
{
invoker.deadeyecnt = 0;
let weap = Weapon(invoker);
if ( !weap ) return;
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,.005);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
Vector3 dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s);
let p = Spawn("ExplodiumMagProj",origin);
p.special1 = invoker.special1;
p.target = self;
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed;
if ( FindInventory("RagekitPower") )
{
p.vel *= 4.;
p.ClearBounce();
}
if ( p.waterlevel <= 0 ) p.vel.z += 5.;
p.vel += vel*.5;
}
action void A_DropMag()
{
invoker.deadeyecnt = 0;
if ( swwm_nomagdrop ) return;
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-10*z);
let c = Spawn("ExplodiumMag",origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
}
action void A_DropCasing()
{
Vector3 x, y, z;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+8*y-10*z);
let c = Spawn("ExplodiumCasing",origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](2,4)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
}
override bool ReportHUDAmmo()
{
return true;
}
override bool Use( bool pickup )
{
// switch to dual if already selected
if ( Owner.player && (Owner.player.ReadyWeapon == self) && (Amount > 1) )
{
Owner.player.PendingWeapon = SisterWeapon;
return false;
}
return Super.Use(pickup);
}
override Inventory CreateTossable( int amt )
{
// disallow dropping if weapon isn't ready for switching
if ( (Owner.player.ReadyWeapon == self) && (!(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH)) )
return null;
let copy = ExplodiumGun(Inventory.CreateTossable(1));
if ( !copy ) return null;
// destroy sister weapon if we're removing ourselves
if ( copy == self )
{
preinit = true; // need this in case we get picked up again out of order
if ( SisterWeapon )
{
SisterWeapon.SisterWeapon = null;
SisterWeapon.Destroy();
}
// reattach our glow if we became a pickup
if ( (PickupFlash is 'SWWMPickupFlash') && swwm_itemglows )
{
let p = Spawn(PickupFlash,Vec3Offset(0,0,16));
p.target = self;
p.SetStateLabel("Pickup");
}
}
else if ( SisterWeapon )
{
// pass sister's clipcount and chamber status to copy
copy.chambered = DualExplodiumGun(SisterWeapon).chambered;
copy.clipcount = DualExplodiumGun(SisterWeapon).clipcount;
copy.preinit = true; // signal that this copy has preset info
// forcibly switch back from sister weapon
if ( Owner.player.ReadyWeapon == SisterWeapon )
{
Owner.player.ReadyWeapon = self;
Owner.player.SetPSprite(PSP_WEAPON,FindState("Ready"));
Owner.player.SetPSprite(PSP_WEAPON+1,null); // delete left weapon psprite
}
}
return copy;
}
override void MarkPrecacheSounds()
{
Super.MarkPrecacheSounds();
MarkSound("explodium/casing1");
MarkSound("explodium/casing2");
MarkSound("explodium/casing3");
MarkSound("explodium/casing4");
MarkSound("explodium/checkout");
MarkSound("explodium/fire1");
MarkSound("explodium/fire2");
MarkSound("explodium/fire3");
MarkSound("explodium/hit1");
MarkSound("explodium/hit2");
MarkSound("explodium/hit3");
MarkSound("explodium/jamitin");
MarkSound("explodium/mag1");
MarkSound("explodium/mag2");
MarkSound("explodium/mag3");
MarkSound("explodium/maghit1");
MarkSound("explodium/maghit2");
MarkSound("explodium/magin");
MarkSound("explodium/magout");
MarkSound("explodium/magpin");
MarkSound("explodium/select");
MarkSound("explodium/deselect");
MarkSound("explodium/slideback");
MarkSound("explodium/slideforward");
MarkSound("explodium/speen");
MarkSound("explodium/throwmag");
}
override void PlayUpSound( Actor origin )
{
if ( firstselect ) return;
Super.PlayUpSound(origin);
}
override void SetTags( String colname )
{
A_ChangeModel("",1,"","",0,"models","DemoTags"..colname..".png",CMDL_USESURFACESKIN,-1);
}
Default
{
Tag "$T_EXPLODIUM";
Inventory.PickupMessage "$T_EXPLODIUM";
Obituary "$O_EXPLODIUM";
SWWMWeapon.Tooltip "$TT_EXPLODIUM";
SWWMWeapon.GetLine "getexplodiumgun1";
Weapon.UpSound "explodium/select";
Weapon.SlotNumber 2;
Weapon.SelectionOrder 1000;
Inventory.MaxAmount 2;
Weapon.SisterWeapon "DualExplodiumGun";
Stamina 8000;
ExplodiumGun.ClipCount 7;
+WEAPON.EXPLOSIVE;
Radius 12;
Height 24;
}
States
{
Spawn:
XZW1 A -1;
Stop;
Select:
XZW2 B 2
{
invoker.deadeyecnt = 0;
A_FullRaise();
return A_JumpIf(invoker.firstselect,"FirstSelect");
}
XZW2 CDEFGH 2;
Goto Ready;
FirstSelect:
XZW2 B 1 A_JumpIf(level.maptime>8,1);
Wait;
XZW2 B 2
{
invoker.firstselect = false;
invoker.PlayUpSound(self);
}
XZW2 CDEFGH 2;
XZW2 A 2;
Goto Slide;
Ready:
XZW2 A 1
{
if ( invoker.firstselect ) player.SetPSprite(PSP_WEAPON,ResolveState("FirstSelect"));
else if ( (invoker.clipcount > 0) && !invoker.chambered ) player.SetPSprite(PSP_WEAPON,ResolveState("Slide"));
else A_WeaponReady(WRF_ALLOWZOOM|WRF_ALLOWUSER1|WRF_ALLOWRELOAD);
}
Wait;
Fire:
XZW2 A 1
{
A_Schutt();
return A_JumpIf((invoker.clipcount<=0)&&!invoker.chambered,"FireLast");
}
XZW2 I 1;
XZW2 J 1
{
int layer = PSP_WEAPON+1;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"Casing");
}
XZW2 KLMNOP 1;
XZW2 Q 2;
Goto Ready;
FireLast:
XZW2 A 1;
XZWA K 1;
XZWA L 1
{
int layer = PSP_WEAPON+1;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"Casing");
}
XZWA M 1;
XZWA N 1 A_StartSound("explodium/slidelock",CHAN_WEAPON,CHANF_OVERLAP);
XZWA OPQR 1;
XZWA S 2;
Goto ReloadLast;
ReloadLast:
XZW9 X 1 A_PlayerReload();
XZW9 Z 1;
XZWA XYZ 1;
XZWB ABCD 1;
XZWB E 1 { invoker.clipcount = 0; }
XZWB F 1;
XZWB G 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZWB HIJK 1;
XZWB L 1 A_DropMag();
XZWB MNOPQR 1;
XZWB S 1 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP);
XZWB TUVWXYZ 1;
XZWC ABCD 1;
XZWC E 1 { A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); invoker.clipcount = invoker.default.clipcount; }
XZWC FGHI 1;
XZWC J 1
{
A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
invoker.clipcount--;
invoker.chambered = true;
}
XZWC K 1;
XZWC L 3;
XZWC MNOP 1;
Goto Ready;
Casing:
XZWA A 1
{
A_OverlayOffset(OverlayID(),0,0);
invoker.casex = FRandom[Explodium](-1.,5.);
invoker.casey = FRandom[Explodium](-2.,1.);
}
XZWA BCDEFGHIJ 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE);
TNT1 A 1 A_DropCasing();
Stop;
AltFire:
XZW2 A 2
{
A_PlayerReload();
return A_JumpIf(invoker.clipcount<=0,"Reload");
}
XZW5 NO 2;
XZW5 P 1 A_StartSound("explodium/magpin",CHAN_WEAPON,CHANF_OVERLAP);
XZW5 QRSTUVWXYZ 1;
XZW6 AB 1;
XZW6 C 1
{
A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
invoker.special1 = invoker.clipcount;
invoker.clipcount = 0;
}
XZW6 D 1
{
A_StartSound("explodium/throwmag",CHAN_WEAPON,CHANF_OVERLAP);
A_PlayerMelee();
}
XZW6 EFGHIJKLMNOPRS 1;
XZW6 T 1 A_ThrowMag();
XZW6 UV 2;
XZW6 W 2
{
A_PlayerReload();
A_StartSound("explodium/magget",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW6 XY 2;
XZW6 Z 4;
Goto ReloadEnd;
Reload:
XZW2 A 1
{
if ( invoker.clipcount >= invoker.default.clipcount ) return ResolveState("CheckBullet");
A_PlayerReload();
if ( invoker.clipcount <= 0 ) return ResolveState("ReloadEmpty");
return ResolveState(null);
}
XZW2 TUVWXYZ 1;
XZW3 A 1;
XZW3 B 1 { invoker.clipcount = 0; }
XZW3 C 1;
XZW3 D 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 EFGH 1;
XZW3 I 1 A_DropMag();
Goto ReloadEnd;
ReloadEmpty:
XZW2 A 1;
XZW3 JKLMNOPQRS 1;
XZW3 T 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 UVWX 1;
XZW3 Y 1 A_DropMag();
Goto ReloadEnd;
ReloadEnd:
XZW3 Z 1;
XZW4 ABCDE 1;
XZW4 F 1 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 GHIJKLMNOPQ 1;
XZW4 R 1 { A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); invoker.clipcount = invoker.default.clipcount; }
XZW4 STUV 1;
XZW2 A 1 A_JumpIf(!invoker.chambered,"Slide");
Goto Ready;
Slide:
XZW2 A 1;
XZW4 WXY 1;
XZW5 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP);
XZW5 BC 1;
XZW5 D 1 { invoker.chambered = true; invoker.clipcount--; }
XZW5 EFG 1;
XZW5 H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
XZW5 IJKLM 1;
Goto Ready;
Zoom:
XZW2 A 1
{
A_PlayerCheckGun();
return A_Jump(256,"Zoom1","Zoom2","Zoom2");
}
Goto Ready;
CheckBullet:
XZW2 A 1;
XZW7 ABCDE 1;
XZW7 F 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP);
XZW7 GHIJKLMNOP 1;
XZW7 Q 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
XZW7 RS 1;
Goto Ready;
User1:
XZW2 A 1;
XZW7 TU 1;
User1Hold:
XZW7 V 1
{
A_PlayerMelee(true);
A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP);
A_Parry(9);
}
XZW7 WX 1;
XZW7 Y 1 A_Melee();
XZW7 Z 2;
XZW8 ABCDE 2;
XZW8 F 1 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold");
XZW2 B 0 { invoker.PlayUpSound(self); }
Goto Select;
Zoom1:
XZW2 A 2 A_StartSound("explodium/checkout",CHAN_WEAPON,CHANF_OVERLAP);
XZW8 GHIJKLMNOPQRSTUVWXYZ 2;
Goto Ready;
Zoom2:
XZW2 A 1 A_StartSound("explodium/speen",CHAN_WEAPON,CHANF_OVERLAP);
XZW9 ABCDEFGHIJKLMNOPQRSTUVW 1;
Goto Ready;
Deselect:
XZW2 A 2 A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP);
XZWA TUVW 2;
XZW2 B 2;
XZW2 B -1 A_FullLower();
Stop;
Flash:
XZWZ A 2 Bright
{
let psp = player.GetPSprite(PSP_FLASH);
psp.frame = Random[GunFlash](0,9);
let l = Spawn("SWWMWeaponLight",pos);
l.target = self;
}
Stop;
}
}
// Dual Explodium Guns
Class DualExplodiumGun : SWWMWeapon
{
int clipcount;
bool chambered;
double casex, casey, lcasex, lcasey;
Property ClipCount : ClipCount;
override Vector3 GetTraceOffset( int index )
{
if ( index == 1 ) return (10.,3.5,-2.);
return (10.,-3.5,-2.);
}
action void A_LeftFlash( StateLabel flashlabel = null )
{
if ( !player || !player.ReadyWeapon )
return;
Weapon weap = player.ReadyWeapon;
State flashstate = null;
if ( !flashlabel )
{
if ( weap.bAltFire )
flashstate = weap.FindState('LeftAltFlash');
if ( !flashstate )
flashstate = weap.FindState('LeftFlash');
}
else flashstate = weap.FindState(flashlabel);
player.SetPSprite(PSP_FLASH+1,flashstate);
A_OverlayFlags(PSP_FLASH+1,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
A_OverlayRenderStyle(PSP_FLASH+1,STYLE_Add);
}
action void A_Schutt( int side = 1 )
{
let weap = Weapon(invoker);
if ( !weap ) return;
if ( side == 1 )
{
ExplodiumGun(invoker.SisterWeapon).chambered = ExplodiumGun(invoker.SisterWeapon).clipcount;
ExplodiumGun(invoker.SisterWeapon).clipcount = max(ExplodiumGun(invoker.SisterWeapon).clipcount-1,0);
}
else if ( side == -1 )
{
invoker.chambered = invoker.clipcount;
invoker.clipcount = max(invoker.clipcount-1,0);
}
A_StartSound("explodium/fire",CHAN_WEAPON,CHANF_OVERLAP);
A_QuakeEx(5,5,5,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.5);
A_BumpFOV(.96);
if ( side == 1 )
A_SWWMFlash("Flash");
else if ( side == -1 )
A_LeftFlash("LeftFlash");
SWWMHandler.DoFlash(self,Color(64,255,224,64),3);
A_AlertMonsters(swwm_uncapalert?0:5000);
A_PlayerFire();
Vector3 x, y, z, x2, y2, z2;
[x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll);
SWWMUtility.DoKnockback(self,-x,4000.);
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+3.5*side*y-2*z);
double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.002);
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
Vector3 dir = SWWMUtility.ConeSpread(x2,y2,z2,a,s);
FLineTraceData d;
LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION|TRF_NOSKY,origin.z,origin.x,origin.y,d);
SWWMBulletTrail.DoTrail(self,origin,dir,10000,2);
if ( d.HitType == TRACE_HitActor )
{
if ( d.HitActor.IsHostile(self) )
{
ExplodiumGun(invoker.SisterWeapon).deadeyecnt++;
SWWMUtility.AchievementProgress("deadeye",ExplodiumGun(invoker.SisterWeapon).deadeyecnt,player);
}
int dmg = 15;
// might as well apply explosion on top
if ( dmg >= d.HitActor.Health ) dmg += 25;
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,48000);
let p = SWWMPuff.Setup(d.HitLocation,d.HitDir,invoker,self,d.HitActor);
dmg = d.HitActor.DamageMobj(p,self,dmg,'Explodium',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
if ( d.HitActor.bNOBLOOD || d.HitActor.bDORMANT || d.HitActor.bINVULNERABLE )
{
let p = Spawn("SWWMBulletImpact",d.HitLocation);
p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
p.pitch = asin(d.HitDir.z);
p.target = self;
}
else
{
d.HitActor.TraceBleed(dmg,self);
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
}
let b = Spawn("ExplodiumBulletImpact",d.HitLocation-d.HitDir*4.);
b.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
b.pitch = asin(d.HitDir.z);
b.target = self;
}
else if ( d.HitType != TRACE_HitNone )
{
ExplodiumGun(invoker.SisterWeapon).deadeyecnt = 0;
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("SWWMBulletImpact",d.HitLocation+hitnormal*0.01);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = self;
if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
let b = Spawn("ExplodiumBulletImpact",d.HitLocation+hitnormal*4.);
b.angle = atan2(hitnormal.y,hitnormal.x);
b.pitch = asin(-hitnormal.z);
b.target = self;
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z);
}
else ExplodiumGun(invoker.SisterWeapon).deadeyecnt = 0;
for ( int i=0; i<6; i++ )
{
let s = Spawn("SWWMSmoke",origin);
s.scale *= .15;
s.alpha *= .5;
s.speed *= .2;
s.vel += vel*.5+x*FRandom[Explodium](.2,.5);
}
}
action void A_DropMag( int side = 1 )
{
ExplodiumGun(invoker.SisterWeapon).deadeyecnt = 0;
if ( swwm_nomagdrop ) return;
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*side*y-10*z);
let c = Spawn("ExplodiumMag",origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.5,.5)+y*side*FRandom[Junk](-1.2,.3)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
}
action void A_DropCasing( int side = 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+8*side*y-10*z);
let c = Spawn("ExplodiumCasing",origin);
c.angle = angle;
c.pitch = pitch;
c.vel = x*FRandom[Junk](-.5,.5)+y*side*FRandom[Junk](2,4)-(0,0,FRandom[Junk](2,3));
c.vel += vel*.5;
}
override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount )
{
return (SisterWeapon&&(SisterWeapon.Amount > 1));
}
override bool Use( bool pickup )
{
// need to override here because other mods will fuck things up
if ( !SisterWeapon || (SisterWeapon.Amount < 2) )
return false;
return Super.Use(pickup);
}
override bool ReportHUDAmmo()
{
return true;
}
override Inventory CreateTossable( int amt )
{
// disallow dropping if weapon isn't ready for switching
if ( (Owner.player.ReadyWeapon == self) && (!(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH)) )
return null;
// call toss on sister
return SisterWeapon.CreateTossable(amt);
}
override void SetTags( String colname )
{
Super.SetTags(colname);
// also set tag for the second gun
A_ChangeModel("",3,"","",0,"models","DemoTags"..colname..".png",CMDL_USESURFACESKIN,-1);
}
Default
{
Tag "$T_EXPLODIUM2";
Obituary "$O_EXPLODIUM";
SWWMWeapon.Tooltip "$TT_EXPLODIUM2";
SWWMWeapon.GetLine "getexplodiumgun2";
SWWMWeapon.NumCrosshairs 2;
Weapon.UpSound "explodium/select";
Weapon.SisterWeapon "ExplodiumGun";
Weapon.SlotNumber 2;
Weapon.SelectionOrder 950;
Weapon.SlotPriority 2.;
DualExplodiumGun.ClipCount 7;
+WEAPON.EXPLOSIVE;
+SWWMWEAPON.HIDEINMENU;
+SWWMWEAPON.NOSWAPWEAPON;
}
States
{
Select:
XZW2 B 2
{
ExplodiumGun(invoker.SisterWeapon).deadeyecnt = 0;
A_FullRaise();
ExplodiumGun(invoker.SisterWeapon).firstselect = false;
}
XZW2 C 2;
XZW2 D 2 { player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftSelect")); }
XZW2 EFGHAA 2;
Goto Ready;
LeftSelect:
XZWB BCDEFGH 2;
Goto LeftReady;
Ready:
XZW2 A 1
{
ExplodiumGun(invoker.SisterWeapon).firstselect = false;
let sis = player.FindPSprite(PSP_WEAPON+1);
if ( !sis )
{
player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftReady"));
sis = player.FindPSprite(PSP_WEAPON+1);
}
if ( sis.CurState.InStateSequence(ResolveState("LeftReady")) )
{
if ( (ExplodiumGun(invoker.SisterWeapon).clipcount > 0) && !ExplodiumGun(invoker.SisterWeapon).chambered )
player.SetPSprite(PSP_WEAPON,ResolveState("Slide"));
else if ( (invoker.clipcount > 0) && !invoker.chambered )
player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftSlide"));
else if ( (player.cmd.buttons&BT_RELOAD) && (ExplodiumGun(invoker.SisterWeapon).clipcount < ExplodiumGun(invoker.SisterWeapon).default.clipcount) )
player.SetPSprite(PSP_WEAPON,ResolveState("Reload"));
else A_WeaponReady(WRF_ALLOWZOOM|WRF_ALLOWUSER1);
}
else
{
// left weapon isn't ready, but we can still reload right weapon if needed
if ( (player.cmd.buttons&BT_RELOAD) && (ExplodiumGun(invoker.SisterWeapon).clipcount < ExplodiumGun(invoker.SisterWeapon).default.clipcount) )
player.SetPSprite(PSP_WEAPON,ResolveState("Reload"));
else
{
int flg = WRF_NOSWITCH|WRF_NOSECONDARY;
if ( !ExplodiumGun(invoker.SisterWeapon).chambered )
flg |= WRF_NOPRIMARY;
// don't bob while the left weapon is firing
if ( sis.CurState.InStateSequence(ResolveState("LeftFire")) || sis.CurState.InStateSequence(ResolveState("LeftFireLast")) )
flg |= WRF_NOBOB;
A_WeaponReady(flg);
}
}
}
Wait;
LeftReady:
XZWB A 1
{
let sis = player.FindPSprite(PSP_WEAPON);
if ( (invoker.clipcount <= 0) && !invoker.chambered )
player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftReloadLast"));
else if ( (player.cmd.buttons&BT_RELOAD) && (invoker.clipcount < invoker.default.clipcount) )
player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftReload"));
else if ( player.cmd.buttons&BT_ALTATTACK && invoker.chambered )
player.SetPSprite(PSP_WEAPON+1,ResolveState("LeftFire"));
// allow bobbing while the right weapon reloads
if ( !sis.CurState.InStateSequence(ResolveState("Fire")) && !sis.CurState.InStateSequence(ResolveState("FireLast")) )
A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH);
}
Wait;
Fire:
XZW2 A 1
{
A_Schutt();
return A_JumpIf((ExplodiumGun(invoker.SisterWeapon).clipcount<=0)&&!ExplodiumGun(invoker.SisterWeapon).chambered,"FireLast");
}
XZW2 I 1;
XZW2 J 1
{
int layer = PSP_WEAPON+2;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"Casing");
}
XZW2 KLMNOP 1;
XZW2 Q 2;
Goto Ready;
FireLast:
XZW2 A 1;
XZWI K 1;
XZWI L 1
{
int layer = PSP_WEAPON+1;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"Casing");
}
XZWI M 1;
XZWI N 1 A_StartSound("explodium/slidelock",CHAN_WEAPON,CHANF_OVERLAP);
XZWI OPQR 1;
XZWI S 2;
Goto ReloadLast;
ReloadLast:
XZWI V 1
{
A_PlayerReload();
A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP);
}
XZWI WXYZ 2;
XZWJ AB 2;
XZWJ CD 1;
XZWJ E 1 { ExplodiumGun(invoker.SisterWeapon).clipcount = 0; }
XZWJ F 1;
XZWJ G 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZWJ HIJ 1;
XZWJ K 0 A_DropMag();
XZWJ LMN 3;
XZWJ O 2 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP);
XZWJ PQR 2;
XZWJ S 1 { A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); ExplodiumGun(invoker.SisterWeapon).clipcount = ExplodiumGun(invoker.SisterWeapon).default.clipcount; }
XZWJ TUVW 1;
XZWJ X 2 A_StartSound("explodium/select",CHAN_WEAPON,CHANF_OVERLAP);
XZWJ Y 2
{
A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
ExplodiumGun(invoker.SisterWeapon).clipcount--;
ExplodiumGun(invoker.SisterWeapon).chambered = true;
}
XZWJ Z 2;
XZWK ABCD 2;
Goto Ready;
LeftFire:
XZWB A 1
{
A_Schutt(-1);
return A_JumpIf((invoker.clipcount<=0)&&!invoker.chambered,"LeftFireLast");
}
XZWB I 1;
XZWB J 1
{
int layer = PSP_WEAPON+12;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"LeftCasing");
}
XZWB KLMNOP 1;
XZWB Q 2;
Goto LeftReady;
LeftFireLast:
XZWB A 1;
XZWK Q 1;
XZWK R 1
{
int layer = PSP_WEAPON+1;
while ( player.FindPSprite(layer) ) layer++;
A_Overlay(layer,"Casing");
}
XZWK S 1;
XZWK T 1 A_StartSound("explodium/slidelock",CHAN_WEAPON,CHANF_OVERLAP);
XZWK UVWX 1;
XZWK Y 2;
Goto LeftReloadLast;
LeftReloadLast:
XZWK Z 4
{
let sis = player.FindPSprite(PSP_WEAPON);
if ( !sis.CurState.InStateSequence(ResolveState("Reload")) && !sis.CurState.InStateSequence(ResolveState("ReloadEmpty")) && !sis.CurState.InStateSequence(ResolveState("ReloadLast")) )
return ResolveState("LeftReloadLast")+1;
return ResolveState(null);
}
XZWK Z 1
{
A_PlayerReload();
A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP);
}
XZWL ABCDEF 2;
XZWL GH 1;
XZWL I 1 { invoker.clipcount = 0; }
XZWL J 1;
XZWL K 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZWL LMN 1;
XZWL O 0 A_DropMag();
XZWL PQR 3;
XZWL S 2 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP);
XZWL TUV 2;
XZWL W 1 { A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); invoker.clipcount = invoker.default.clipcount; }
XZWL XYZ 1;
XZWM A 1;
XZWM B 2 A_StartSound("explodium/select",CHAN_WEAPON,CHANF_OVERLAP);
XZWM C 2
{
A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
invoker.clipcount--;
invoker.chambered = true;
}
XZWM DEFGH 2;
Goto LeftReady;
Casing:
XZWA A 1
{
A_OverlayOffset(OverlayID(),0,0);
invoker.casex = FRandom[Explodium](-1.,5.);
invoker.casey = FRandom[Explodium](-2.,1.);
}
XZWA BCDEFGHIJ 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE);
TNT1 A 1 A_DropCasing();
Stop;
LeftCasing:
XZWI A 1
{
A_OverlayOffset(OverlayID(),0,0);
invoker.lcasex = FRandom[Explodium](-5.,1.);
invoker.lcasey = FRandom[Explodium](-1.,2.);
}
XZWI BCDEFGHIJ 1 A_OverlayOffset(OverlayID(),invoker.lcasex,invoker.lcasey,WOF_ADD|WOF_INTERPOLATE);
TNT1 A 1 A_DropCasing(-1);
Stop;
Lower:
XZW2 A 2;
XZW7 TUV 2;
XZW2 B -1;
Stop;
LeftLower:
XZWB A 2;
XZWG TUV 2;
XZWB B -1;
Stop;
Raise:
XZW2 B 2;
XZW7 VUT 2;
XZW2 A 1;
Goto Ready;
LeftRaise:
XZWB B 2;
XZWG VUT 2;
XZWB A 1;
Goto LeftReady;
Reload:
XZW2 A 1
{
A_PlayerReload();
A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP);
if ( ExplodiumGun(invoker.SisterWeapon).clipcount <= 0 ) return ResolveState("ReloadEmpty");
return ResolveState(null);
}
XZW2 TUVWXY 2;
XZW2 Z 1;
XZW3 A 1;
XZW3 B 1 { ExplodiumGun(invoker.SisterWeapon).clipcount = 0; }
XZW3 C 1;
XZW3 D 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 EFG 1;
XZW3 H 0 A_DropMag();
Goto ReloadEnd;
ReloadEmpty:
XZW2 A 1;
XZW3 IJKLMN 2;
XZW3 OPQR 1;
XZW3 S 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZW3 TUV 1;
XZW3 W 0 A_DropMag();
Goto ReloadEnd;
ReloadEnd:
XZW3 XYZ 3;
XZW4 A 2 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 BCD 2;
XZW4 E 1 { A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); ExplodiumGun(invoker.SisterWeapon).clipcount = ExplodiumGun(invoker.SisterWeapon).default.clipcount; }
XZW4 FGHI 1;
XZW4 J 2 A_StartSound("explodium/select",CHAN_WEAPON,CHANF_OVERLAP);
XZW4 KLMNOP 2;
Goto Ready;
Slide:
XZW2 A 9 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftLower")); }
XZW2 A 1;
XZW4 WXY 1;
XZW5 A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP);
XZW5 BC 1;
XZW5 D 1
{
ExplodiumGun(invoker.SisterWeapon).chambered = true;
ExplodiumGun(invoker.SisterWeapon).clipcount--;
}
XZW5 EFG 1;
XZW5 H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
XZW5 IJKLM 1;
XZW2 A 0 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftRaise")); }
Goto Ready;
LeftReload:
XZWB A 4
{
let sis = player.FindPSprite(PSP_WEAPON);
if ( !sis.CurState.InStateSequence(ResolveState("Reload")) && !sis.CurState.InStateSequence(ResolveState("ReloadEmpty")) && !sis.CurState.InStateSequence(ResolveState("ReloadLast")) )
return ResolveState("LeftReload")+1;
return ResolveState(null);
}
XZWB A 1
{
A_PlayerReload();
A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP);
if ( invoker.clipcount <= 0 ) return ResolveState("LeftReloadEmpty");
return ResolveState(null);
}
XZWB TUVWXY 2;
XZWB Z 1;
XZWC A 1;
XZWC B 1 { invoker.clipcount = 0; }
XZWC C 1;
XZWC D 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZWC EFG 1;
XZWC H 0 A_DropMag();
Goto LeftReloadEnd;
LeftReloadEmpty:
XZWB A 1;
XZWC IJKLMN 2;
XZWC OPQR 1;
XZWC S 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP);
XZWC TUV 1;
XZWC W 0 A_DropMag();
Goto LeftReloadEnd;
LeftReloadEnd:
XZWC XYZ 3;
XZWD A 2 A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP);
XZWD BCD 2;
XZWD E 1 { A_StartSound("explodium/jamitin",CHAN_WEAPON,CHANF_OVERLAP); invoker.clipcount = invoker.default.clipcount; }
XZWD FGHI 1;
XZWD J 2 A_StartSound("explodium/select",CHAN_WEAPON,CHANF_OVERLAP);
XZWD KLMNOP 2;
Goto LeftReady;
LeftSlide:
XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState("Lower")); }
XZWB A 1;
XZWD WXY 1;
XZWE A 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP);
XZWE BC 1;
XZWE D 1
{
invoker.chambered = true;
invoker.clipcount--;
}
XZWE EFG 1;
XZWE H 1 A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP);
XZWE IJKLM 1;
XZWB A 0 { player.SetPSPrite(PSP_WEAPON,ResolveState("Raise")); }
Goto LeftReady;
Zoom:
XZW2 A 1
{
A_PlayerCheckGun();
A_StartSound("explodium/speen",CHAN_WEAPON,CHANF_OVERLAP);
}
XZW9 ABCDEFG 1;
XZW9 H 1 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftZoom")); }
XZW9 IJKLMNOPQRSTUVW 1;
Goto Ready;
LeftZoom:
XZWB A 1;
XZWH ABCDEFGHIJKLMNOPQRSTUVW 1;
Goto LeftReady;
User1:
XZW2 A 1 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftUser1")); }
XZW7 TU 1;
User1Hold:
XZW7 V 1
{
A_PlayerMelee(true);
A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP);
A_Parry(9);
}
XZW7 WX 1;
XZW7 Y 1 A_Melee();
XZW7 Z 2;
XZW8 ABCDE 2;
XZW8 F 1 A_JumpIf(player.cmd.buttons&BT_USER1,"User1Hold");
XZW2 B 0 { invoker.PlayUpSound(self); player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftSelect")); }
Goto Select;
LeftUser1:
XZWB A 1;
XZWG TUV 1;
XZWB B -1;
Stop;
Deselect:
XZW2 A 2 A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP);
XZWA T 2;
XZWA U 2 { player.SetPSPrite(PSP_WEAPON+1,ResolveState("LeftDeselect")); }
XZWA VW 2;
XZW2 B 6;
XZW2 B -1 A_FullLower();
Stop;
LeftDeselect:
XZWB A 2;
XZWA PQRS 2;
XZWB B 2;
Stop;
Flash:
XZWZ A 2 Bright
{
let psp = player.GetPSprite(PSP_FLASH);
psp.frame = Random[GunFlash](0,9);
let l = Spawn("SWWMWeaponLight",pos);
l.target = self;
}
Stop;
LeftFlash:
XZWZ K 2 Bright
{
let psp = player.GetPSprite(PSP_FLASH+1);
psp.frame = Random[GunFlash](10,19);
let l = Spawn("SWWMWeaponLight",pos);
l.target = self;
}
Stop;
}
}