mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-14 08:31:23 +00:00
712 lines
No EOL
18 KiB
Text
712 lines
No EOL
18 KiB
Text
class Weapon : StateProvider native
|
|
{
|
|
enum EFireMode
|
|
{
|
|
PrimaryFire,
|
|
AltFire,
|
|
EitherFire
|
|
};
|
|
|
|
const ZOOM_INSTANT = 1;
|
|
const ZOOM_NOSCALETURNING = 2;
|
|
|
|
native uint WeaponFlags;
|
|
native class<Ammo> AmmoType1, AmmoType2; // Types of ammo used by self weapon
|
|
native int AmmoGive1, AmmoGive2; // Amount of each ammo to get when picking up weapon
|
|
native int MinAmmo1, MinAmmo2; // Minimum ammo needed to switch to self weapon
|
|
native int AmmoUse1, AmmoUse2; // How much ammo to use with each shot
|
|
native int Kickback;
|
|
native float YAdjust; // For viewing the weapon fullscreen (visual only so no need to be a double)
|
|
native sound UpSound, ReadySound; // Sounds when coming up and idle
|
|
native class<Weapon> SisterWeaponType; // Another weapon to pick up with self one
|
|
native class<Actor> ProjectileType; // Projectile used by primary attack
|
|
native class<Actor> AltProjectileType; // Projectile used by alternate attack
|
|
native int SelectionOrder; // Lower-numbered weapons get picked first
|
|
native int MinSelAmmo1, MinSelAmmo2; // Ignore in BestWeapon() if inadequate ammo
|
|
native double MoveCombatDist; // Used by bots, but do they *really* need it?
|
|
native int ReloadCounter; // For A_CheckForReload
|
|
native int BobStyle; // [XA] Bobbing style. Defines type of bobbing (e.g. Normal, Alpha) (visual only so no need to be a double)
|
|
native float BobSpeed; // [XA] Bobbing speed. Defines how quickly a weapon bobs.
|
|
native float BobRangeX, BobRangeY; // [XA] Bobbing range. Defines how far a weapon bobs in either direction.
|
|
native Ammo Ammo1, Ammo2; // In-inventory instance variables
|
|
native Weapon SisterWeapon;
|
|
native float FOVScale;
|
|
native int Crosshair; // 0 to use player's crosshair
|
|
native bool GivenAsMorphWeapon;
|
|
native bool bAltFire; // Set when self weapon's alternate fire is used.
|
|
native readonly bool bDehAmmo;
|
|
|
|
Default
|
|
{
|
|
Inventory.PickupSound "misc/w_pkup";
|
|
Weapon.DefaultKickback;
|
|
Weapon.BobSpeed 1.0;
|
|
Weapon.BobRangeX 1.0;
|
|
Weapon.BobRangeY 1.0;
|
|
Weapon.SlotNumber -1;
|
|
Weapon.SlotPriority 32767;
|
|
+WEAPONSPAWN
|
|
DefaultStateUsage SUF_ACTOR|SUF_OVERLAY|SUF_WEAPON;
|
|
}
|
|
States
|
|
{
|
|
LightDone:
|
|
SHTG E 0 A_Light0;
|
|
Stop;
|
|
}
|
|
|
|
native bool CheckAmmo(int fireMode, bool autoSwitch, bool requireAmmo = false, int ammocount = -1);
|
|
native bool DepleteAmmo(bool altFire, bool checkEnough = true, int ammouse = -1);
|
|
|
|
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;
|
|
}
|
|
|
|
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 ();
|
|
}
|
|
if (flashlabel == null)
|
|
{
|
|
if (player.ReadyWeapon.bAltFire)
|
|
{
|
|
flashlabel = 'AltFlash';
|
|
}
|
|
if (flashlabel == null)
|
|
{
|
|
flashlabel = 'Flash';
|
|
}
|
|
}
|
|
player.SetPsprite(PSP_FLASH, player.ReadyWeapon.FindState(flashlabel));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// PROC A_Lower
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
action void A_Lower(int lowerspeed = 6)
|
|
{
|
|
let player = player;
|
|
|
|
if (null == player)
|
|
{
|
|
return;
|
|
}
|
|
if (null == player.ReadyWeapon)
|
|
{
|
|
player.BringUpWeapon();
|
|
return;
|
|
}
|
|
let psp = player.GetPSprite(PSP_WEAPON);
|
|
if (player.morphTics || player.cheats & CF_INSTANTWEAPSWITCH)
|
|
{
|
|
psp.y = WEAPONBOTTOM;
|
|
}
|
|
else
|
|
{
|
|
psp.y += lowerspeed;
|
|
}
|
|
if (psp.y < WEAPONBOTTOM)
|
|
{ // Not lowered all the way yet
|
|
return;
|
|
}
|
|
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.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.DropWeapon();
|
|
return;
|
|
}
|
|
if (player.ReadyWeapon == null)
|
|
{
|
|
return;
|
|
}
|
|
let psp = player.GetPSprite(PSP_WEAPON);
|
|
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_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.
|
|
self.player.FOV = self.player.DesiredFOV * zoom;
|
|
}
|
|
if (flags & 2)
|
|
{ // Disable pitch/yaw scaling.
|
|
zoom = -zoom;
|
|
}
|
|
self.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 ) && ( 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));
|
|
}
|
|
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)
|
|
{
|
|
ammoitem.Amount += amount;
|
|
if (ammoitem.Amount > ammoitem.MaxAmount)
|
|
{
|
|
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));
|
|
}
|
|
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)
|
|
{
|
|
if (GetReadyState() != SisterWeapon.GetReadyState())
|
|
{
|
|
if (player.PendingWeapon == NULL || player.PendingWeapon == WP_NOCHANGE)
|
|
player.PendingWeapon = SisterWeapon;
|
|
}
|
|
else
|
|
{
|
|
let psp = player.FindPSprite(PSP_WEAPON);
|
|
if (psp != null && psp.Caller == player.ReadyWeapon)
|
|
{
|
|
// 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
|
|
{
|
|
// Something went wrong. Initiate a regular weapon change.
|
|
player.PendingWeapon = SisterWeapon;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
struct WeaponSlots native
|
|
{
|
|
native bool, int, int LocateWeapon(class<Weapon> weap);
|
|
} |