gzdoom-gles/wadsrc/static/zscript/actor_inventory.txt

595 lines
14 KiB
Plaintext

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 :: ClearInventory
//
// Clears the inventory of a single actor.
//
//============================================================================
virtual void ClearInventory()
{
// In case destroying an inventory item causes another to be destroyed
// (e.g. Weapons destroy their sisters), keep track of the pointer to
// the next inventory item rather than the next inventory item itself.
// For example, if a weapon is immediately followed by its sister, the
// next weapon we had tracked would be to the sister, so it is now
// invalid and we won't be able to find the complete inventory by
// following it.
//
// When we destroy an item, we leave last alone, since the destruction
// process will leave it pointing to the next item we want to check. If
// we don't destroy an item, then we move last to point to its Inventory
// pointer.
//
// It should be safe to assume that an item being destroyed will only
// destroy items further down in the chain, because if it was going to
// destroy something we already processed, we've already destroyed it,
// so it won't have anything to destroy.
let last = self;
while (last.inv != NULL)
{
let inv = last.inv;
if (!inv.bUndroppable)
{
inv.DepleteOrDestroy();
if (!inv.bDestroyed) last = inv; // was only depleted so advance the pointer manually.
}
else
{
last = inv;
}
}
if (player != null)
{
player.ReadyWeapon = null;
player.PendingWeapon = WP_NOCHANGE;
}
}
//============================================================================
//
// 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);
}
}