mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-14 08:31:23 +00:00
543 lines
12 KiB
Text
543 lines
12 KiB
Text
extend class Actor
|
|
{
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: FirstInv
|
|
//
|
|
// Returns the first item in this actor's inventory that has IF_INVBAR set.
|
|
//
|
|
//============================================================================
|
|
|
|
clearscope Inventory FirstInv ()
|
|
{
|
|
if (Inv == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
if (Inv.bInvBar)
|
|
{
|
|
return Inv;
|
|
}
|
|
return Inv.NextInv ();
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: AddInventory
|
|
//
|
|
//============================================================================
|
|
|
|
virtual void AddInventory (Inventory item)
|
|
{
|
|
// Check if it's already attached to an actor
|
|
if (item.Owner != NULL)
|
|
{
|
|
// Is it attached to us?
|
|
if (item.Owner == self)
|
|
return;
|
|
|
|
// No, then remove it from the other actor first
|
|
item.Owner.RemoveInventory (item);
|
|
}
|
|
|
|
item.Owner = self;
|
|
item.Inv = Inv;
|
|
Inv = item;
|
|
|
|
// Each item receives an unique ID when added to an actor's inventory.
|
|
// This is used by the DEM_INVUSE command to identify the item. Simply
|
|
// using the item's position in the list won't work, because ticcmds get
|
|
// run sometime in the future, so by the time it runs, the inventory
|
|
// might not be in the same state as it was when DEM_INVUSE was sent.
|
|
Inv.InventoryID = InventoryID++;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: GiveInventory
|
|
//
|
|
//============================================================================
|
|
|
|
bool GiveInventory(Class<Inventory> type, int amount, bool givecheat = false)
|
|
{
|
|
bool result = true;
|
|
let player = self.player;
|
|
|
|
// This can be called from places which do not check the given item's type.
|
|
if (type == null || !(type is 'Inventory')) return false;
|
|
|
|
Weapon savedPendingWeap = player != NULL ? player.PendingWeapon : NULL;
|
|
bool hadweap = player != NULL ? player.ReadyWeapon != NULL : true;
|
|
|
|
Inventory item;
|
|
if (!givecheat)
|
|
{
|
|
item = Inventory(Spawn (type));
|
|
}
|
|
else
|
|
{
|
|
item = Inventory(Spawn (type, Pos, NO_REPLACE));
|
|
if (item == NULL) return false;
|
|
}
|
|
|
|
// This shouldn't count for the item statistics.
|
|
item.ClearCounters();
|
|
if (!givecheat || amount > 0)
|
|
{
|
|
item.SetGiveAmount(self, amount, givecheat);
|
|
}
|
|
if (!item.CallTryPickup (self))
|
|
{
|
|
item.Destroy ();
|
|
result = false;
|
|
}
|
|
// If the item was a weapon, don't bring it up automatically
|
|
// unless the player was not already using a weapon.
|
|
// Don't bring it up automatically if this is called by the give cheat.
|
|
if (!givecheat && player != NULL && savedPendingWeap != NULL && hadweap)
|
|
{
|
|
player.PendingWeapon = savedPendingWeap;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: RemoveInventory
|
|
//
|
|
//============================================================================
|
|
|
|
virtual void RemoveInventory(Inventory item)
|
|
{
|
|
Inventory invp;
|
|
|
|
if (item != NULL && item.Owner != NULL) // can happen if the owner was destroyed by some action from an item's use state.
|
|
{
|
|
if (Inv == item) Inv = item.Inv;
|
|
else
|
|
{
|
|
for (invp = Inv; invp != null; invp = invp.Inv)
|
|
{
|
|
if (invp.Inv == item)
|
|
{
|
|
invp.Inv = item.Inv;
|
|
item.DetachFromOwner();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
item.Owner = NULL;
|
|
item.Inv = NULL;
|
|
}
|
|
}
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: TakeInventory
|
|
//
|
|
//============================================================================
|
|
|
|
bool TakeInventory(class<Inventory> itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false)
|
|
{
|
|
amount = abs(amount);
|
|
let item = FindInventory(itemclass);
|
|
|
|
if (item == NULL)
|
|
return false;
|
|
|
|
if (!fromdecorate)
|
|
{
|
|
item.Amount -= amount;
|
|
if (item.Amount <= 0)
|
|
{
|
|
item.DepleteOrDestroy();
|
|
}
|
|
// It won't be used in non-decorate context, so return false here
|
|
return false;
|
|
}
|
|
|
|
bool result = false;
|
|
if (item.Amount > 0)
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
// Do not take ammo if the "no take infinite/take as ammo depletion" flag is set
|
|
// and infinite ammo is on
|
|
if (notakeinfinite &&
|
|
(sv_infiniteammo || (player && FindInventory('PowerInfiniteAmmo', true))) && (item is 'Ammo'))
|
|
{
|
|
// Nothing to do here, except maybe res = false;? Would it make sense?
|
|
result = false;
|
|
}
|
|
else if (!amount || amount >= item.Amount)
|
|
{
|
|
item.DepleteOrDestroy();
|
|
}
|
|
else item.Amount -= amount;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: SetInventory
|
|
//
|
|
//============================================================================
|
|
|
|
bool SetInventory(class<Inventory> itemclass, int amount, bool beyondMax = false)
|
|
{
|
|
let item = FindInventory(itemclass);
|
|
|
|
if (item != null)
|
|
{
|
|
// A_SetInventory sets the absolute amount.
|
|
// Subtract or set the appropriate amount as necessary.
|
|
|
|
if (amount == item.Amount)
|
|
{
|
|
// Nothing was changed.
|
|
return false;
|
|
}
|
|
else if (amount <= 0)
|
|
{
|
|
//Remove it all.
|
|
return TakeInventory(itemclass, item.Amount, true, false);
|
|
}
|
|
else if (amount < item.Amount)
|
|
{
|
|
int amt = abs(item.Amount - amount);
|
|
return TakeInventory(itemclass, amt, true, false);
|
|
}
|
|
else
|
|
{
|
|
item.Amount = (beyondMax ? amount : clamp(amount, 0, item.MaxAmount));
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (amount <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
item = Inventory(Spawn(itemclass));
|
|
if (item == null)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
item.Amount = amount;
|
|
item.bDropped = true;
|
|
item.bIgnoreSkill = true;
|
|
item.ClearCounters();
|
|
if (!item.CallTryPickup(self))
|
|
{
|
|
item.Destroy();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: UseInventory
|
|
//
|
|
// Attempts to use an item. If the use succeeds, one copy of the item is
|
|
// removed from the inventory. If all copies are removed, then the item is
|
|
// destroyed.
|
|
//
|
|
//============================================================================
|
|
|
|
virtual bool UseInventory (Inventory item)
|
|
{
|
|
// No using items if you're dead or you don't have them.
|
|
if (health <= 0 || item.Amount <= 0 || item.bDestroyed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!item.Use(false))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (sv_infiniteinventory)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (--item.Amount <= 0)
|
|
{
|
|
item.DepleteOrDestroy ();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// AActor :: DropInventory
|
|
//
|
|
// Removes a single copy of an item and throws it out in front of the actor.
|
|
//
|
|
//===========================================================================
|
|
|
|
Inventory DropInventory (Inventory item, int amt = 1)
|
|
{
|
|
Inventory drop = item.CreateTossable(amt);
|
|
if (drop == null) return NULL;
|
|
drop.SetOrigin(Pos + (0, 0, 10.), false);
|
|
drop.Angle = Angle;
|
|
drop.VelFromAngle(5.);
|
|
drop.Vel.Z = 1.;
|
|
drop.Vel += Vel;
|
|
drop.bNoGravity = false; // Don't float
|
|
drop.ClearCounters(); // do not count for statistics again
|
|
drop.OnDrop(self);
|
|
return drop;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
//
|
|
// AActor :: GiveAmmo
|
|
//
|
|
// Returns true if the ammo was added, false if not.
|
|
//
|
|
//============================================================================
|
|
|
|
bool GiveAmmo (Class<Ammo> type, int amount)
|
|
{
|
|
if (type != NULL && type is 'Ammo')
|
|
{
|
|
let item = Inventory(Spawn (type));
|
|
if (item)
|
|
{
|
|
item.Amount = amount;
|
|
item.bDropped = true;
|
|
if (!item.CallTryPickup (self))
|
|
{
|
|
item.Destroy ();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// DoGiveInventory
|
|
//
|
|
//===========================================================================
|
|
|
|
static bool DoGiveInventory(Actor receiver, bool orresult, class<Inventory> mi, int amount, int setreceiver)
|
|
{
|
|
int paramnum = 0;
|
|
|
|
if (!orresult)
|
|
{
|
|
receiver = receiver.GetPointer(setreceiver);
|
|
}
|
|
if (receiver == NULL)
|
|
{ // If there's nothing to receive it, it's obviously a fail, right?
|
|
return false;
|
|
}
|
|
// Owned inventory items cannot own anything because their Inventory pointer is repurposed for the owner's linked list.
|
|
if (receiver is 'Inventory' && Inventory(receiver).Owner != null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (amount <= 0)
|
|
{
|
|
amount = 1;
|
|
}
|
|
if (mi)
|
|
{
|
|
let item = Inventory(Spawn(mi));
|
|
if (item == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (item is 'Health')
|
|
{
|
|
item.Amount *= amount;
|
|
}
|
|
else
|
|
{
|
|
item.Amount = amount;
|
|
}
|
|
item.bDropped = true;
|
|
item.ClearCounters();
|
|
if (!item.CallTryPickup(receiver))
|
|
{
|
|
item.Destroy();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool A_GiveInventory(class<Inventory> itemtype, int amount = 0, int giveto = AAPTR_DEFAULT)
|
|
{
|
|
return DoGiveInventory(self, false, itemtype, amount, giveto);
|
|
}
|
|
|
|
bool A_GiveToTarget(class<Inventory> itemtype, int amount = 0, int giveto = AAPTR_DEFAULT)
|
|
{
|
|
return DoGiveInventory(target, false, itemtype, amount, giveto);
|
|
}
|
|
|
|
int A_GiveToChildren(class<Inventory> itemtype, int amount = 0)
|
|
{
|
|
let it = ThinkerIterator.Create('Actor');
|
|
Actor mo;
|
|
int count = 0;
|
|
|
|
while ((mo = Actor(it.Next())))
|
|
{
|
|
if (mo.master == self)
|
|
{
|
|
count += DoGiveInventory(mo, true, itemtype, amount, AAPTR_DEFAULT);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int A_GiveToSiblings(class<Inventory> itemtype, int amount = 0)
|
|
{
|
|
let it = ThinkerIterator.Create('Actor');
|
|
Actor mo;
|
|
int count = 0;
|
|
|
|
if (self.master != NULL)
|
|
{
|
|
while ((mo = Actor(it.Next())))
|
|
{
|
|
if (mo.master == self.master && mo != self)
|
|
{
|
|
count += DoGiveInventory(mo, true, itemtype, amount, AAPTR_DEFAULT);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// A_TakeInventory
|
|
//
|
|
//===========================================================================
|
|
|
|
bool DoTakeInventory(Actor receiver, bool orresult, class<Inventory> itemtype, int amount, int flags, int setreceiver = AAPTR_DEFAULT)
|
|
{
|
|
int paramnum = 0;
|
|
|
|
if (itemtype == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (!orresult)
|
|
{
|
|
receiver = receiver.GetPointer(setreceiver);
|
|
}
|
|
if (receiver == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return receiver.TakeInventory(itemtype, amount, true, (flags & TIF_NOTAKEINFINITE) != 0);
|
|
}
|
|
|
|
bool A_TakeInventory(class<Inventory> itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT)
|
|
{
|
|
return DoTakeInventory(self, false, itemtype, amount, flags, giveto);
|
|
}
|
|
|
|
bool A_TakeFromTarget(class<Inventory> itemtype, int amount = 0, int flags = 0, int giveto = AAPTR_DEFAULT)
|
|
{
|
|
return DoTakeInventory(target, false, itemtype, amount, flags, giveto);
|
|
}
|
|
|
|
int A_TakeFromChildren(class<Inventory> itemtype, int amount = 0)
|
|
{
|
|
let it = ThinkerIterator.Create('Actor');
|
|
Actor mo;
|
|
int count = 0;
|
|
|
|
while ((mo = Actor(it.Next())))
|
|
{
|
|
if (mo.master == self)
|
|
{
|
|
count += DoTakeInventory(mo, true, itemtype, amount, 0, AAPTR_DEFAULT);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int A_TakeFromSiblings(class<Inventory> itemtype, int amount = 0)
|
|
{
|
|
let it = ThinkerIterator.Create('Actor');
|
|
Actor mo;
|
|
int count = 0;
|
|
|
|
if (self.master != NULL)
|
|
{
|
|
while ((mo = Actor(it.Next())))
|
|
{
|
|
if (mo.master == self.master && mo != self)
|
|
{
|
|
count += DoTakeInventory(mo, true, itemtype, amount, 0, AAPTR_DEFAULT);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// A_SetInventory
|
|
//
|
|
//===========================================================================
|
|
|
|
bool A_SetInventory(class<Inventory> itemtype, int amount, int ptr = AAPTR_DEFAULT, bool beyondMax = false)
|
|
{
|
|
bool res = false;
|
|
|
|
if (itemtype == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Actor mobj = GetPointer(ptr);
|
|
|
|
if (mobj == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Do not run this function on voodoo dolls because the way they transfer the inventory to the player will not work with the code below.
|
|
if (mobj.player != null)
|
|
{
|
|
mobj = mobj.player.mo;
|
|
}
|
|
return mobj.SetInventory(itemtype, amount, beyondMax);
|
|
}
|
|
|
|
|
|
}
|
|
|