// Base class for all SWWM Weapons Class SWWMWeapon : Weapon abstract { Mixin SWWMOverlapPickupSound; bool wasused; bool bUsePickup; private int SWeaponFlags; meta String tooltip, getline; bool tooltipsent; meta Class dropammotype; int dropamount; bool bInitialized; Property Tooltip : tooltip; Property GetLine : getline; Property DropAmmoType : dropammotype; FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count) FlagDef HideInMenu : SWeaponFlags, 1; // don't show in inventory menu (usually for sister weapons) FlagDef NoSwapWeapon : SWeaponFlags, 2; // weapon is not affected by slot swapping FlagDef HasScrTex : SWeaponFlags, 3; // weapon model has a scripted texture (calls RenderTexture() from Event Handler) FlagDef SwappedTo : SWeaponFlags, 4; // this weapon was swapped to on pickup (so it won't respawn) int oldtagcolor; bool IsSwapWeapon( Inventory i ) const { if ( bNoSwapWeapon || (i.GetClass() == GetClass()) ) return false; let w = SWWMWeapon(i); if ( w && !w.bNoSwapWeapon && (SlotNumber != -1) && (w.SlotNumber == SlotNumber) ) return true; let wg = SWWMDualWeaponGiver(i); if ( wg ) { let w = wg.giveme[0]; if ( w && !w.bNoSwapWeapon && (SlotNumber != -1) & (w.SlotNumber == SlotNumber) ) return true; } return false; } SWWMWeapon HasSwapWeapon( Actor other ) const { if ( bNoSwapWeapon ) return null; for ( Inventory i=other.inv; i; i=i.inv ) { if ( IsSwapWeapon(i) ) return SWWMWeapon(i); } return null; } override void Touch( Actor toucher ) { // show message about unimplemented weapons State ReadyState = FindState('Ready'); if ( !ReadyState || !ReadyState.ValidateSpriteFrame() ) { if ( toucher.CheckLocalView() ) Console.MidPrint(SmallFont,String.Format(StringTable.Localize(SWWMUtility.SellFemaleItem(self,"SWWM_TODOWEAPON_FEM")?"$SWWM_TODOWEAPON_FEM":"$SWWM_TODOWEAPON"),GetTag())); return; } // show prompt to swap weapon, and prevent normal pickup SWWMWeapon sw; if ( bSPECIAL && swwm_swapweapons && (sw = HasSwapWeapon(toucher)) ) { if ( toucher.CheckLocalView() ) { // use sisterweapon tag for dual wield (slot 2 weapons) if ( sw.SisterWeapon && (sw.Amount > 1) ) Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.SisterWeapon.GetTag(),GetTag())); else Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),GetTag())); } return; } // explicit use-to pickup, function must be called from Used() virtual if ( toucher.player && swwm_usetopickup && !bUsePickup ) return; Super.Touch(toucher); } // allow pickup by use + swap weapon support override bool Used( Actor user ) { // can't pick up if ( !bSPECIAL ) return false; // no use through melee if ( (user.player.ReadyWeapon is 'SWWMWeapon') && SWWMWeapon(user.player.ReadyWeapon).wallponch ) return false; Vector3 itempos = Vec3Offset(0,0,Height/2), userpos = user.Vec2OffsetZ(0,0,user.player.viewz); // test vertical range Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); if ( abs(diff.z) > rang ) return false; // if the toucher owns our SwapWeapon, drop it before picking us up bool swapto = false; SWWMWeapon sw; State ReadyState = FindState('Ready'); if ( swwm_swapweapons && (sw = HasSwapWeapon(user)) && (user.player.WeaponState&WF_WEAPONSWITCHOK) && !(user.player.WeaponState&WF_DISABLESWITCH) && ReadyState && ReadyState.ValidateSpriteFrame() ) { // special case, otherwise candy gun won't drop itself if ( sw is 'CandyGun' ) CandyGun(sw).swapdrop = true; if ( (sw == user.player.ReadyWeapon) || (sw.SisterWeapon && (sw.SisterWeapon == user.player.ReadyWeapon)) ) swapto = true; // don't autoswitch just yet (hacky) if ( swapto ) { user.player.ReadyWeapon = null; user.player.PendingWeapon = WP_NOCHANGE; } int ngun = sw.Amount; if ( ngun == 2 ) { // create a dual giver let dg = SWWMDualWeaponGiver(Spawn("SWWMDualWeaponGiver",pos)); dg.bDROPPED = bDROPPED; // inherit drop flag dg.angle = angle; dg.vel = vel; dg.FloatBobPhase = FloatBobPhase; // transfer both guns dg.giveme[0] = SWWMWeapon(sw.CreateTossable(1)); dg.giveme[0].AttachToOwner(dg); dg.giveme[1] = SWWMWeapon(sw.CreateTossable(1)); dg.giveme[1].AttachToOwner(dg); dg.SetPickupState(); dg.bSPECIAL = false; dg.DropTime = 30; } else { // swap in-place let d = user.DropInventory(sw); d.bDROPPED = bDROPPED; // inherit drop flag d.SetOrigin(pos,false); d.angle = angle; d.vel = vel; d.FloatBobPhase = FloatBobPhase; d.bSPECIAL = false; d.DropTime = 30; } } bUsePickup = true; bSWAPPEDTO = true; Touch(user); bUsePickup = false; // we got picked up if ( bDestroyed || Owner || !bSPECIAL ) { bSWAPPEDTO = false; // clear this flag // autoswitch to us if we got swapped if ( swapto ) user.A_SelectWeapon(GetClass()); Vector3 tracedir = level.Vec3Diff(userpos,itempos); double dist = tracedir.length(); tracedir /= dist; let cf = new("CrossLineFinder"); cf.Trace(userpos,level.PointInSector(userpos.xy),tracedir,dist,0); // trigger all player cross lines found between user and item for ( int i=0; i 0 ) gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,AmmoGive1); if ( AmmoGive2 > 0 ) gotstuff |= AddExistingAmmo(ownedWeapon.Ammo2,AmmoGive2); let Owner = ownedWeapon.Owner; if ( gotstuff && Owner && Owner.player ) { if ( ownedWeapon.Ammo1 && (oldamount1 == 0) ) PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass()); else if ( ownedWeapon.Ammo2 && (oldamount2 == 0) ) PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo2.GetClass()); } if ( ownedWeapon.Ammo1 ) { // subtract price of ammo we don't give int ammonotgiven = default.AmmoGive1-AmmoGive1; if ( ammonotgiven > 0 ) Stamina -= int(abs(ownedWeapon.Ammo1.Stamina)*(1.+.75*(ammonotgiven-1))); // subtract price of given ammo int ammogiven = ownedWeapon.Ammo1.Amount-oldamount1; if ( ammogiven > 0 ) Stamina -= int(abs(ownedWeapon.Ammo1.Stamina)*(1.+.75*(ammogiven-1))); // drop excess int dropme = AmmoGive1-ammogiven; if ( dropme > 0 ) { if ( (ownedWeapon.Ammo1 is 'SWWMAmmo') && SWWMAmmo(ownedWeapon.Ammo1).MagAmmoType ) { // can we add it as mag ammo? MagAmmo ma = MagAmmo(Owner.FindInventory(SWWMAmmo(ownedWeapon.Ammo1).MagAmmoType)); if ( !ma ) { ma = MagAmmo(Spawn(SWWMAmmo(ownedWeapon.Ammo1).MagAmmoType)); ma.Amount = 0; ma.AttachToOwner(Owner); } while ( ma.Amount <= (ma.MaxAmount-ma.ClipSize) ) { ma.Amount += ma.ClipSize; dropme--; } } if ( dropme > 0 ) { // hacky, but it works ownedWeapon.Ammo1.CreateTossable(dropme); ownedWeapon.Ammo1.Amount += dropme; } } } if ( ownedWeapon.Ammo2 ) { // subtract price of ammo we don't give int ammonotgiven = default.AmmoGive2-AmmoGive2; if ( ammonotgiven > 0 ) Stamina -= int(abs(ownedWeapon.Ammo2.Stamina)*(1.+.75*(ammonotgiven-1))); // subtract price of given ammo int ammogiven = ownedWeapon.Ammo2.Amount-oldamount2; if ( ammogiven > 0 ) Stamina -= int(abs(ownedWeapon.Ammo2.Stamina)*(1.+.75*(ammogiven-1))); // drop excess int dropme = AmmoGive2-ammogiven; if ( dropme > 0 ) { if ( (ownedWeapon.Ammo2 is 'SWWMAmmo') && SWWMAmmo(ownedWeapon.Ammo2).MagAmmoType ) { // can we add it as mag ammo? MagAmmo ma = MagAmmo(Owner.FindInventory(SWWMAmmo(ownedWeapon.Ammo2).MagAmmoType)); if ( !ma ) { ma = MagAmmo(Spawn(SWWMAmmo(ownedWeapon.Ammo2).MagAmmoType)); ma.Amount = 0; ma.AttachToOwner(Owner); } while ( ma.Amount <= (ma.MaxAmount-ma.ClipSize) ) { ma.Amount += ma.ClipSize; dropme--; } } if ( dropme > 0 ) { // hacky, but it works ownedWeapon.Ammo2.CreateTossable(dropme); ownedWeapon.Ammo2.Amount += dropme; } } } return gotstuff; } override bool HandlePickup( Inventory item ) { // can't hold both weapons at once if ( swwm_swapweapons && IsSwapWeapon(item) ) return true; if ( (GetClass() == item.GetClass()) && !item.ShouldStay() ) { if ( SWWMWeapon(item).PickupForAmmoSWWM(self) ) item.bPickupGood = true; 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; } // reset the price in case it has to respawn item.Stamina = item.default.Stamina; return true; } return Super.HandlePickup(item); } override bool ShouldStay() { // SWWM weapons never stay unless explicitly stated if ( !bDROPPED && (deathmatch || alwaysapplydmflags) && sv_weaponstay ) return true; return false; } // we need to copy this since we can't re-use the mixin override void Hide() { bSPECIAL = false; bNOGRAVITY = true; bINVISIBLE = true; SetState(FindState("HideDoomish")); tics = 1050; if ( (self is 'Ammo') || (self is 'MagAmmo') ) tics -= 350; if ( self.bBIGPOWERUP || SWWMUtility.IsVIPItem(self) ) tics += 1050; if ( RespawnTics != 0 ) tics = RespawnTics; if ( ShouldRespawn() ) { Vector3 oldpos = pos; A_RestoreSpecialPosition(); let t = Spawn("SWWMRespawnTimer",pos); t.tracer = self; t.special1 = tics; t.A_SetSize(radius,height); SetOrigin(oldpos,false); } } override bool ShouldRespawn() { // swapped-to weapons do not respawn if ( bSWAPPEDTO ) return false; // always respawn in DM if ( deathmatch && !bNEVERRESPAWN ) return true; if ( (bBigPowerup || SWWMUtility.IsVIPItem(self)) && !sv_respawnsuper ) return false; if ( bNEVERRESPAWN ) return false; return (sv_itemrespawn||bALWAYSRESPAWN); } protected Ammo AddAmmoSWWM( Actor other, Class ammotype, int amount ) { // no weirdass factors or anything involved in this one // just simple and straightforward if ( !ammotype ) return null; let amo = Ammo(other.FindInventory(ammotype)); if ( !amo ) { amo = Ammo(Spawn(ammotype)); amo.amount = 0; amo.AttachToOwner(other); } amo.amount += amount; if ( amo.amount > amo.maxamount && !sv_unlimited_pickup ) amo.amount = amo.maxamount; return amo; } override void AttachToOwner( Actor other ) { Inventory.AttachToOwner(other); Ammo1 = AddAmmoSWWM(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1); Ammo2 = AddAmmoSWWM(Owner,AmmoType2,bNoFirstGive?0:AmmoGive2); SisterWeapon = AddWeapon(SisterWeaponType); if ( !bInitialized ) { InitializeWeapon(); bInitialized = true; } if ( Owner.player ) { if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch && ReportHUDAmmo() ) // hey, as long as it works Owner.player.PendingWeapon = self; if ( Owner.player.mo == players[consoleplayer].camera ) StatusBar.ReceivedWeapon(self); } GivenAsMorphWeapon = false; bSwappedTo = false; } override void OwnerDied() { ClearBufferedAmmo(); if ( Owner.player && (Owner.player.ReadyWeapon == self) ) { Owner.A_StopSound(CHAN_WEAPONEXTRA); Owner.A_StopSound(CHAN_WEAPONEXTRA2); Owner.A_StopSound(CHAN_WEAPONEXTRA3); } A_ClearRefire(); Super.OwnerDied(); } override void Travelled() { ClearBufferedAmmo(); Super.Travelled(); } override void DetachFromOwner() { ClearBufferedAmmo(); Super.DetachFromOwner(); } override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack ) { if ( mod == 'Melee' ) return StringTable.Localize("$O_MELEE"); return Super.GetObituary(victim,inflictor,mod,playerattack); } // override in subclasses to perform first-time setup on a newly obtained weapon virtual void InitializeWeapon() { } // draw ammo on hud above weapon box virtual ui void DrawWeapon( double TicFrac, double bx, double by, double hs, Vector2 ss ) { } // extra drawing, usually scopes virtual ui void RenderUnderlay( RenderEvent e ) { } // HUD-side ticking virtual ui void HudTick() { } // updating a scripted texture in the model virtual ui void RenderTexture( RenderEvent e ) { } // update color tags on first person model void UpdateTags( int idx, String colname ) { if ( idx != oldtagcolor ) SetTags(colname); oldtagcolor = idx; } // should be overridable in case indices aren't the same // (or there's dual-wielding) virtual void SetTags( String colname ) { A_ChangeModel("",1,"","",0,"models","DemoTags"..colname..".png",CMDL_USESURFACESKIN,-1); } // third-person animations for player action void A_PlayerFire() { let demo = Demolitionist(player.mo); if ( demo && (demo.Health > 0) ) demo.PlayFire(); } action void A_PlayerMelee( bool bFast = false ) { let demo = Demolitionist(player.mo); if ( demo && (demo.Health > 0) ) { if ( bFast ) demo.PlayFastMelee(); else demo.PlayMelee(); } } action void A_PlayerReload( bool bFast = false ) { let demo = Demolitionist(player.mo); if ( demo && (demo.Health > 0) ) { if ( bFast ) demo.PlayFastReload(); else demo.PlayReload(); } } action void A_PlayerCheckGun() { let demo = Demolitionist(player.mo); if ( demo && (demo.Health > 0) ) demo.PlayCheckGun(); } // instant raise/lower action void A_FullRaise() { if ( !player ) return; if ( player.PendingWeapon != WP_NOCHANGE ) { player.mo.DropWeapon(); return; } if ( !player.ReadyWeapon ) return; let psp = player.GetPSprite(PSP_WEAPON); if ( !psp ) return; ResetPSprite(psp); psp.y = WEAPONTOP; // do not jump to ready state here, the weapon should do that // directly once it finishes playing its select animation } action void A_FullLower() { if ( !player ) return; if ( !player.ReadyWeapon ) { player.mo.BringUpWeapon(); return; } let psp = player.GetPSprite(PSP_WEAPON); if ( !psp ) return; psp.y = WEAPONBOTTOM; ResetPSprite(psp); if ( player.playerstate == PST_DEAD ) { // Player is dead, so don't bring up a pending weapon // Player is dead, so keep the weapon off screen player.SetPSprite(PSP_FLASH,null); psp.SetState(player.ReadyWeapon.FindState('DeadLowered')); return; } // [RH] Clear the flash state. Only needed for Strife. player.SetPSprite(PSP_FLASH,null); player.mo.BringUpWeapon(); } override void PlayUpSound( Actor origin ) { if ( UpSound ) origin.A_StartSound(UpSound,CHAN_WEAPON,CHANF_OVERLAP); } override void Tick() { Super.Tick(); if ( !Owner ) return; if ( !Owner.player || (Owner.player.ReadyWeapon != self) || !(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH) ) { tooltipsent = false; return; } if ( tooltipsent ) return; tooltipsent = true; if ( Owner.player == players[consoleplayer] ) SWWMUtility.SendTooltip(GetClass()); } action void A_SWWMFlash( StateLabel flashlabel = null ) { if ( !player || !player.ReadyWeapon ) return; Weapon weap = player.ReadyWeapon; State flashstate = null; if ( !flashlabel ) { if ( weap.bAltFire ) flashstate = weap.FindState('AltFlash'); if ( !flashstate ) flashstate = weap.FindState('Flash'); } else flashstate = weap.FindState(flashlabel); player.SetPSprite(PSP_FLASH,flashstate); A_OverlayFlags(PSP_FLASH,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true); A_OverlayRenderStyle(PSP_FLASH,STYLE_Add); } // tells the SWWM HUD that this weapon has ammo available virtual clearscope bool ReportHUDAmmo() { return (!Ammo1||(Ammo1.Amount>0)||(Ammo2&&(Ammo2.Amount>0))); } // tells the Embiggener that this weapon uses the specified ammo type // even if it is not its primary one virtual clearscope bool UsesAmmo( Class kind ) { return (AmmoType1&&(kind is AmmoType1))||(AmmoType2&&(kind is AmmoType2)); } // ensure weapons dropped by enemies only give one unit of each ammo override void ModifyDropAmount( int dropamount ) { self.dropamount = dropamount; Super.ModifyDropAmount(dropamount); if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) ) AmmoGive1 = 1; if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) ) AmmoGive2 = 1; } override bool SpecialDropAction( Actor dropper ) { if ( swwm_enemydrops > 0 ) return false; else if ( swwm_enemydrops == 0 ) { // drop our corresponding ammo if ( !DropAmmoType ) return true; let a = Spawn(DropAmmoType,pos,ALLOW_REPLACE); if ( !a ) return true; a.bDROPPED = true; a.bNOGRAVITY = false; if ( !(level.compatflags&COMPATF_NOTOSSDROPS) ) a.TossItem(); if ( a is 'Inventory' ) { let i = Inventory(a); i.ModifyDropAmount(dropamount); i.bTOSSED = true; if ( i.SpecialDropAction(dropper) ) i.Destroy(); } return true; } // no weapon drops from enemies 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; let ret = Super.CreateTossable(amt); // reattach our glow if we became a pickup if ( (ret == self) && (PickupFlash is 'SWWMPickupFlash') ) { let p = Spawn(PickupFlash,pos); p.target = self; p.SetStateLabel("Pickup"); } return ret; } Default { Weapon.BobStyle "Alpha"; Weapon.BobSpeed 3.0; Weapon.BobRangeX 0.5; Weapon.BobRangeY 0.2; Weapon.YAdjust 0; Weapon.SlotPriority 1.; Inventory.RestrictedTo "Demolitionist"; Inventory.PickupFlash "SWWMRedPickupFlash"; +INVENTORY.IGNORESKILL; +WEAPON.NOALERT; +WEAPON.NODEATHINPUT; +FLOATBOB; +DONTGIB; FloatBobStrength 0.25; } } // used for dual swapweapons Class SWWMDualWeaponGiver : Inventory { Mixin SWWMOverlapPickupSound; Mixin SWWMRespawn; bool bUsePickup; SWWMWeapon giveme[2]; Default { Inventory.PickupSound "misc/w_pkup"; Inventory.RestrictedTo "Demolitionist"; Inventory.PickupFlash "SWWMRedPickupFlash"; +WEAPONSPAWN; +FLOATBOB; +DONTGIB; +INVENTORY.NEVERRESPAWN; +INVENTORY.QUIET; FloatBobStrength 0.25; } SWWMWeapon HasSwapWeapon( Actor other ) const { if ( giveme[0].bNoSwapWeapon ) return null; for ( Inventory i=other.inv; i; i=i.inv ) { if ( giveme[0].IsSwapWeapon(i) ) return SWWMWeapon(i); } return null; } override void Touch( Actor toucher ) { // show prompt to swap weapon, and prevent normal pickup SWWMWeapon sw; if ( bSPECIAL && swwm_swapweapons && (sw = giveme[0].HasSwapWeapon(toucher)) ) { if ( toucher.CheckLocalView() ) { // use sisterweapon tag for dual wield (slot 2 weapons) if ( sw.SisterWeapon && (sw.Amount > 1) ) Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.SisterWeapon.GetTag(),GetTag())); else Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),GetTag())); } return; } // explicit use-to pickup, function must be called from Used() virtual if ( toucher.player && swwm_usetopickup && !bUsePickup ) return; Super.Touch(toucher); } override bool Used( Actor user ) { // can't pick up if ( !bSPECIAL ) return false; // no use through melee if ( (user.player.ReadyWeapon is 'SWWMWeapon') && SWWMWeapon(user.player.ReadyWeapon).wallponch ) return false; Vector3 itempos = Vec3Offset(0,0,Height/2), userpos = user.Vec2OffsetZ(0,0,user.player.viewz); // test vertical range Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2)); double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2); if ( abs(diff.z) > rang ) return false; // if the toucher owns our SwapWeapon, drop it before picking us up bool swapto = false; SWWMWeapon sw; if ( swwm_swapweapons && (sw = giveme[0].HasSwapWeapon(user)) ) { // no need for candygun check here if ( (sw == user.player.ReadyWeapon) || (sw.SisterWeapon && (sw.SisterWeapon == user.player.ReadyWeapon)) ) swapto = true; int ngun = sw.Amount; if ( ngun == 2 ) { // create a dual giver let dg = SWWMDualWeaponGiver(Spawn("SWWMDualWeaponGiver",pos)); dg.angle = angle; dg.vel = vel; dg.FloatBobPhase = FloatBobPhase; // transfer both guns dg.giveme[0] = SWWMWeapon(sw.CreateTossable(1)); dg.giveme[0].AttachToOwner(dg); dg.giveme[1] = SWWMWeapon(sw.CreateTossable(1)); dg.giveme[1].AttachToOwner(dg); dg.SetPickupState(); } else { // swap in-place let d = user.DropInventory(sw); d.SetOrigin(pos,false); d.angle = angle; d.vel = vel; d.FloatBobPhase = FloatBobPhase; } // don't autoswitch just yet (hacky) if ( swapto ) { user.player.ReadyWeapon = null; user.player.PendingWeapon = WP_NOCHANGE; } } bUsePickup = true; Touch(user); bUsePickup = false; // we got picked up if ( bDestroyed || Owner || !bSPECIAL ) { // autoswitch to us if we got swapped if ( swapto ) user.A_SelectWeapon(giveme[0].GetClass()); Vector3 tracedir = level.Vec3Diff(userpos,itempos); double dist = tracedir.length(); tracedir /= dist; let cf = new("CrossLineFinder"); cf.Trace(userpos,level.PointInSector(userpos.xy),tracedir,dist,0); // trigger all player cross lines found between user and item for ( int i=0; i