// 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 if ( Owner.player ) { int sellprice = abs(item.Stamina)/2; SWWMCredits.Give(Owner.player,sellprice); if ( Owner.player == players[consoleplayer] ) { SWWMScoreObj.SpawnAtActorBunch(sellprice,Owner); 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 && !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(4.5,4.5,4.5,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.5); A_BumpFOV(.96); A_BumpView(-.2); A_SWWMFlash(); SWWMHandler.DoFlash(self,Color(64,255,224,64),3); A_AlertMonsters(swwm_uncapalert?0:5000); A_PlayerFire(); Vector3 x = SWWMUtility.GetPlayerViewDir(self); SWWMUtility.DoKnockback(self,-x,4000.); Vector3 origin = SWWMUtility.GetFireOffset(self,10,3,-2); 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); FLineTraceData d; LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,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 if ( dmg > 0 ) { 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 = SWWMUtility.GetLineTraceHitNormal(d); 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.HitType == TRACE_HitWall ) 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 origin = SWWMUtility.GetFireOffset(self,10,-2,-3); double a = FRandom[Spread](0,360), s = FRandom[Spread](0,.005); let [x2, y2, z2] = SWWMUtility.GetPlayerAxesAutoAimed(self); 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; let [x, y, z] = SWWMUtility.GetPlayerAxes(self); Vector3 origin = SWWMUtility.GetFireOffset(self,10,-2,-10); 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() { let [x, y, z] = SWWMUtility.GetPlayerAxes(self); Vector3 origin = SWWMUtility.GetFireOffset(self,10,8,-10); 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 or if first selecting (unless disabled) if ( Owner.player && (Amount > 1) && ((Owner.player.ReadyWeapon == self) || ((Owner.player.ReadyWeapon != SisterWeapon) && !swwm_singlefirst)) ) { // if the player's current weapon is a gesture, set sister as the former weapon, to avoid breaking gesture sequences if ( Owner.player.ReadyWeapon is 'SWWMGesture' ) { let g = SWWMGesture(Owner.player.ReadyWeapon); g.formerweapon = SisterWeapon; } else if ( Owner.player.PendingWeapon is 'SWWMGesture' ) { let g = SWWMGesture(Owner.player.PendingWeapon); g.formerweapon = SisterWeapon; } else if ( Owner.player.ReadyWeapon is 'SWWMItemGesture' ) { let ig = SWWMItemGesture(Owner.player.ReadyWeapon); ig.gest.formerweapon = SisterWeapon; } else if ( Owner.player.PendingWeapon is 'SWWMItemGesture' ) { let ig = SWWMItemGesture(Owner.player.PendingWeapon); ig.gest.formerweapon = SisterWeapon; } else Owner.player.PendingWeapon = self; 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' ) { let p = Spawn(PickupFlash,pos); 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.Icon "graphics/HUD/Icons/W_ExplodiumGun.png"; 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; } States { Spawn: XZW1 A -1; Stop; Select: XZW2 B 2 { invoker.deadeyecnt = 0; A_FullRaise(); return A_JumpIf(invoker.firstselect,'FirstSelect'); } XZW2 CDE 2 A_BumpView(-.1,tics:2); XZW2 FGH 2 A_BumpView(.05,tics:2); Goto Ready; FirstSelect: XZW2 B 1 A_JumpIf(level.maptime>8,1); Wait; XZW2 B 2 { invoker.firstselect = false; invoker.PlayUpSound(self); } XZW2 CDE 2 A_BumpView(-.1,tics:2); XZW2 FGH 2 A_BumpView(.05,tics: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+2; 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+2; 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 H 1 A_BumpView(.3); XZWB IJK 1; XZWB L 1 A_DropMag(); XZWB MNOPQR 1; XZWB S 1 { A_BumpView(-.1); A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP); } XZWB TUVWXYZ 1; XZWC ABCD 1; XZWC E 1 { A_BumpView(-.4); 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 EFGHI 1 A_BumpView(.25); XZW6 JKLMNO 1; XZW6 PRS 1 A_BumpView(-1.5); 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 E 1 A_BumpView(.3); XZW3 FGH 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 U 1 A_BumpView(.3); XZW3 VWX 1; XZW3 Y 1 A_DropMag(); Goto ReloadEnd; ReloadEnd: XZW3 Z 1; XZW4 ABCDE 1; XZW4 F 1 { A_BumpView(-.1); A_StartSound("explodium/magin",CHAN_WEAPON,CHANF_OVERLAP); } XZW4 GHIJKLMNOPQ 1; XZW4 R 1 { A_BumpView(-.4); 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 A_BumpView(-.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 A_BumpView(.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 A 1 A_Overlay(PSP_WEAPON+1,'CheckBulletCasing'); XZW7 BCDE 1 A_BumpView(-.2); XZW7 F 1 A_StartSound("explodium/slideback",CHAN_WEAPON,CHANF_OVERLAP); XZW7 GHIJKLMNOP 1; XZW7 Q 1 { A_BumpView(.6); A_StartSound("explodium/slideforward",CHAN_WEAPON,CHANF_OVERLAP); } XZW7 RS 1; Goto Ready; CheckBulletCasing: XZWC QRSTUVWXYZ 1; XZWD ABCDEFGHI 1; Stop; User1: XZW2 A 1; XZW7 TU 1 A_BumpView(.1); User1Hold: XZW7 V 1 { A_BumpAngle(1.); A_PlayerMelee(true); A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); A_Parry(9); } XZW7 WX 1 A_BumpAngle(-.5); 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 GHIJ 2 A_BumpView(-.2); XZW8 KLMNO 2 A_BumpView(-.3); XZW8 PQR 2 A_BumpView(.5); XZW8 STUV 2 A_BumpView(.2); XZW8 WXYZ 2; Goto Ready; Zoom2: XZW2 A 1 A_StartSound("explodium/speen",CHAN_WEAPON,CHANF_OVERLAP); XZW9 ABCD 1 A_BumpView(.2); XZW9 EFGHIJ 1 A_BumpView(-.2); XZW9 KLMNOP 1 A_BumpView(-.1); XZW9 QRST 1 A_BumpView(.3); XZW9 UVW 1; Goto Ready; Deselect: XZW2 A 2 A_StartSound("explodium/deselect",CHAN_WEAPON,CHANF_OVERLAP); XZWA TUVW 2 A_BumpView(.1,tics: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(4.5,4.5,4.5,3,0,10,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:1.5); A_BumpFOV(.96); A_BumpView(-.2); 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 = SWWMUtility.GetPlayerViewDir(self); SWWMUtility.DoKnockback(self,-x,4000.); Vector3 origin = SWWMUtility.GetFireOffset(self,10,3.5*side,-2); 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); FLineTraceData d; LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,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 if ( dmg > 0 ) { 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 = SWWMUtility.GetLineTraceHitNormal(d); 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.HitType == TRACE_HitWall ) 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; let [x, y, z] = SWWMUtility.GetPlayerAxes(self); Vector3 origin = SWWMUtility.GetFireOffset(self,10,-2*side,-10); 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 ) { let [x, y, z] = SWWMUtility.GetPlayerAxes(self); Vector3 origin = SWWMUtility.GetFireOffset(self,10,8*side,-10); 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 could screw it 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 A_BumpView(-.1,tics:2); XZW2 D 2 { A_BumpView(-.1,tics:2); player.SetPSprite(PSP_WEAPON+1,ResolveState('LeftSelect')); } XZW2 E 2 A_BumpView(-.1,tics:2); XZW2 FGH 2 A_BumpView(.05,tics:2); XZW2 AA 2; Goto Ready; LeftSelect: XZWB B 2; XZWB CDE 2 A_BumpView(-.1,tics:2); XZWB FGH 2 A_BumpView(.05,tics: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_CanBob(); } 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 WXY 2 A_BumpView(.2,tics:2); XZWI Z 2 A_BumpView(.1,tics:2); XZWJ AB 2 A_BumpView(.1,tics: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 H 1 A_BumpView(.2); XZWJ IJ 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_BumpView(-.2); 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,'LeftCasing'); } 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 ABC 2 A_BumpView(.2,tics:2); XZWL DEF 2 A_BumpView(.1,tics: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 L 1 A_BumpView(.2); XZWL MN 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_BumpView(-.2); 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 A_BumpView(.1,tics:2); XZW2 B -1; Stop; LeftLower: XZWB A 2; XZWG TUV 2 A_BumpView(.1,tics:2); XZWB B -1; Stop; Raise: XZW2 B 2; XZW7 VUT 2 A_BumpView(-.1,tics:2); XZW2 A 1; Goto Ready; LeftRaise: XZWB B 2; XZWG VUT 2 A_BumpView(-.1,tics: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 TUV 2 A_BumpView(.2,tics:2); XZW2 WXY 2 A_BumpView(.1,tics: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 E 1 A_BumpView(.2); XZW3 FG 1; XZW3 H 0 A_DropMag(); Goto ReloadEnd; ReloadEmpty: XZW2 A 1; XZW3 IJK 2 A_BumpView(.2,tics:2); XZW3 LMN 2 A_BumpView(.1,tics:2); XZW3 OPQR 1; XZW3 S 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); XZW3 T 1 A_BumpView(.2); XZW3 UV 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_BumpView(-.2); 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 KLM 2 A_BumpView(-.2,tics:2); XZW4 NOP 2 A_BumpView(.1,tics:2); Goto Ready; Slide: XZW2 A 9 { player.SetPSPrite(PSP_WEAPON+1,ResolveState('LeftLower')); } XZW2 A 1; XZW4 WXY 1 A_BumpView(-.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 A_BumpView(.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 TUV 2 A_BumpView(.2,tics:2); XZWB WXY 2 A_BumpView(.1,tics: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 E 1 A_BumpView(.2); XZWC FG 1; XZWC H 0 A_DropMag(); Goto LeftReloadEnd; LeftReloadEmpty: XZWB A 1; XZWC IJK 2 A_BumpView(.2,tics:2); XZWC LMN 2 A_BumpView(.1,tics:2); XZWC OPQR 1; XZWC S 1 A_StartSound("explodium/magout",CHAN_WEAPON,CHANF_OVERLAP); XZWC T 1 A_BumpView(.2); XZWC UV 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_BumpView(-.2); 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 KLM 2 A_BumpView(-.2,tics:2); XZWD NOP 2 A_BumpView(.1,tics:2); Goto LeftReady; LeftSlide: XZWB A 9 { player.SetPSPrite(PSP_WEAPON,ResolveState('Lower')); } XZWB A 1; XZWD WXY 1 A_BumpView(-.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 A_BumpView(.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 ABCD 1 A_BumpView(.2); XZW9 EFG 1 A_BumpView(-.2); XZW9 H 1 { A_BumpView(-.2); player.SetPSPrite(PSP_WEAPON+1,ResolveState('LeftZoom')); } XZW9 IJ 1 A_BumpView(-.2); XZW9 KLMNOP 1 A_BumpView(-.1); XZW9 QRST 1 A_BumpView(.3); XZW9 UVW 1; Goto Ready; LeftZoom: XZWB A 1; XZWH ABCD 1 A_BumpView(.2); XZWH EFGHIJ 1 A_BumpView(-.2); XZWH KLMNOP 1 A_BumpView(-.1); XZWH QRST 1 A_BumpView(.3); XZWH UVW 1; Goto LeftReady; User1: XZW2 A 1 { player.SetPSPrite(PSP_WEAPON+1,ResolveState('LeftUser1')); } XZW7 TU 1 A_BumpView(.1); User1Hold: XZW7 V 1 { A_BumpAngle(1.); A_PlayerMelee(true); A_StartSound("demolitionist/swing",CHAN_WEAPON,CHANF_OVERLAP); A_Parry(9); } XZW7 WX 1 A_BumpAngle(-.5); 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 A_BumpView(.1,tics:2); XZWA U 2 { A_BumpView(.1,tics:2); player.SetPSPrite(PSP_WEAPON+1,ResolveState('LeftDeselect')); } XZWA VW 2 A_BumpView(.1,tics:2); XZW2 B -1 A_FullLower(); Stop; LeftDeselect: XZWB A 2; XZWA PQRS 2 A_BumpView(.1,tics:2); XZWB B 0; 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; } }