// Blackmann-Forx Silver Bullet JET (successor to Silver Bullet from Zanaveth Ultra Suite) // Slot 8, replaces Plasma Rifle, Hellstaff, Quietus (hilt) Class WallPenetrate { Vector3 hitpos, hitdir, hitnormal, bustdir; int hitside, hittier, hittype, penetration; Line hitline; Sector hitsector; F3DFloor hitffloor; bool pastwall; } Class AuxiliarySilverBulletTracer : LineTracer { override ETraceStatus TraceCallback() { if ( Results.HitType == TRACE_HitActor ) 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 SilverBulletTracer : SpreadSlugTracer { bool pastwall, fullstop; Array WallPenetrateList; Vector3 exitpoint; transient Array ffloors; // needs to be done like this because HAHA SCOPE override ETraceStatus TraceCallback() { // liquid splashes if ( Results.CrossedWater ) { let hl = new("WaterHit"); hl.sect = Results.CrossedWater; hl.hitpos = Results.CrossedWaterPos; WaterHitList.Push(hl); } else if ( Results.Crossed3DWater ) { let hl = new("WaterHit"); hl.sect = Results.Crossed3DWater; hl.hitpos = Results.Crossed3DWaterPos; WaterHitList.Push(hl); } if ( Results.HitType == TRACE_HitActor ) { if ( Results.HitActor == ignoreme ) return TRACE_Skip; if ( Results.HitActor.bSHOOTABLE ) { let ent = new("HitListEntry"); ent.hitactor = Results.HitActor; ent.hitlocation = Results.HitPos; ent.x = Results.HitVector; ent.pastwall = pastwall; ent.hitdamage = int(penetration); if ( (Results.HitActor.Health >= int(penetration)) || Results.HitActor.bNODAMAGE ) penetration = 0; else { int killdamage = min(Results.HitActor.health,int(penetration)); penetration = max(0,penetration-killdamage*.4); } hitlist.Push(ent); if ( penetration <= 0 ) { fullstop = true; return TRACE_Stop; } return TRACE_Skip; } return TRACE_Skip; } else if ( Results.HitType == TRACE_HasHitSky ) { fullstop = true; return TRACE_Stop; } else if ( Results.HitType != TRACE_HitNone ) { if ( Results.HitType == TRACE_HitWall ) { ShootThroughList.Push(Results.HitLine); if ( (Results.Tier == TIER_Middle) && Results.HitLine.sidedef[1] && !(Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) ) return TRACE_Skip; } for ( int i=1; i maxz) ) continue; stop3d = true; break; } if ( stop3d ) continue; let wp = new("WallPenetrate"); wp.hittype = Results.HitType; wp.hitline = Results.HitLine; wp.hitside = Results.Side; wp.hittier = Results.Tier; wp.hitsector = Results.HitSector; wp.hitffloor = Results.ffloor; wp.hitpos = Results.HitPos; wp.hitdir = Results.HitVector; wp.bustdir = Results.HitVector; wp.penetration = int(penetration); if ( Results.HitType == TRACE_HitWall ) { wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit(); if ( !Results.Side ) wp.hitnormal *= -1; ShootThroughList.Push(Results.HitLine); } else if ( Results.HitType == TRACE_HitCeiling ) wp.hitnormal = Results.HitSector.ceilingplane.Normal; else if ( Results.HitType == TRACE_HitFloor ) wp.hitnormal = Results.HitSector.floorplane.Normal; wp.pastwall = pastwall; WallPenetrateList.Push(wp); pastwall = true; penetration = max(0,penetration-i*4); // trace backwards to find exit surface let at = new("AuxiliarySilverBulletTracer"); at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,0); let wp2 = new("WallPenetrate"); wp2.hittype = at.Results.HitType; wp2.hitline = at.Results.HitLine; wp2.hitside = at.Results.Side; wp2.hittier = at.Results.Tier; wp2.hitsector = at.Results.HitSector; wp2.hitffloor = at.Results.ffloor; wp2.hitside = at.Results.Side; wp2.hitpos = at.Results.HitPos; wp2.hitdir = at.Results.HitVector; wp2.bustdir = -at.Results.HitVector; wp2.penetration = int(penetration); if ( at.Results.HitType == TRACE_HitWall ) { wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit(); if ( !at.Results.Side ) wp2.hitnormal *= -1; if ( at.Results.HitLine.sidedef[1] ) ShootThroughList.Push(at.Results.HitLine); } else if ( at.Results.HitType == TRACE_HitCeiling ) wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal; else if ( at.Results.HitType == TRACE_HitFloor ) wp2.hitnormal = at.Results.HitSector.floorplane.Normal; else wp2.hitnormal = wp2.hitdir; wp2.pastwall = pastwall; WallPenetrateList.Push(wp2); fullstop = false; exitpoint = ofs; return TRACE_Stop; } } fullstop = true; return TRACE_Stop; } return TRACE_Skip; } } Class FatChodeTracer : LineTracer { Actor ignoreme; Array ShootThroughList; Array WaterHitList; override ETraceStatus TraceCallback() { // liquid splashes if ( Results.CrossedWater ) { let hl = new("WaterHit"); hl.sect = Results.CrossedWater; hl.hitpos = Results.CrossedWaterPos; WaterHitList.Push(hl); } else if ( Results.Crossed3DWater ) { let hl = new("WaterHit"); hl.sect = Results.Crossed3DWater; hl.hitpos = Results.Crossed3DWaterPos; WaterHitList.Push(hl); } if ( Results.HitType == TRACE_HitActor ) { if ( Results.HitActor == ignoreme ) return TRACE_Skip; if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop; return TRACE_Skip; } else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) ) { if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&Line.ML_BlockHitscan) ) return TRACE_Stop; ShootThroughList.Push(Results.HitLine); return TRACE_Skip; } return TRACE_Stop; } } Class SilverBullet : SWWMWeapon { bool chambered, fired; bool fcbchambered, fcbloaded, fcbselected; ui int lastammo; int clipcount; double casex, casey; int nkills; transient ui SmoothDynamicValueInterpolator ZoomInter; bool zoomed; double zoomlevel; ui TextureID reticle, scope; int rezoom; bool proneme; int wastecycle; // for easter egg State dezoomstate; Property ClipCount : clipcount; override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) { if ( fcbchambered ) return StringTable.Localize("$O_SILVERBULLET2"); return Super.GetObituary(victim,inflictor,mod,playerattack); } override void HudTick() { Super.HudTick(); if ( !ZoomInter ) ZoomInter = SmoothDynamicValueInterpolator.Create(zoomlevel*10,.5); ZoomInter.Update(zoomlevel*10); if ( lastammo && (lastammo != fcbselected+1) && (Owner.player == players[consoleplayer]) ) { let bar = SWWMStatusBar(statusbar); if ( bar ) { bar.ntagstr = StringTable.Localize(fcbselected?"$SWWM_FCBSEL":"$SWWM_XSBSEL"); bar.ntagtic = level.totaltime; bar.ntagcol = nametagcolor; } } lastammo = fcbselected+1; } override bool ReportHUDAmmo() { if ( (chambered && !fired) || (clipcount > 0) ) return true; if ( (Ammo1.Amount <= 0) && (Ammo2.Amount <= 0) && (Owner.CountInv("SilverBullets") <= 0) && (Owner.CountInv("SilverBullets2") <= 0) ) return false; return true; } override bool IsCurrentAmmo( Class kind ) { if ( fcbselected ) return (kind is AmmoType2); return (kind is AmmoType1); } override bool CheckAmmo( int firemode, bool autoswitch, bool requireammo, int ammocount ) { if ( sv_infiniteammo || Owner.FindInventory('PowerInfiniteAmmo',true) ) return true; if ( (fireMode == PrimaryFire) || (fireMode == EitherFire) ) { if ( (chambered && !fired) || (clipcount > 0) || (Ammo1.Amount > 0) || (Ammo2.Amount > 0) || (Owner.CountInv("SilverBullets") > 0) || (Owner.CountInv("SilverBullets2") > 0) ) return true; if ( autoswitch ) PlayerPawn(Owner).PickNewWeapon(null); return false; } return Super.CheckAmmo(firemode,autoswitch,requireammo,ammocount); } override void DetachFromOwner() { // force disable zoom zoomed = false; if ( Owner.player == players[consoleplayer] ) PPShader.SetEnabled("SilverScope",false); Super.DetachFromOwner(); } override double GetSpeedFactor() { if ( proneme ) return 0.; return 1.; } override void DoEffect() { Super.DoEffect(); if ( zoomed ) crosshair = 99; if ( !Owner || !Owner.player ) { proneme = false; lookscale = 1.; return; } if ( (Owner.player.ReadyWeapon == self) && Owner.player.onground ) { if ( Owner.player.cmd.buttons&BT_CROUCH ) { if ( !proneme ) Owner.A_StartSound("silverbullet/crouch",CHAN_WEAPONEXTRA,CHANF_OVERLAP); proneme = true; } else { if ( proneme ) Owner.A_StartSound("silverbullet/uncrouch",CHAN_WEAPONEXTRA,CHANF_OVERLAP); proneme = false; } } else proneme = false; lookscale = proneme?.4:1.; } override void RenderUnderlay( RenderEvent e ) { if ( zoomed && (Owner.player == players[consoleplayer]) ) { Vector2 ss = (Screen.GetWidth(),Screen.GetHeight()); ss *= (512./ss.y); if ( swwm_shaders ) PPShader.SetEnabled("SilverScope",true); else { PPShader.SetEnabled("SilverScope",false); if ( !scope ) scope = TexMan.CheckForTexture("graphics/SBScope.png",TexMan.Type_Any); Screen.DrawTexture(scope,false,ss.x*.5,ss.y*.5,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Multiply); } if ( !reticle ) reticle = TexMan.CheckForTexture("graphics/SBReticle.png",TexMan.Type_Any); Screen.DrawTexture(reticle,false,ss.x*.5,ss.y*.5,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_LegacyRenderStyle,STYLE_Stencil,DTA_FillColor,Color(0,0,0)); return; } else PPShader.SetEnabled("SilverScope",false); Super.RenderUnderlay(e); } action void ProcessTraceHit( SilverBulletTracer t, Vector3 origin, Vector3 dir ) { for ( int i=0; i 0 ) p.bAMBUSH = true; if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos); if ( swwm_omnibust ) BusterWall.Bust(t.Results,int(t.penetration),self,t.Results.HitVector,t.Results.HitPos.z); } if ( t.WallPenetrateList.Size() > 0 ) { Vector3 start = origin; Vector3 end = t.WallPenetrateList[0].hitpos; Vector3 tdir = level.Vec3Diff(start,end); double dist = tdir.length(); tdir /= dist; SilverAirRip s; for ( int i=0; i= t.WallPenetrateList.Size()-1 ) end = t.Results.HitPos; else end = t.WallPenetrateList[i+1].hitpos; tdir = level.Vec3Diff(start,end); dist = tdir.length(); tdir /= dist; for ( int j=0; j= t.Results.HitActor.Health ) dmg += 600; let p = SWWMPuff.Setup(t.Results.HitPos,t.Results.HitVector,invoker,self,t.Results.HitActor); dmg = t.Results.HitActor.DamageMobj(p,self,dmg,'Explodium',DMG_FOILINVUL|DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF); SWWMUtility.DoKnockback(t.Results.HitActor,t.Results.HitVector+(0,0,.025),dmg*20.*FRandom[SilverBullet](.8,1.2)); if ( t.Results.HitActor && !t.Results.HitActor.bNOBLOOD && !t.Results.HitActor.bDORMANT ) { t.Results.HitActor.TraceBleed(dmg,self); t.Results.HitActor.SpawnBlood(t.Results.HitPos,atan2(t.Results.HitVector.y,t.Results.HitVector.x)+180,dmg); t.Results.HitActor.A_StartSound("silverbullet/flesh",CHAN_DAMAGE,CHANF_OVERLAP,1.,2.); } } else if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos); let p = Spawn("FatChodeImpact",t.Results.HitPos+hitnormal*4); p.angle = atan2(hitnormal.y,hitnormal.x); p.pitch = asin(-hitnormal.z); p.target = self; FatChodeImpact(p).realangle = atan2(t.Results.HitVector.y,t.Results.HitVector.x); FatChodeImpact(p).realpitch = asin(-t.Results.HitVector.z); BusterWall.Bust(t.Results,invoker.proneme?1400:1200,self,t.Results.HitVector,t.Results.HitPos.z); } SilverAirRip s; for ( int i=0; i mac = fcb?"SilverBullets2":"SilverBullets"; MagAmmo ma = MagAmmo(FindInventory(mac)); if ( !ma ) { ma = MagAmmo(Spawn(mac)); ma.Amount = 0; ma.AttachToOwner(self); } if ( ma.Amount < ma.MaxAmount ) { ma.Amount++; ma.MagFill(); } else { let c = Spawn(mac,origin); c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](4,8)-(0,0,FRandom[Junk](1,3)); c.vel += vel*.5; } if ( !Demolitionist(self) || !(Demolitionist(self).mystats) || swwm_nomapmsg ) return; invoker.wastecycle++; let s = Demolitionist(self).mystats; if ( s.silveregg < 13 ) { if ( invoker.wastecycle < 5 ) return; s.silveregg++; } else return; invoker.wastecycle = 0; if ( (s.silveregg > 2) && (s.silveregg%2) && player == players[consoleplayer] ) Console.MidPrint(null,"swwmsilverbulleteasteregg"..((s.silveregg-1)/2)); } action void A_DropMag() { Class mac = invoker.fcbloaded?"SilverBullets2":"SilverBullets"; MagAmmo ma = MagAmmo(FindInventory(mac)); if ( !ma ) { ma = MagAmmo(Spawn(mac)); ma.Amount = 0; ma.AttachToOwner(self); } int maxgiveamt = min(ma.MaxAmount-ma.Amount,invoker.clipcount); int dropamt = invoker.clipcount-maxgiveamt; if ( dropamt > 0 ) invoker.BufferMagAmmo(mac,dropamt); ma.Amount = min(ma.MaxAmount,ma.Amount+invoker.clipcount); ma.MagFill(); invoker.ClipCount = 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),6*x-15*z); let c = Spawn(invoker.fcbloaded?"SilverBulletMag2":"SilverBulletMag",origin); c.angle = angle; c.pitch = pitch; c.vel = x*FRandom[Junk](-.5,.5)+y*FRandom[Junk](-.5,.5)-(0,0,FRandom[Junk](2,3)); c.vel += vel*.5; } action void A_JetCompensate() { invoker.specialf1 -= .06; Vector3 x, y, z; [x, y, z] = swwm_CoordUtil.GetAxes(pitch,angle,roll); vel += x*min(1.,invoker.specialf1)*(600./Mass); A_OverlayAlpha(PSP_WEAPON+1,clamp(invoker.specialf1*3.,0.,1.)); if ( Random[SilverBullet](0,int(invoker.specialf1*2)) ) self.DamageMobj(invoker,self,1,'jet'); Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),-x*10-z*10); for ( int i=0; i<4; i++ ) { let s = Spawn("SWWMSmoke",origin); s.scale *= 1.3; s.alpha *= .5*invoker.specialf1; s.special1 = Random[Silverbullet](0,3); s.SetShade(Color(1,1,1)*Random[Silverbullet](224,255)); s.vel += vel*.5+x*FRandom[Silverbullet](-8.,-5.)+y*FRandom[Silverbullet](3.,6.)*RandomPick[Silverbullet](-1,1)+z*FRandom[Silverbullet](-3.,-2.); } } action void A_SwitchAmmoType( bool ifempty = false ) { bool oldsel = invoker.fcbselected; if ( invoker.fcbselected && ((invoker.Ammo1.Amount > 0) || (CountInv("SilverBullets") > 0)) ) invoker.fcbselected = (ifempty&&((invoker.Ammo2.Amount>0)||(CountInv("SilverBullets2")>0)))?true:false; else if ( !invoker.fcbselected && ((invoker.Ammo2.Amount > 0) || (CountInv("SilverBullets2") > 0)) ) invoker.fcbselected = (ifempty&&((invoker.Ammo1.Amount>0)||(CountInv("SilverBullets")>0)))?false:true; if ( oldsel != invoker.fcbselected ) A_StartSound("misc/invchange",CHAN_WEAPONEXTRA,CHANF_UI|CHANF_LOCAL); A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); } action void A_AltHold() { A_WeaponReady(WRF_NOFIRE|WRF_NOSWITCH); // tap fire to unload round if ( player.cmd.buttons&BT_ATTACK ) { player.SetPSPrite(PSP_WEAPON,invoker.FindState("Cock")); return; } if ( player.cmd.buttons&BT_ALTATTACK ) return; A_SwitchAmmoType(); if ( invoker.zoomed ) player.SetPSPrite(PSP_WEAPON,invoker.FindState("ZoomReady")); else player.SetPSPrite(PSP_WEAPON,invoker.fcbloaded?invoker.FindState("Ready2"):invoker.FindState("Ready")); } action void A_LoadMag() { MagAmmo sb = MagAmmo(FindInventory("SilverBullets")); if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) ) invoker.clipcount = invoker.default.clipcount; else if ( (invoker.Ammo1.Amount <= 0) || (sb.Amount >= sb.ClipSize) ) { int takeamt = min(sb.Amount,sb.ClipSize); invoker.clipcount = takeamt; sb.Amount -= takeamt; int req = invoker.default.ClipCount-invoker.clipcount; if ( req > 0 ) invoker.clipcount += invoker.FetchBufferedMagAmmo("SilverBullets",req); } else if ( invoker.FetchBufferedMagAmmo("SilverBullets",sb.ClipSize,true) ) invoker.clipcount = invoker.default.clipcount; else { invoker.Ammo1.Amount = max(0,invoker.Ammo1.Amount-1); invoker.clipcount = invoker.default.clipcount; } invoker.wastecycle = 0; invoker.ClearBufferedAmmo(); } action void A_LoadMagAlt() { MagAmmo sb = MagAmmo(FindInventory("SilverBullets2")); if ( sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) ) invoker.clipcount = invoker.default.clipcount; else if ( (invoker.Ammo2.Amount <= 0) || (sb.Amount >= sb.ClipSize) ) { int takeamt = min(sb.Amount,sb.ClipSize); invoker.clipcount = takeamt; sb.Amount -= takeamt; int req = invoker.default.ClipCount-invoker.clipcount; if ( req > 0 ) invoker.clipcount += invoker.FetchBufferedMagAmmo("SilverBullets2",req); } else if ( invoker.FetchBufferedMagAmmo("SilverBullets2",sb.ClipSize,true) ) invoker.clipcount = invoker.default.clipcount; else { invoker.Ammo2.Amount = max(0,invoker.Ammo2.Amount-1); invoker.clipcount = invoker.default.clipcount; } invoker.wastecycle = 0; invoker.ClearBufferedAmmo(); } override bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon ) { bool good = Super.PickupForAmmoSWWM(ownedWeapon); let Owner = ownedWeapon.Owner; if ( (AmmoGive1 == 0) && ((clipcount > 0) || chambered) ) { // we were dropped, see if we can add the bullets we contain if ( chambered ) { // first check the bullet in the chamber Class wammo = fcbchambered?'SilverBullets2':'SilverBullets'; let ma = Owner.FindInventory(wammo); if ( !ma ) { ma = Inventory(Spawn(wammo)); ma.Amount = 0; ma.AttachToOwner(Owner); } if ( ma.Amount >= ma.MaxAmount ) ma.CreateTossable(1); ma.Amount++; good = true; } if ( clipcount > 0 ) { Class wammo = fcbloaded?'SilverBullets2':'SilverBullets'; let ma = Owner.FindInventory(wammo); if ( !ma ) { ma = Inventory(Spawn(wammo)); ma.Amount = 0; ma.AttachToOwner(Owner); } int maxgiveamt = min(ma.MaxAmount-ma.Amount,clipcount); int dropamt = clipcount-maxgiveamt; if ( dropamt > 0 ) ma.CreateTossable(dropamt); ma.Amount = min(ma.MaxAmount,ma.Amount+clipcount); good = true; } } return good; } override void MarkPrecacheSounds() { Super.MarkPrecacheSounds(); MarkSound("silverbullet/select"); MarkSound("silverbullet/deselect"); MarkSound("silverbullet/meleestart"); MarkSound("silverbullet/meleeend"); MarkSound("silverbullet/idle"); MarkSound("silverbullet/boltopen"); MarkSound("silverbullet/boltclose"); MarkSound("silverbullet/magout"); MarkSound("silverbullet/magin"); MarkSound("silverbullet/zoomstart"); MarkSound("silverbullet/zooming"); MarkSound("silverbullet/zoomend"); MarkSound("silverbullet/fire1"); MarkSound("silverbullet/fire2"); MarkSound("silverbullet/fire3"); MarkSound("silverbullet/altfire1"); MarkSound("silverbullet/altfire2"); MarkSound("silverbullet/altfire3"); MarkSound("silverbullet/jet"); MarkSound("silverbullet/casing1"); MarkSound("silverbullet/casing2"); MarkSound("silverbullet/casing3"); MarkSound("silverbullet/casing4"); MarkSound("silverbullet/mag1"); MarkSound("silverbullet/mag2"); MarkSound("silverbullet/mag3"); MarkSound("silverbullet/hit1"); MarkSound("silverbullet/hit2"); MarkSound("silverbullet/flesh1"); MarkSound("silverbullet/flesh2"); MarkSound("silverbullet/chode1"); MarkSound("silverbullet/chode2"); MarkSound("silverbullet/crouch"); MarkSound("silverbullet/uncrouch"); } Default { Tag "$T_SILVERBULLET"; Inventory.PickupMessage "$T_SILVERBULLET"; Obituary "$O_SILVERBULLET"; SWWMWeapon.Tooltip "$TT_SILVERBULLET"; SWWMWeapon.GetLine "getsilverbullet"; Weapon.SlotNumber 8; Weapon.SelectionOrder 800; Weapon.UpSound "silverbullet/select"; Stamina 400000; Weapon.AmmoType1 "SilverBulletAmmo"; Weapon.AmmoGive1 1; Weapon.AmmoType2 "SilverBulletAmmo2"; Weapon.AmmoGive2 0; SWWMWeapon.DropAmmoType "SWWMCellAmmoSmall"; SilverBullet.ClipCount 5; +SWWMWEAPON.NOFIRSTGIVE; +WEAPON.ALT_AMMO_OPTIONAL; +WEAPON.EXPLOSIVE; Radius 36; Height 36; } States { Spawn: XZW1 A -1 NoDelay A_JumpIf(invoker.fcbloaded,1); XZW1 B -1; Stop; Select: XZW2 G 0 A_JumpIf(invoker.fcbloaded,"Select2"); XZW2 G 2 A_FullRaise(); XZW2 HIJKLMN 2; Goto Ready; Select2: XZW8 G 2 A_FullRaise(); XZW8 HIJKLMN 2; Goto Ready2; Ready: XZW2 A 1 { int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; A_WeaponReady(flg); if ( player.cmd.buttons&BT_ATTACK ) invoker.CheckAmmo(EitherFire,true); } Wait; Ready2: XZW8 A 1 { int flg = WRF_ALLOWRELOAD|WRF_ALLOWZOOM|WRF_ALLOWUSER1; A_WeaponReady(flg); if ( player.cmd.buttons&BT_ATTACK ) invoker.CheckAmmo(EitherFire,true); } Wait; ZoomReady: TNT1 A 1 { int flg = WRF_ALLOWRELOAD|WRF_ALLOWUSER1; if ( invoker.rezoom > 0 ) invoker.rezoom--; else flg |= WRF_ALLOWZOOM; A_WeaponReady(flg); if ( player.cmd.buttons&BT_ATTACK ) invoker.CheckAmmo(EitherFire,true); } Wait; AltFire: #### # 1 A_AltHold(); Wait; Fire: XZW2 A 1 { if ( (player.cmd.buttons&BT_ALTATTACK) && invoker.chambered ) return ResolveState("Cock"); if ( !invoker.chambered || invoker.fired ) { if ( !invoker.fired && (invoker.clipcount <= 0) && (sv_infiniteammo || FindInventory('PowerInfiniteAmmo',true) || (invoker.Ammo1.Amount > 0) || (invoker.Ammo2.Amount > 0) || (CountInv("SilverBullets") > 0) || (CountInv("SilverBullets2") > 0)) ) { A_SwitchAmmoType(true); return ResolveState("Reload"); } return ResolveState("Cock"); } A_SilverFire(); invoker.fired = true; if ( invoker.zoomed ) return ResolveState("ZoomFire"); return A_JumpIf(invoker.fcbloaded,"Fire2"); } XZW2 OPQR 2; XZW2 STUVW 3; XZW2 A 1 A_JumpIf(invoker.specialf1<=0.,"Ready"); Wait; Fire2: XZW8 A 1; XZW8 OPQR 2; XZW8 STUVW 3; XZW8 A 1 A_JumpIf(invoker.specialf1<=0.,"Ready2"); Wait; Jet: TNT1 A 3 { invoker.specialf1 = 1.5; } TNT1 A 0 { A_StartSound("silverbullet/jet",CHAN_WEAPON,CHANF_OVERLAP); A_Overlay(PSP_WEAPON+1,"JetSmoke"); A_OverlayFlags(PSP_WEAPON+1,PSPF_RENDERSTYLE|PSPF_ALPHA|PSPF_FORCESTYLE|PSPF_FORCEALPHA,true); A_OverlayRenderStyle(PSP_WEAPON+1,STYLE_Add); } TNT1 A 1 { A_JetCompensate(); return A_JumpIf(invoker.specialf1<=0.,1); } Wait; TNT1 A 1; Stop; JetSmoke: XZW7 PQR 2; XZW7 STUVW 3; XZW7 X 1 A_JumpIf(invoker.specialf1<=0.,1); Wait; TNT1 A 1; Stop; Cock: XZW2 A 0 { if ( invoker.zoomed ) return ResolveState("ZoomCock"); return invoker.fcbloaded?ResolveState("DoCock2"):ResolveState("DoCock"); } ZoomCock: TNT1 A 12 A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); TNT1 A 10 { A_StartSound("silverbullet/boltopen",CHAN_WEAPON,CHANF_OVERLAP); if ( invoker.chambered ) { int layer = PSP_WEAPON+2; while ( player.FindPSprite(layer) ) layer++; if ( invoker.fired ) { if ( invoker.fcbchambered ) A_Overlay(layer,"ZoomCasing2"); else A_Overlay(layer,"ZoomCasing"); } else { if ( invoker.fcbchambered ) A_Overlay(layer,"ZoomBullet2"); else A_Overlay(layer,"ZoomBullet"); } } if ( !invoker.chambered || invoker.fired ) invoker.wastecycle = 0; invoker.fired = false; invoker.chambered = (invoker.clipcount>0); if ( invoker.clipcount > 0 ) invoker.fcbchambered = invoker.fcbloaded; invoker.clipcount = max(0,invoker.clipcount-1); } TNT1 A 2 A_StartSound("silverbullet/boltclose",CHAN_WEAPON,CHANF_OVERLAP); TNT1 A 20 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); Goto ZoomReady; DoCock: XZW2 A 2 A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); XZW2 XYZ 2; XZW3 AB 2; XZW3 C 2 { A_StartSound("silverbullet/boltopen",CHAN_WEAPON,CHANF_OVERLAP); if ( invoker.chambered ) { int layer = PSP_WEAPON+2; while ( player.FindPSprite(layer) ) layer++; if ( invoker.fired ) { if ( invoker.fcbchambered ) A_Overlay(layer,"Casing2"); else A_Overlay(layer,"Casing"); } else { if ( invoker.fcbchambered ) A_Overlay(layer,"Bullet2"); else A_Overlay(layer,"Bullet"); } } if ( !invoker.chambered || invoker.fired ) invoker.wastecycle = 0; invoker.fired = false; invoker.chambered = (invoker.clipcount>0); if ( invoker.clipcount > 0 ) invoker.fcbchambered = invoker.fcbloaded; invoker.clipcount = max(0,invoker.clipcount-1); } XZW3 DEFG 2; XZW3 H 2 A_StartSound("silverbullet/boltclose",CHAN_WEAPON,CHANF_OVERLAP); XZW3 I 2 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZW3 JKLMNOPQR 2; Goto Ready; DoCock2: XZW8 A 2 A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); XZW8 XYZ 2; XZW9 AB 2; XZW9 C 2 { A_StartSound("silverbullet/boltopen",CHAN_WEAPON,CHANF_OVERLAP); if ( invoker.chambered ) { int layer = PSP_WEAPON+2; while ( player.FindPSprite(layer) ) layer++; if ( invoker.fired ) { if ( invoker.fcbchambered ) A_Overlay(layer,"Casing2"); else A_Overlay(layer,"Casing"); } else { if ( invoker.fcbchambered ) A_Overlay(layer,"Bullet2"); else A_Overlay(layer,"Bullet"); } } if ( !invoker.chambered || invoker.fired ) invoker.wastecycle = 0; invoker.fired = false; invoker.chambered = (invoker.clipcount>0); if ( invoker.clipcount > 0 ) invoker.fcbchambered = invoker.fcbloaded; invoker.clipcount = max(0,invoker.clipcount-1); } XZW9 DEFG 2; XZW9 H 2 A_StartSound("silverbullet/boltclose",CHAN_WEAPON,CHANF_OVERLAP); XZW9 I 2 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZW9 JKLMNOPQR 2; Goto Ready2; ZoomCasing: TNT1 A 14; TNT1 A 0 A_DropCasing(); Stop; ZoomCasing2: TNT1 A 14; TNT1 A 0 A_DropCasing(true); Stop; ZoomBullet: TNT1 A 14; TNT1 A 0 A_DropBullet(); Stop; ZoomBullet2: TNT1 A 14; TNT1 A 0 A_DropBullet(true); Stop; Casing: XZW7 D 2 { A_OverlayOffset(OverlayID(),0,0); invoker.casex = FRandom[Silverbullet](-2.,2.); invoker.casey = FRandom[Silverbullet](-2.,2.); } XZW7 E 2; XZW7 FGHIJKLMNO 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); TNT1 A 0 A_DropCasing(); Stop; Casing2: XZWD D 2 { A_OverlayOffset(OverlayID(),0,0); invoker.casex = FRandom[Silverbullet](-2.,2.); invoker.casey = FRandom[Silverbullet](-2.,2.); } XZWD E 2; XZWD FGHIJKLMNO 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); TNT1 A 0 A_DropCasing(true); Stop; Bullet: XZWD P 2 { A_OverlayOffset(OverlayID(),0,0); invoker.casex = FRandom[Silverbullet](-2.,2.); invoker.casey = FRandom[Silverbullet](-2.,2.); } XZWD Q 2; XZWD RSTUVWXYZ 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); XZWE A 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); TNT1 A 0 A_DropBullet(); Stop; Bullet2: XZWE B 2 { A_OverlayOffset(OverlayID(),0,0); invoker.casex = FRandom[Silverbullet](-2.,2.); invoker.casey = FRandom[Silverbullet](-2.,2.); } XZWE C 2; XZWE DEFGHIJKLM 1 A_OverlayOffset(OverlayID(),invoker.casex,invoker.casey,WOF_ADD|WOF_INTERPOLATE); TNT1 A 0 A_DropBullet(true); Stop; ZoomFire: TNT1 A 24; TNT1 A 1 A_JumpIf(invoker.specialf1<=0.,"ZoomReady"); Wait; Zoom: XZW2 A 0 A_JumpIf(invoker.zoomed,"UnZoom"); XZW2 A 0 A_JumpIf(invoker.fcbloaded,"Zoom2"); XZW2 A 2 { A_StartSound("silverbullet/zoomstart",CHAN_WEAPON,CHANF_OVERLAP); invoker.zoomlevel = 0; } XZW3 STUVWX 1; TNT1 A 1 { invoker.zoomed = true; invoker.zoomlevel = clamp(invoker.zoomlevel*1.05+.05,1.,16.); A_ZoomFactor(invoker.zoomlevel); if ( !(level.maptime%2) && (invoker.zoomlevel < 16.) ) A_StartSound("silverbullet/zooming",CHAN_WEAPON,CHANF_OVERLAP,.2); if ( !(player.cmd.buttons&BT_ZOOM) ) { invoker.rezoom = 3; return ResolveState("ZoomReady"); } return ResolveState(null); } Wait; Zoom2: XZW8 A 0 A_JumpIf(invoker.zoomed,"UnZoom2"); XZW8 A 2 { A_StartSound("silverbullet/zoomstart",CHAN_WEAPON,CHANF_OVERLAP); invoker.zoomlevel = 0; } XZW9 STUVWX 1; TNT1 A 1 { invoker.zoomed = true; invoker.zoomlevel = clamp(invoker.zoomlevel*1.05+.05,1.,16.); A_ZoomFactor(invoker.zoomlevel); if ( !(level.maptime%2) && (invoker.zoomlevel < 16.) ) A_StartSound("silverbullet/zooming",CHAN_WEAPON,CHANF_OVERLAP,.2); if ( !(player.cmd.buttons&BT_ZOOM) ) { invoker.rezoom = 3; return ResolveState("ZoomReady"); } return ResolveState(null); } Wait; UnZoom: XZW3 X 0 A_JumpIf(invoker.fcbloaded,"UnZoom2"); XZW3 X 1 { A_StartSound("silverbullet/zoomend",CHAN_WEAPON,CHANF_OVERLAP); invoker.zoomed = false; invoker.zoomlevel = 0; A_ZoomFactor(1.,ZOOM_INSTANT); } XZW3 YZ 1; XZW4 ABC 1; XZW2 A 0 { if ( invoker.dezoomstate ) { State tmp = invoker.dezoomstate; invoker.dezoomstate = null; return tmp; } return ResolveState(null); } Goto Ready; UnZoom2: XZW9 X 1 { A_StartSound("silverbullet/zoomend",CHAN_WEAPON,CHANF_OVERLAP); invoker.zoomed = false; invoker.zoomlevel = 0; A_ZoomFactor(1.,ZOOM_INSTANT); } XZW9 YZ 1; XZWA ABC 1; XZW8 A 0 { if ( invoker.dezoomstate ) { State tmp = invoker.dezoomstate; invoker.dezoomstate = null; return tmp; } return ResolveState(null); } Goto Ready2; Reload: XZW2 A 0 { // autoswitch if needed A_SwitchAmmoType(true); if ( ((invoker.clipcount >= invoker.default.clipcount) && (invoker.fcbselected == invoker.fcbloaded)) || (!sv_infiniteammo && !FindInventory('PowerInfiniteAmmo',true) && (invoker.Ammo1.Amount <= 0) && (invoker.Ammo2.Amount <= 0) && (CountInv("SilverBullets") <= 0) && (CountInv("SilverBullets2") <= 0)) ) return ResolveState("Idle"); if ( invoker.zoomed ) { invoker.dezoomstate = invoker.fcbloaded?ResolveState("Unload2"):ResolveState("Unload"); return ResolveState("UnZoom"); } return A_JumpIf(invoker.fcbloaded,"Unload2"); } Unload: XZW2 A 2 { A_PlayerReload(); A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); return A_JumpIf(invoker.clipcount<=0,"UnloadEmpty"); } XZW4 DEFGHI 2; XZW4 J 2 A_StartSound("silverbullet/magout",CHAN_WEAPON,CHANF_OVERLAP); XZW4 KLMN 2; Goto Load; UnloadEmpty: XZW2 A 2; XZW4 OPQRST 2; XZW4 U 2 A_StartSound("silverbullet/magout",CHAN_WEAPON,CHANF_OVERLAP); XZW4 VWXY 2; Goto Load; Unload2: XZW8 A 2 { A_PlayerReload(); A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); return A_JumpIf(invoker.clipcount<=0,"UnloadEmpty2"); } XZWA DEFGHI 2; XZWA J 2 A_StartSound("silverbullet/magout",CHAN_WEAPON,CHANF_OVERLAP); XZWA KLMN 2; Goto Load; UnloadEmpty2: XZW8 A 2; XZWA OPQRST 2; XZWA U 2 A_StartSound("silverbullet/magout",CHAN_WEAPON,CHANF_OVERLAP); XZWA VWXY 2; Goto Load; Load: XZW4 Z 2 { A_DropMag(); return A_JumpIf(invoker.fcbselected,"Load2"); } XZW5 ABC 2; XZW5 D 2 { invoker.fcbloaded = false; A_LoadMag(); A_SwitchAmmoType(true); A_StartSound("silverbullet/magin",CHAN_WEAPON,CHANF_OVERLAP); } XZW5 E 2 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZW5 FGHIJKLMN 2; Goto Ready; Load2: XZWA Z 2; XZWB ABC 2; XZWB D 2 { invoker.fcbloaded = true; A_LoadMagAlt(); A_SwitchAmmoType(true); A_StartSound("silverbullet/magin",CHAN_WEAPON,CHANF_OVERLAP); } XZWB E 2 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZWB FGHIJKLMN 2; Goto Ready2; Idle: XZW2 A 0 { if ( invoker.zoomed ) { invoker.dezoomstate = invoker.fcbloaded?ResolveState("DoIdle2"):ResolveState("DoIdle"); return ResolveState("UnZoom"); } return invoker.fcbloaded?ResolveState("DoIdle2"):ResolveState("DoIdle"); } DoIdle: XZW2 A 2 { A_StartSound("silverbullet/idle",CHAN_WEAPON,CHANF_OVERLAP); A_PlayerCheckGun(); } XZW5 OPQR 3; XZW5 STUV 2; XZW5 WXYZ 3; XZW6 ABCD 4; XZW6 EFGH 3; Goto Ready; DoIdle2: XZW8 A 2 { A_StartSound("silverbullet/idle",CHAN_WEAPON,CHANF_OVERLAP); A_PlayerCheckGun(); } XZWB OPQR 3; XZWB STUV 2; XZWB WXYZ 3; XZWC ABCD 4; XZWC EFGH 3; Goto Ready2; User1: XZW2 A 0 { if ( invoker.zoomed ) { invoker.dezoomstate = invoker.fcbloaded?ResolveState("DoUser12"):ResolveState("DoUser1"); return ResolveState("UnZoom"); } return invoker.fcbloaded?ResolveState("DoUser12"):ResolveState("DoUser1"); } DoUser1: XZW2 A 2 { A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); A_PlayerMelee(); } XZW6 IJK 3; XZW6 L 1 A_Parry(9); XZW6 MN 1; XZW6 O 1 A_Melee(90,"demolitionist/whitl",1.6,2.,2.,MELEE_Wider); XZW6 PQ 1; XZW6 RST 2; XZW6 U 3 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZW6 VWXYZ 3; XZW7 ABC 2; Goto Ready; DoUser12: XZW8 A 2 { A_StartSound("silverbullet/meleestart",CHAN_WEAPON,CHANF_OVERLAP); A_StartSound("demolitionist/wswing",CHAN_WEAPON,CHANF_OVERLAP); A_PlayerMelee(); } XZWC IJK 3; XZWC L 1 A_Parry(9); XZWC MN 1; XZWC O 1 A_Melee(90,"demolitionist/whitl",1.6,2.,2.,MELEE_Wider); XZWC PQ 1; XZWC RST 2; XZWC U 3 A_StartSound("silverbullet/meleeend",CHAN_WEAPON,CHANF_OVERLAP); XZWC VWXYZ 3; XZWD ABC 2; Goto Ready2; Deselect: XZW2 A 0 { if ( invoker.zoomed ) { invoker.dezoomstate = invoker.fcbloaded?ResolveState("DoDeselect2"):ResolveState("DoDeselect"); return ResolveState("UnZoom"); } return invoker.fcbloaded?ResolveState("DoDeselect2"):ResolveState("DoDeselect"); } DoDeselect: XZW2 A 2 A_StartSound("silverbullet/deselect",CHAN_WEAPON,CHANF_OVERLAP); XZW2 BCDEF 2; XZW2 G -1 A_FullLower(); Stop; DoDeselect2: XZW8 A 2 A_StartSound("silverbullet/deselect",CHAN_WEAPON,CHANF_OVERLAP); XZW8 BCDEF 2; XZW8 G -1 A_FullLower(); Stop; Flash: XZWZ A 2 Bright { let l = Spawn("SWWMWeaponLight",pos); l.args[3] = 200; l.target = self; } Stop; } }