gzdoom/wadsrc/static/zscript/actors/inventory/weapons.zs
Boondorl 12dc5c1506 Further morphing clean up
Players will now use their Alternative field to check if they're morphed instead of their MorphTics. This makes the current state of morphing more reliable, otherwise setting this to 0 manually without unmorphing could have very odd results. Both monsters and players consider 0 morph time to mean infinite now (previously this only applied to monsters). Player unmorphs no longer die in the case of a failed unmorph on death. Removed inventory swapping on player pointer substitution as it's too messy to do here.
2024-03-01 08:35:31 -05:00

1184 lines
31 KiB
Text

class Weapon : StateProvider
{
enum EFireMode
{
PrimaryFire,
AltFire,
EitherFire
};
const ZOOM_INSTANT = 1;
const ZOOM_NOSCALETURNING = 2;
deprecated("3.7") uint WeaponFlags; // not to be used directly.
class<Ammo> AmmoType1, AmmoType2; // Types of ammo used by self weapon
int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon
deprecated("3.7") int MinAmmo1, MinAmmo2; // not used anywhere and thus deprecated.
int AmmoUse1, AmmoUse2; // How much ammo to use with each shot
int Kickback;
double YAdjust; // For viewing the weapon fullscreen
sound UpSound, ReadySound; // Sounds when coming up and idle
class<Weapon> SisterWeaponType; // Another weapon to pick up with self one
int SelectionOrder; // Lower-numbered weapons get picked first
int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo
int ReloadCounter; // For A_CheckForReload
int BobStyle; // [XA] Bobbing style. Defines type of bobbing (e.g. Normal, Alpha) (visual only so no need to be a double)
float BobSpeed; // [XA] Bobbing speed. Defines how quickly a weapon bobs.
float BobRangeX, BobRangeY; // [XA] Bobbing range. Defines how far a weapon bobs in either direction.
double WeaponScaleX, WeaponScaleY; // [XA] Weapon scale. Defines the scale for the held weapon sprites (PSprite). Defaults to (1.0, 1.2) since that's what Doom does.
Ammo Ammo1, Ammo2; // In-inventory instance variables
Weapon SisterWeapon;
double FOVScale;
double LookScale; // Multiplier for look sensitivity (like FOV scaling but without the zooming)
int Crosshair; // 0 to use player's crosshair
bool GivenAsMorphWeapon;
bool bAltFire; // Set when this weapon's alternate fire is used.
readonly bool bDehAmmo; // Uses Doom's original amount of ammo for the respective attack functions so that old DEHACKED patches work as intended.
// AmmoUse1 will be set to the first attack's ammo use so that checking for empty weapons still works
meta int SlotNumber;
meta double SlotPriority;
Vector3 BobPivot3D; // Pivot used for BobWeapon3D
virtual ui Vector2 ModifyBobLayer(Vector2 Bob, int layer, double ticfrac) { return Bob; }
virtual ui Vector3, Vector3 ModifyBobLayer3D(Vector3 Translation, Vector3 Rotation, int layer, double ticfrac) { return Translation, Rotation; }
virtual ui Vector3 ModifyBobPivotLayer3D(int layer, double ticfrac) { return BobPivot3D; }
property AmmoGive: AmmoGive1;
property AmmoGive1: AmmoGive1;
property AmmoGive2: AmmoGive2;
property AmmoUse: AmmoUse1;
property AmmoUse1: AmmoUse1;
property AmmoUse2: AmmoUse2;
property AmmoType: AmmoType1;
property AmmoType1: AmmoType1;
property AmmoType2: AmmoType2;
property Kickback: Kickback;
property ReadySound: ReadySound;
property SelectionOrder: SelectionOrder;
property MinSelectionAmmo1: MinSelAmmo1;
property MinSelectionAmmo2: MinSelAmmo2;
property SisterWeapon: SisterWeaponType;
property UpSound: UpSound;
property YAdjust: YAdjust;
property BobSpeed: BobSpeed;
property BobRangeX: BobRangeX;
property BobRangeY: BobRangeY;
property WeaponScaleX: WeaponScaleX;
property WeaponScaleY: WeaponScaleY;
property SlotNumber: SlotNumber;
property SlotPriority: SlotPriority;
property LookScale: LookScale;
property BobPivot3D : BobPivot3D;
flagdef NoAutoFire: WeaponFlags, 0; // weapon does not autofire
flagdef ReadySndHalf: WeaponFlags, 1; // ready sound is played ~1/2 the time
flagdef DontBob: WeaponFlags, 2; // don't bob the weapon
flagdef AxeBlood: WeaponFlags, 3; // weapon makes axe blood on impact
flagdef NoAlert: WeaponFlags, 4; // weapon does not alert monsters
flagdef Ammo_Optional: WeaponFlags, 5; // weapon can use ammo but does not require it
flagdef Alt_Ammo_Optional: WeaponFlags, 6; // alternate fire can use ammo but does not require it
flagdef Primary_Uses_Both: WeaponFlags, 7; // primary fire uses both ammo
flagdef Alt_Uses_Both: WeaponFlags, 8; // alternate fire uses both ammo
flagdef Wimpy_Weapon:WeaponFlags, 9; // change away when ammo for another weapon is replenished
flagdef Powered_Up: WeaponFlags, 10; // this is a tome-of-power'ed version of its sister
flagdef Ammo_CheckBoth: WeaponFlags, 11; // check for both primary and secondary fire before switching it off
flagdef No_Auto_Switch: WeaponFlags, 12; // never switch to this weapon when it's picked up
flagdef Staff2_Kickback: WeaponFlags, 13; // the powered-up Heretic staff has special kickback
flagdef NoAutoaim: WeaponFlags, 14; // this weapon never uses autoaim (useful for ballistic projectiles)
flagdef MeleeWeapon: WeaponFlags, 15; // melee weapon. Used by monster AI with AVOIDMELEE.
flagdef NoDeathDeselect: WeaponFlags, 16; // Don't jump to the Deselect state when the player dies
flagdef NoDeathInput: WeaponFlags, 17; // The weapon cannot be fired/reloaded/whatever when the player is dead
flagdef CheatNotWeapon: WeaponFlags, 18; // Give cheat considers this not a weapon (used by Sigil)
flagdef NoAutoSwitchTo : WeaponFlags, 19; // do not auto switch to this weapon ever!
// no-op flags
flagdef NoLMS: none, 0;
flagdef Allow_With_Respawn_Invul: none, 0;
flagdef BFG: none, 0;
flagdef Explosive: none, 0;
Default
{
Inventory.PickupSound "misc/w_pkup";
Weapon.DefaultKickback;
Weapon.BobSpeed 1.0;
Weapon.BobRangeX 1.0;
Weapon.BobRangeY 1.0;
Weapon.WeaponScaleX 1.0;
Weapon.WeaponScaleY 1.2;
Weapon.SlotNumber -1;
Weapon.SlotPriority 32767;
Weapon.BobPivot3D (0.0, 0.0, 0.0);
+WEAPONSPAWN
DefaultStateUsage SUF_ACTOR|SUF_OVERLAY|SUF_WEAPON;
}
States
{
LightDone:
SHTG E 0 A_Light0;
Stop;
}
//===========================================================================
//
// Weapon :: MarkPrecacheSounds
//
//===========================================================================
override void MarkPrecacheSounds()
{
Super.MarkPrecacheSounds();
MarkSound(UpSound);
MarkSound(ReadySound);
}
virtual int, int CheckAddToSlots()
{
if (GetReplacement(GetClass()) == GetClass() && !bPowered_Up)
{
return SlotNumber, int(SlotPriority*65536);
}
return -1, 0;
}
virtual State GetReadyState ()
{
return FindState('Ready');
}
virtual State GetUpState ()
{
return FindState('Select');
}
virtual State GetDownState ()
{
return FindState('Deselect');
}
virtual State GetAtkState (bool hold)
{
State s = null;
if (hold) s = FindState('Hold');
if (s == null) s = FindState('Fire');
return s;
}
virtual State GetAltAtkState (bool hold)
{
State s = null;
if (hold) s = FindState('AltHold');
if (s == null) s = FindState('AltFire');
return s;
}
virtual void PlayUpSound(Actor origin)
{
if (UpSound)
{
origin.A_StartSound(UpSound, CHAN_WEAPON);
}
}
override String GetObituary(Actor victim, Actor inflictor, Name mod, bool playerattack)
{
// Weapons may never return HitObituary by default. Override this if it is needed.
return Obituary;
}
action void A_GunFlash(statelabel flashlabel = null, int flags = 0)
{
let player = player;
if (null == player || player.ReadyWeapon == null)
{
return;
}
if (!(flags & GFF_NOEXTCHANGE))
{
player.mo.PlayAttacking2 ();
}
Weapon weapon = player.ReadyWeapon;
state flashstate = null;
if (flashlabel == null)
{
if (weapon.bAltFire)
{
flashstate = weapon.FindState('AltFlash');
}
if (flashstate == null)
{
flashstate = weapon.FindState('Flash');
}
}
else
{
flashstate = weapon.FindState(flashlabel);
}
player.SetPsprite(PSP_FLASH, flashstate);
}
//---------------------------------------------------------------------------
//
// PROC A_Lower
//
//---------------------------------------------------------------------------
action void ResetPSprite(PSprite psp)
{
if (!psp) return;
psp.rotation = 0;
psp.baseScale.x = invoker.WeaponScaleX;
psp.baseScale.y = invoker.WeaponScaleY;
psp.scale.x = 1;
psp.scale.y = 1;
psp.pivot.x = 0;
psp.pivot.y = 0;
psp.valign = 0;
psp.halign = 0;
psp.Coord0 = (0,0);
psp.Coord1 = (0,0);
psp.Coord2 = (0,0);
psp.Coord3 = (0,0);
}
action void A_Lower(int lowerspeed = 6)
{
let player = player;
if (null == player)
{
return;
}
if (null == player.ReadyWeapon)
{
player.mo.BringUpWeapon();
return;
}
let psp = player.GetPSprite(PSP_WEAPON);
if (!psp) return;
if (Alternative || player.cheats & CF_INSTANTWEAPSWITCH)
{
psp.y = WEAPONBOTTOM;
}
else
{
psp.y += lowerspeed;
}
if (psp.y < WEAPONBOTTOM)
{ // Not lowered all the way yet
return;
}
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 ();
return;
}
//---------------------------------------------------------------------------
//
// PROC A_Raise
//
//---------------------------------------------------------------------------
action void A_Raise(int raisespeed = 6)
{
let player = player;
if (null == player)
{
return;
}
if (player.PendingWeapon != WP_NOCHANGE)
{
player.mo.DropWeapon();
return;
}
if (player.ReadyWeapon == null)
{
return;
}
let psp = player.GetPSprite(PSP_WEAPON);
if (!psp) return;
if (psp.y <= WEAPONBOTTOM)
{
ResetPSprite(psp);
}
psp.y -= raisespeed;
if (psp.y > WEAPONTOP)
{ // Not raised all the way yet
return;
}
psp.y = WEAPONTOP;
psp.SetState(player.ReadyWeapon.GetReadyState());
return;
}
//============================================================================
//
// PROC A_WeaponReady
//
// Readies a weapon for firing or bobbing with its three ancillary functions,
// DoReadyWeaponToSwitch(), DoReadyWeaponToFire() and DoReadyWeaponToBob().
// [XA] Added DoReadyWeaponToReload() and DoReadyWeaponToZoom()
//
//============================================================================
static void DoReadyWeaponToSwitch (PlayerInfo player, bool switchable)
{
// Prepare for switching action.
if (switchable)
{
player.WeaponState |= WF_WEAPONSWITCHOK | WF_REFIRESWITCHOK;
}
else
{
// WF_WEAPONSWITCHOK is automatically cleared every tic by P_SetPsprite().
player.WeaponState &= ~WF_REFIRESWITCHOK;
}
}
static void DoReadyWeaponDisableSwitch (PlayerInfo player, int disable)
{
// Discard all switch attempts?
if (disable)
{
player.WeaponState |= WF_DISABLESWITCH;
player.WeaponState &= ~WF_REFIRESWITCHOK;
}
else
{
player.WeaponState &= ~WF_DISABLESWITCH;
}
}
static void DoReadyWeaponToFire (PlayerPawn pawn, bool prim, bool alt)
{
let player = pawn.player;
let weapon = player.ReadyWeapon;
if (!weapon)
{
return;
}
// Change player from attack state
if (pawn.InStateSequence(pawn.curstate, pawn.MissileState) ||
pawn.InStateSequence(pawn.curstate, pawn.MeleeState))
{
pawn.PlayIdle ();
}
// Play ready sound, if any.
let psp = player.GetPSprite(PSP_WEAPON);
if (weapon.ReadySound && psp && psp.curState == weapon.FindState('Ready'))
{
if (!weapon.bReadySndHalf || random[WpnReadySnd]() < 128)
{
pawn.A_StartSound(weapon.ReadySound, CHAN_WEAPON);
}
}
// Prepare for firing action.
player.WeaponState |= ((prim ? WF_WEAPONREADY : 0) | (alt ? WF_WEAPONREADYALT : 0));
return;
}
static void DoReadyWeaponToBob (PlayerInfo player)
{
if (player.ReadyWeapon)
{
// Prepare for bobbing action.
player.WeaponState |= WF_WEAPONBOBBING;
let pspr = player.GetPSprite(PSP_WEAPON);
if (pspr)
{
pspr.x = 0;
pspr.y = WEAPONTOP;
}
}
}
static int GetButtonStateFlags(int flags)
{
// Rewritten for efficiency and clarity
int outflags = 0;
if (flags & WRF_AllowZoom) outflags |= WF_WEAPONZOOMOK;
if (flags & WRF_AllowReload) outflags |= WF_WEAPONRELOADOK;
if (flags & WRF_AllowUser1) outflags |= WF_USER1OK;
if (flags & WRF_AllowUser2) outflags |= WF_USER2OK;
if (flags & WRF_AllowUser3) outflags |= WF_USER3OK;
if (flags & WRF_AllowUser4) outflags |= WF_USER4OK;
return outflags;
}
action void A_WeaponReady(int flags = 0)
{
if (!player) return;
DoReadyWeaponToSwitch(player, !(flags & WRF_NoSwitch));
if ((flags & WRF_NoFire) != WRF_NoFire) DoReadyWeaponToFire(player.mo, !(flags & WRF_NoPrimary), !(flags & WRF_NoSecondary));
if (!(flags & WRF_NoBob)) DoReadyWeaponToBob(player);
player.WeaponState |= GetButtonStateFlags(flags);
DoReadyWeaponDisableSwitch(player, flags & WRF_DisableSwitch);
}
//---------------------------------------------------------------------------
//
// PROC A_CheckReload
//
// Present in Doom, but unused. Also present in Strife, and actually used.
//
//---------------------------------------------------------------------------
action void A_CheckReload()
{
let player = self.player;
if (player != NULL)
{
player.ReadyWeapon.CheckAmmo (player.ReadyWeapon.bAltFire ? Weapon.AltFire : Weapon.PrimaryFire, true);
}
}
//===========================================================================
//
// A_ZoomFactor
//
//===========================================================================
action void A_ZoomFactor(double zoom = 1, int flags = 0)
{
let player = self.player;
if (player != NULL && player.ReadyWeapon != NULL)
{
zoom = 1 / clamp(zoom, 0.1, 50.0);
if (flags & 1)
{ // Make the zoom instant.
player.FOV = player.DesiredFOV * zoom;
player.cheats |= CF_NOFOVINTERP;
}
if (flags & 2)
{ // Disable pitch/yaw scaling.
zoom = -zoom;
}
player.ReadyWeapon.FOVScale = zoom;
}
}
//===========================================================================
//
// A_SetCrosshair
//
//===========================================================================
action void A_SetCrosshair(int xhair)
{
let player = self.player;
if (player != NULL && player.ReadyWeapon != NULL)
{
player.ReadyWeapon.Crosshair = xhair;
}
}
//===========================================================================
//
// Weapon :: TryPickup
//
// If you can't see the weapon when it's active, then you can't pick it up.
//
//===========================================================================
override bool TryPickupRestricted (in out Actor toucher)
{
// Wrong class, but try to pick up for ammo
if (ShouldStay())
{ // Can't pick up weapons for other classes in coop netplay
return false;
}
bool gaveSome = (NULL != AddAmmo (toucher, AmmoType1, AmmoGive1));
gaveSome |= (NULL != AddAmmo (toucher, AmmoType2, AmmoGive2));
if (gaveSome)
{
GoAwayAndDie ();
}
return gaveSome;
}
//===========================================================================
//
// Weapon :: TryPickup
//
//===========================================================================
override bool TryPickup (in out Actor toucher)
{
State ReadyState = FindState('Ready');
if (ReadyState != NULL && ReadyState.ValidateSpriteFrame())
{
return Super.TryPickup (toucher);
}
return false;
}
//===========================================================================
//
// Weapon :: Use
//
// Make the player switch to self weapon.
//
//===========================================================================
override bool Use (bool pickup)
{
Weapon useweap = self;
// Powered up weapons cannot be used directly.
if (bPowered_Up) return false;
// If the player is powered-up, use the alternate version of the
// weapon, if one exists.
if (SisterWeapon != NULL &&
SisterWeapon.bPowered_Up &&
Owner.FindInventory ("PowerWeaponLevel2", true))
{
useweap = SisterWeapon;
}
if (Owner.player != NULL && Owner.player.ReadyWeapon != useweap)
{
Owner.player.PendingWeapon = useweap;
}
// Return false so that the weapon is not removed from the inventory.
return false;
}
//===========================================================================
//
// Weapon :: Destroy
//
//===========================================================================
override void OnDestroy()
{
let sister = SisterWeapon;
if (sister != NULL)
{
// avoid recursion
sister.SisterWeapon = NULL;
if (sister != self)
{ // In case we are our own sister, don't crash.
sister.Destroy();
}
}
Super.OnDestroy();
}
//===========================================================================
//
// Weapon :: HandlePickup
//
// Try to leach ammo from the weapon if you have it already.
//
//===========================================================================
override bool HandlePickup (Inventory item)
{
if (item.GetClass() == GetClass())
{
if (Weapon(item).PickupForAmmo (self))
{
item.bPickupGood = true;
}
if (MaxAmount > 1) //[SP] If amount<maxamount do another pickup test of the weapon itself!
{
return Super.HandlePickup (item);
}
return true;
}
return false;
}
//===========================================================================
//
// Weapon :: PickupForAmmo
//
// The player already has self weapon, so try to pick it up for ammo.
//
//===========================================================================
protected bool PickupForAmmo (Weapon ownedWeapon)
{
bool gotstuff = false;
// Don't take ammo if the weapon sticks around.
if (!ShouldStay ())
{
int oldamount1 = 0;
int oldamount2 = 0;
if (ownedWeapon.Ammo1 != NULL) oldamount1 = ownedWeapon.Ammo1.Amount;
if (ownedWeapon.Ammo2 != NULL) oldamount2 = ownedWeapon.Ammo2.Amount;
if (AmmoGive1 > 0) gotstuff = AddExistingAmmo (ownedWeapon.Ammo1, AmmoGive1);
if (AmmoGive2 > 0) gotstuff |= AddExistingAmmo (ownedWeapon.Ammo2, AmmoGive2);
let Owner = ownedWeapon.Owner;
if (gotstuff && Owner != NULL && Owner.player != NULL)
{
if (ownedWeapon.Ammo1 != NULL && oldamount1 == 0)
{
PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass());
}
else if (ownedWeapon.Ammo2 != NULL && oldamount2 == 0)
{
PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo2.GetClass());
}
}
}
return gotstuff;
}
//===========================================================================
//
// Weapon :: CreateCopy
//
//===========================================================================
override Inventory CreateCopy (Actor other)
{
let copy = Weapon(Super.CreateCopy (other));
if (copy != self && copy != null)
{
copy.AmmoGive1 = AmmoGive1;
copy.AmmoGive2 = AmmoGive2;
}
return copy;
}
//===========================================================================
//
// Weapon :: CreateTossable
//
// A weapon that's tossed out should contain no ammo, so you can't cheat
// by dropping it and then picking it back up.
//
//===========================================================================
override Inventory CreateTossable (int amt)
{
// Only drop the weapon that is meant to be placed in a level. That is,
// only drop the weapon that normally gives you ammo.
if (SisterWeapon != NULL &&
Default.AmmoGive1 == 0 && Default.AmmoGive2 == 0 &&
(SisterWeapon.Default.AmmoGive1 > 0 || SisterWeapon.Default.AmmoGive2 > 0))
{
return SisterWeapon.CreateTossable (amt);
}
let copy = Weapon(Super.CreateTossable (-1));
if (copy != NULL)
{
// If self weapon has a sister, remove it from the inventory too.
if (SisterWeapon != NULL)
{
SisterWeapon.SisterWeapon = NULL;
SisterWeapon.Destroy ();
}
// To avoid exploits, the tossed weapon must not have any ammo.
copy.AmmoGive1 = 0;
copy.AmmoGive2 = 0;
}
return copy;
}
//===========================================================================
//
// Weapon :: AttachToOwner
//
//===========================================================================
override void AttachToOwner (Actor other)
{
Super.AttachToOwner (other);
Ammo1 = AddAmmo (Owner, AmmoType1, AmmoGive1);
Ammo2 = AddAmmo (Owner, AmmoType2, AmmoGive2);
SisterWeapon = AddWeapon (SisterWeaponType);
if (Owner.player != NULL)
{
if (!Owner.player.GetNeverSwitch() && !bNo_Auto_Switch)
{
Owner.player.PendingWeapon = self;
}
if (Owner.player.mo == players[consoleplayer].camera)
{
StatusBar.ReceivedWeapon (self);
}
}
GivenAsMorphWeapon = false; // will be set explicitly by morphing code
}
//===========================================================================
//
// Weapon :: AddAmmo
//
// Give some ammo to the owner, even if it's just 0.
//
//===========================================================================
protected Ammo AddAmmo (Actor other, Class<Ammo> ammotype, int amount)
{
Ammo ammoitem;
if (ammotype == NULL)
{
return NULL;
}
// [BC] This behavior is from the original Doom. Give 5/2 times as much ammoitem when
// we pick up a weapon in deathmatch.
if (( deathmatch && !sv_noextraammo ) && ( gameinfo.gametype & GAME_DoomChex ))
amount = amount * 5 / 2;
// extra ammoitem in baby mode and nightmare mode
if (!bIgnoreSkill)
{
amount = int(amount * (G_SkillPropertyFloat(SKILLP_AmmoFactor) * sv_ammofactor));
}
ammoitem = Ammo(other.FindInventory (ammotype));
if (ammoitem == NULL)
{
ammoitem = Ammo(Spawn (ammotype));
ammoitem.Amount = MIN (amount, ammoitem.MaxAmount);
ammoitem.AttachToOwner (other);
}
else if (ammoitem.Amount < ammoitem.MaxAmount || sv_unlimited_pickup)
{
ammoitem.Amount += amount;
if (ammoitem.Amount > ammoitem.MaxAmount && !sv_unlimited_pickup)
{
ammoitem.Amount = ammoitem.MaxAmount;
}
}
return ammoitem;
}
//===========================================================================
//
// Weapon :: AddExistingAmmo
//
// Give the owner some more ammo he already has.
//
//===========================================================================
protected bool AddExistingAmmo (Inventory ammo, int amount)
{
if (ammo != NULL && (ammo.Amount < ammo.MaxAmount || sv_unlimited_pickup))
{
// extra ammo in baby mode and nightmare mode
if (!bIgnoreSkill)
{
amount = int(amount * (G_SkillPropertyFloat(SKILLP_AmmoFactor) * sv_ammofactor));
}
ammo.Amount += amount;
if (ammo.Amount > ammo.MaxAmount && !sv_unlimited_pickup)
{
ammo.Amount = ammo.MaxAmount;
}
return true;
}
return false;
}
//===========================================================================
//
// Weapon :: AddWeapon
//
// Give the owner a weapon if they don't have it already.
//
//===========================================================================
protected Weapon AddWeapon (Class<Weapon> weapontype)
{
Weapon weap;
if (weapontype == NULL)
{
return NULL;
}
weap = Weapon(Owner.FindInventory (weapontype));
if (weap == NULL)
{
weap = Weapon(Spawn (weapontype));
weap.AttachToOwner (Owner);
}
return weap;
}
//===========================================================================
//
// Weapon :: ShouldStay
//
//===========================================================================
override bool ShouldStay ()
{
if (((multiplayer &&
(!deathmatch && !alwaysapplydmflags)) || sv_weaponstay) &&
!bDropped)
{
return true;
}
return false;
}
//===========================================================================
//
// Weapon :: EndPowerUp
//
// The Tome of Power just expired.
//
//===========================================================================
virtual void EndPowerup ()
{
let player = Owner.player;
if (SisterWeapon != NULL && bPowered_Up)
{
let ready = GetReadyState();
if (ready != SisterWeapon.GetReadyState())
{
if (player.PendingWeapon == NULL || player.PendingWeapon == WP_NOCHANGE)
{
player.refire = 0;
player.ReadyWeapon = SisterWeapon;
player.SetPsprite(PSP_WEAPON, SisterWeapon.GetReadyState());
}
}
else
{
let psp = player.FindPSprite(PSP_WEAPON);
if (psp != null && psp.Caller == player.ReadyWeapon && psp.CurState.InStateSequence(ready))
{
// If the weapon changes but the state does not, we have to manually change the PSprite's caller here.
psp.Caller = SisterWeapon;
player.ReadyWeapon = SisterWeapon;
}
else
{
if (player.PendingWeapon == NULL || player.PendingWeapon == WP_NOCHANGE)
{
// Something went wrong. Initiate a regular weapon change.
player.refire = 0;
player.ReadyWeapon = SisterWeapon;
player.SetPsprite(PSP_WEAPON, SisterWeapon.GetReadyState());
}
}
}
}
}
//===========================================================================
//
// Weapon :: PostMorphWeapon
//
// Bring this weapon up after a player unmorphs.
//
//===========================================================================
void PostMorphWeapon ()
{
if (Owner == null)
{
return;
}
let p = owner.player;
p.PendingWeapon = WP_NOCHANGE;
p.ReadyWeapon = self;
p.refire = 0;
let pspr = p.GetPSprite(PSP_WEAPON);
if (pspr)
{
pspr.y = WEAPONBOTTOM;
pspr.ResetInterpolation();
pspr.SetState(GetUpState());
}
}
//===========================================================================
//
// Weapon :: CheckAmmo
//
// Returns true if there is enough ammo to shoot. If not, selects the
// next weapon to use.
//
//===========================================================================
virtual bool CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo = false, int ammocount = -1)
{
int count1, count2;
int enough, enoughmask;
int lAmmoUse1;
int lAmmoUse2 = AmmoUse2;
if (sv_infiniteammo || (Owner.FindInventory ('PowerInfiniteAmmo', true) != null))
{
return true;
}
if (fireMode == EitherFire)
{
bool gotSome = CheckAmmo (PrimaryFire, false) || CheckAmmo (AltFire, false);
if (!gotSome && autoSwitch)
{
PlayerPawn(Owner).PickNewWeapon (null);
}
return gotSome;
}
let altFire = (fireMode == AltFire);
let optional = (altFire? bAlt_Ammo_Optional : bAmmo_Optional);
let useboth = (altFire? bAlt_Uses_Both : bPrimary_Uses_Both);
if (!requireAmmo && optional)
{
return true;
}
count1 = (Ammo1 != null) ? Ammo1.Amount : 0;
count2 = (Ammo2 != null) ? Ammo2.Amount : 0;
if (bDehAmmo && Ammo1 == null)
{
lAmmoUse1 = 0;
}
else if (ammocount >= 0)
{
lAmmoUse1 = ammocount;
lAmmoUse2 = ammocount;
}
else
{
lAmmoUse1 = AmmoUse1;
}
enough = (count1 >= lAmmoUse1) | ((count2 >= lAmmoUse2) << 1);
if (useboth)
{
enoughmask = 3;
}
else
{
enoughmask = 1 << altFire;
}
if (altFire && FindState('AltFire') == null)
{ // If this weapon has no alternate fire, then there is never enough ammo for it
enough &= 1;
}
if (((enough & enoughmask) == enoughmask) || (enough && bAmmo_CheckBoth))
{
return true;
}
// out of ammo, pick a weapon to change to
if (autoSwitch)
{
PlayerPawn(Owner).PickNewWeapon (null);
}
return false;
}
//===========================================================================
//
// Weapon :: DepleteAmmo
//
// Use up some of the weapon's ammo. Returns true if the ammo was successfully
// depleted. If checkEnough is false, then the ammo will always be depleted,
// even if it drops below zero.
//
//===========================================================================
virtual bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1, bool forceammouse = false)
{
if (!(sv_infiniteammo || (Owner.FindInventory ('PowerInfiniteAmmo', true) != null)))
{
if (checkEnough && !CheckAmmo (altFire ? AltFire : PrimaryFire, false, false, ammouse))
{
return false;
}
if (!altFire)
{
if (Ammo1 != null)
{
if (ammouse >= 0 && (bDehAmmo || forceammouse))
{
Ammo1.Amount -= ammouse;
}
else
{
Ammo1.Amount -= AmmoUse1;
}
}
if (bPRIMARY_USES_BOTH && Ammo2 != null)
{
Ammo2.Amount -= AmmoUse2;
}
}
else
{
if (Ammo2 != null)
{
Ammo2.Amount -= AmmoUse2;
}
if (bALT_USES_BOTH && Ammo1 != null)
{
Ammo1.Amount -= AmmoUse1;
}
}
if (Ammo1 != null && Ammo1.Amount < 0)
Ammo1.Amount = 0;
if (Ammo2 != null && Ammo2.Amount < 0)
Ammo2.Amount = 0;
}
return true;
}
//---------------------------------------------------------------------------
//
// Modifies the drop amount of this item according to the current skill's
// settings (also called by ADehackedPickup::TryPickup)
//
//---------------------------------------------------------------------------
override void ModifyDropAmount(int dropamount)
{
bool ignoreskill = true;
double dropammofactor = G_SkillPropertyFloat(SKILLP_DropAmmoFactor);
// Default drop amount is half of regular amount * regular ammo multiplication
if (dropammofactor == -1)
{
dropammofactor = 0.5;
ignoreskill = false;
}
if (dropamount > 0)
{
self.Amount = dropamount;
}
// Adjust the ammo given by this weapon
AmmoGive1 = int(AmmoGive1 * dropammofactor);
AmmoGive2 = int(AmmoGive2 * dropammofactor);
bIgnoreSkill = ignoreskill;
}
}
class WeaponGiver : Weapon
{
double AmmoFactor;
Default
{
Weapon.AmmoGive1 -1;
Weapon.AmmoGive2 -1;
}
override bool TryPickup(in out Actor toucher)
{
DropItem di = GetDropItems();
Weapon weap;
if (di != NULL)
{
Class<Weapon> ti = di.Name;
if (ti != NULL)
{
if (master == NULL)
{
// save the spawned weapon in 'master' to avoid constant respawning if it cannot be picked up.
master = weap = Weapon(Spawn(di.Name));
if (weap != NULL)
{
weap.bAlwaysPickup = false; // use the flag of self item only.
weap.bDropped = bDropped;
// If our ammo gives are non-negative, transfer them to the real weapon.
if (AmmoGive1 >= 0) weap.AmmoGive1 = AmmoGive1;
if (AmmoGive2 >= 0) weap.AmmoGive2 = AmmoGive2;
// If AmmoFactor is non-negative, modify the given ammo amounts.
if (AmmoFactor > 0)
{
weap.AmmoGive1 = int(weap.AmmoGive1 * AmmoFactor);
weap.AmmoGive2 = int(weap.AmmoGive2 * AmmoFactor);
}
weap.BecomeItem();
}
else return false;
}
weap = Weapon(master);
bool res = false;
if (weap != null)
{
res = weap.CallTryPickup(toucher);
if (res)
{
GoAwayAndDie();
master = NULL;
}
}
return res;
}
}
return false;
}
//---------------------------------------------------------------------------
//
// Modifies the drop amount of this item according to the current skill's
// settings (also called by ADehackedPickup::TryPickup)
//
//---------------------------------------------------------------------------
override void ModifyDropAmount(int dropamount)
{
bool ignoreskill = true;
double dropammofactor = G_SkillPropertyFloat(SKILLP_DropAmmoFactor);
// Default drop amount is half of regular amount * regular ammo multiplication
if (dropammofactor == -1)
{
dropammofactor = 0.5;
ignoreskill = false;
}
AmmoFactor = dropammofactor;
bIgnoreSkill = ignoreskill;
}
}
struct WeaponSlots native
{
native bool, int, int LocateWeapon(class<Weapon> weap) const;
native static void SetupWeaponSlots(PlayerPawn pp);
native class<Weapon> GetWeapon(int slot, int index) const;
native int SlotSize(int slot) const;
}