diff --git a/src/actor.h b/src/actor.h index 862858b32a..59201b6859 100644 --- a/src/actor.h +++ b/src/actor.h @@ -677,6 +677,11 @@ public: // Removes the item from the inventory list. virtual void RemoveInventory (AInventory *item); + // Take the amount value of an item from the inventory list. + // If nothing is left, the item may be destroyed. + // Returns true if the initial item count is positive. + virtual bool TakeInventory (const PClass *itemclass, int amount, bool fromdecorate = false, bool notakeinfinite = false); + // Uses an item and removes it from the inventory. virtual bool UseInventory (AInventory *item); diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index e3433f12ef..ceffe907a2 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -137,14 +137,9 @@ bool P_MorphPlayer (player_t *activator, player_t *p, const PClass *spawntype, i hxarmor->Slots[3] = 0; hxarmor->Slots[4] = spawntype->Meta.GetMetaFixed (APMETA_Hexenarmor0); } - else if (item->ItemFlags & IF_KEEPDEPLETED) - { - // Set depletable armor to 0 (this includes BasicArmor). - item->Amount = 0; - } else { - item->Destroy (); + item->DepleteOrDestroy(); } } item = next; diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 5eda53ad69..038b630290 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -1131,6 +1131,32 @@ void AInventory::Destroy () if (SendItemDrop == this) SendItemDrop = NULL; } +//=========================================================================== +// +// AInventory :: DepleteOrDestroy +// +// If the item is depleted, just change its amount to 0, otherwise it's destroyed. +// +//=========================================================================== + +void AInventory::DepleteOrDestroy () +{ + // If it's not ammo or an internal armor, destroy it. + // Ammo needs to stick around, even when it's zero for the benefit + // of the weapons that use it and to maintain the maximum ammo + // amounts a backpack might have given. + // Armor shouldn't be removed because they only work properly when + // they are the last items in the inventory. + if (ItemFlags & IF_KEEPDEPLETED) + { + Amount = 0; + } + else + { + Destroy(); + } +} + //=========================================================================== // // AInventory :: GetBlend diff --git a/src/g_shared/a_pickups.h b/src/g_shared/a_pickups.h index df0c94b460..504a26c73e 100644 --- a/src/g_shared/a_pickups.h +++ b/src/g_shared/a_pickups.h @@ -151,6 +151,7 @@ public: virtual void MarkPrecacheSounds() const; virtual void BeginPlay (); virtual void Destroy (); + virtual void DepleteOrDestroy (); virtual void Tick (); virtual bool ShouldRespawn (); virtual bool ShouldStay (); diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index f60131c8e5..73ed823761 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -1052,24 +1052,7 @@ void cht_Take (player_t *player, const char *name, int amount) } else { - AInventory *inventory = player->mo->FindInventory (type); - - if (inventory != NULL) - { - inventory->Amount -= amount ? amount : 1; - - if (inventory->Amount <= 0) - { - if (inventory->ItemFlags & IF_KEEPDEPLETED) - { - inventory->Amount = 0; - } - else - { - inventory->Destroy (); - } - } - } + player->mo->TakeInventory(type, amount ? amount : 1); } return; } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 753d7728fe..7a8a8bade2 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -1131,40 +1131,6 @@ static void GiveInventory (AActor *activator, const char *type, int amount) } } -//============================================================================ -// -// DoTakeInv -// -// Takes an item from a single actor. -// -//============================================================================ - -static void DoTakeInv (AActor *actor, const PClass *info, int amount) -{ - AInventory *item = actor->FindInventory (info); - if (item != NULL) - { - item->Amount -= amount; - if (item->Amount <= 0) - { - // If it's not ammo or an internal armor, destroy it. - // Ammo needs to stick around, even when it's zero for the benefit - // of the weapons that use it and to maintain the maximum ammo - // amounts a backpack might have given. - // Armor shouldn't be removed because they only work properly when - // they are the last items in the inventory. - if (item->ItemFlags & IF_KEEPDEPLETED) - { - item->Amount = 0; - } - else - { - item->Destroy (); - } - } - } -} - //============================================================================ // // TakeInventory @@ -1199,12 +1165,12 @@ static void TakeInventory (AActor *activator, const char *type, int amount) for (int i = 0; i < MAXPLAYERS; ++i) { if (playeringame[i]) - DoTakeInv (players[i].mo, info, amount); + players[i].mo->TakeInventory(info, amount); } } else { - DoTakeInv (activator, info, amount); + activator->TakeInventory(info, amount); } } diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 4fd6f2fa60..069fca6e12 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -649,22 +649,7 @@ static void TakeStrifeItem (player_t *player, const PClass *itemtype, int amount if (itemtype == RUNTIME_CLASS(ASigil)) return; - AInventory *item = player->mo->FindInventory (itemtype); - if (item != NULL) - { - item->Amount -= amount; - if (item->Amount <= 0) - { - if (item->ItemFlags & IF_KEEPDEPLETED) - { - item->Amount = 0; - } - else - { - item->Destroy (); - } - } - } + player->mo->TakeInventory(itemtype, amount); } CUSTOM_CVAR(Float, dlg_musicvolume, 1.0f, CVAR_ARCHIVE) diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index 1e9dbf9b6e..36f50a8978 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -804,10 +804,7 @@ static int UseHealthItems(TArray &Items, int &saveHealth) saveHealth -= maxhealth; if (--Items[index]->Amount == 0) { - if (!(Items[index]->ItemFlags & IF_KEEPDEPLETED)) - { - Items[index]->Destroy (); - } + Items[index]->DepleteOrDestroy (); Items.Delete(index); break; } diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index e2aa677649..3afc89b110 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -591,6 +591,57 @@ void AActor::RemoveInventory(AInventory *item) } } +//============================================================================ +// +// AActor :: TakeInventory +// +//============================================================================ + +bool AActor::TakeInventory(const PClass *itemclass, int amount, bool fromdecorate, bool notakeinfinite) +{ + AInventory *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; + } + + if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor))) + return false; + + // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set + // and infinite ammo is on + if (notakeinfinite && + ((dmflags & DF_INFINITE_AMMO) || (player && player->cheats & CF_INFINITEAMMO)) && + item->IsKindOf(RUNTIME_CLASS(AAmmo))) + { + // Nothing to do here, except maybe res = false;? Would it make sense? + } + else if (!amount || amount>=item->Amount) + { + item->DepleteOrDestroy(); + } + else item->Amount-=amount; + + return result; +} + + //============================================================================ // // AActor :: DestroyAllInventory @@ -658,9 +709,9 @@ bool AActor::UseInventory (AInventory *item) if (dmflags2 & DF2_INFINITE_INVENTORY) return true; - if (--item->Amount <= 0 && !(item->ItemFlags & IF_KEEPDEPLETED)) + if (--item->Amount <= 0) { - item->Destroy (); + item->DepleteOrDestroy (); } return true; } diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index b4c6a219a9..b1da8f546c 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -1783,31 +1783,7 @@ void DoTakeInventory(AActor * receiver, bool use_aaptr, DECLARE_PARAMINFO) COPY_AAPTR_NOT_NULL(receiver, receiver, setreceiver); } - bool res = false; - - AInventory * inv = receiver->FindInventory(item); - - if (inv && !inv->IsKindOf(RUNTIME_CLASS(AHexenArmor))) - { - if (inv->Amount > 0) - { - res = true; - } - // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set - // and infinite ammo is on - if (flags & TIF_NOTAKEINFINITE && - ((dmflags & DF_INFINITE_AMMO) || (receiver->player->cheats & CF_INFINITEAMMO)) && - inv->IsKindOf(RUNTIME_CLASS(AAmmo))) - { - // Nothing to do here, except maybe res = false;? Would it make sense? - } - else if (!amount || amount>=inv->Amount) - { - if (inv->ItemFlags&IF_KEEPDEPLETED) inv->Amount=0; - else inv->Destroy(); - } - else inv->Amount-=amount; - } + bool res = receiver->TakeInventory(item, amount, true, (flags & TIF_NOTAKEINFINITE) != 0); ACTION_SET_RESULT(res); }