gzdoom/wadsrc/static/zscript/shared/player_inventory.txt
Christoph Oelckers 6c168d88e4 - fixed crash in AutoUseStrifeHealth
The loop never checked if the item was still valid and would continue to try to use it, even after it was removed from the inventory and destroyed.
As native code this just failed silently, but with the VM it needs to be explicitly checked.
2019-01-01 13:58:25 +01:00

324 lines
No EOL
7.4 KiB
Text

struct AutoUseHealthInfo play
{
Array<Inventory> collectedItems[2];
int collectedHealth[2];
void AddItemToList(Inventory item, int list)
{
collectedItems[list].Push(item);
collectedHealth[list] += Item.Amount * Item.health;
}
int UseHealthItems(int list, in out int saveHealth)
{
int saved = 0;
while (collectedItems[list].Size() > 0 && saveHealth > 0)
{
int maxhealth = 0;
int index = -1;
// Find the largest item in the list
for(int i = 0; i < collectedItems[list].Size(); i++)
{
// Workaround for a deficiency in the expression resolver.
let item = list == 0? collectedItems[0][i] : collectedItems[1][i];
if (Item.health > maxhealth)
{
index = i;
maxhealth = Item.health;
}
}
// Now apply the health items, using the same logic as Heretic and Hexen.
int count = (saveHealth + maxhealth-1) / maxhealth;
for(int i = 0; i < count; i++)
{
saved += maxhealth;
saveHealth -= maxhealth;
let item = list == 0? collectedItems[0][index] : collectedItems[1][index];
if (--item.Amount == 0)
{
item.DepleteOrDestroy ();
collectedItems[list].Delete(index);
break;
}
}
}
return saved;
}
}
extend class PlayerPawn
{
//===========================================================================
//
//
//
//===========================================================================
ui void InvNext()
{
Inventory next;
let old = InvSel;
if (InvSel != NULL)
{
if ((next = InvSel.NextInv()) != NULL)
{
InvSel = next;
}
else
{
// Select the first item in the inventory
InvSel = FirstInv();
}
if (InvSel) InvSel.DisplayNameTag();
}
player.inventorytics = 5*TICRATE;
if (old != InvSel)
{
A_PlaySound("misc/invchange", CHAN_AUTO, 1.0, false, ATTN_NONE);
}
}
//===========================================================================
//
// APlayerPawn :: InvPrev
//
//===========================================================================
ui void InvPrev()
{
Inventory item, newitem;
let old = InvSel;
if (InvSel != NULL)
{
if ((item = InvSel.PrevInv()) != NULL)
{
InvSel = item;
}
else
{
// Select the last item in the inventory
item = InvSel;
while ((newitem = item.NextInv()) != NULL)
{
item = newitem;
}
InvSel = item;
}
if (InvSel) InvSel.DisplayNameTag();
}
player.inventorytics = 5*TICRATE;
if (old != InvSel)
{
A_PlaySound("misc/invchange", CHAN_AUTO, 1.0, false, ATTN_NONE);
}
}
//===========================================================================
//
// APlayerPawn :: AddInventory
//
//===========================================================================
override void AddInventory (Inventory item)
{
// Adding inventory to a voodoo doll should add it to the real player instead.
if (player != NULL && player.mo != self && player.mo != NULL)
{
player.mo.AddInventory (item);
return;
}
Super.AddInventory (item);
// If nothing is selected, select this item.
if (InvSel == NULL && item.bInvBar)
{
InvSel = item;
}
}
//===========================================================================
//
// APlayerPawn :: RemoveInventory
//
//===========================================================================
override void RemoveInventory (Inventory item)
{
bool pickWeap = false;
// Since voodoo dolls aren't supposed to have an inventory, there should be
// no need to redirect them to the real player here as there is with AddInventory.
// If the item removed is the selected one, select something else, either the next
// item, if there is one, or the previous item.
if (player != NULL)
{
if (InvSel == item)
{
InvSel = item.NextInv ();
if (InvSel == NULL)
{
InvSel = item.PrevInv ();
}
}
if (InvFirst == item)
{
InvFirst = item.NextInv ();
if (InvFirst == NULL)
{
InvFirst = item.PrevInv ();
}
}
if (item == player.PendingWeapon)
{
player.PendingWeapon = WP_NOCHANGE;
}
if (item == player.ReadyWeapon)
{
// If the current weapon is removed, clear the refire counter and pick a new one.
pickWeap = true;
player.ReadyWeapon = NULL;
player.refire = 0;
}
}
Super.RemoveInventory (item);
if (pickWeap && player.mo == self && player.PendingWeapon == WP_NOCHANGE)
{
PickNewWeapon (NULL);
}
}
//===========================================================================
//
// APlayerPawn :: UseInventory
//
//===========================================================================
override bool UseInventory (Inventory item)
{
let itemtype = item.GetClass();
if (player.cheats & CF_TOTALLYFROZEN)
{ // You can't use items if you're totally frozen
return false;
}
if ((level.FROZEN) && (player == NULL || player.timefreezer == 0))
{
// Time frozen
return false;
}
if (!Super.UseInventory (item))
{
// Heretic and Hexen advance the inventory cursor if the use failed.
// Should this behavior be retained?
return false;
}
if (player == players[consoleplayer])
{
A_PlaySound(item.UseSound, CHAN_ITEM);
StatusBar.FlashItem (itemtype); // Fixme: This shouldn't be called from here, because it is in the UI.
}
return true;
}
//---------------------------------------------------------------------------
//
// PROC P_AutoUseHealth
//
//---------------------------------------------------------------------------
void AutoUseHealth(int saveHealth)
{
AutoUseHealthInfo collector;
for(Inventory inv = self.Inv; inv != NULL; inv = inv.Inv)
{
let hp = HealthPickup(inv);
if (hp && hp.Amount > 0)
{
int mode = hp.autousemode;
if (mode == 1 || mode == 2) collector.AddItemToList(inv, mode-1);
}
}
bool skilluse = !!G_SkillPropertyInt(SKILLP_AutoUseHealth);
if (skilluse && collector.collectedHealth[0] >= saveHealth)
{
// Use quartz flasks
player.health += collector.UseHealthItems(0, saveHealth);
}
else if (collector.collectedHealth[1] >= saveHealth)
{
// Use mystic urns
player.health += collector.UseHealthItems(1, saveHealth);
}
else if (skilluse && collector.collectedHealth[0] + collector.collectedHealth[1] >= saveHealth)
{
// Use mystic urns and quartz flasks
player.health += collector.UseHealthItems(0, saveHealth);
if (saveHealth > 0) player.health += collector.UseHealthItems(1, saveHealth);
}
health = player.health;
}
//============================================================================
//
// P_AutoUseStrifeHealth
//
//============================================================================
void AutoUseStrifeHealth ()
{
Array<Inventory> Items;
for(Inventory inv = self.Inv; inv != NULL; inv = inv.Inv)
{
let hp = HealthPickup(inv);
if (hp && hp.Amount > 0)
{
if (hp.autousemode == 3) Items.Push(inv);
}
}
if (!sv_disableautohealth)
{
while (Items.Size() > 0)
{
int maxhealth = 0;
int index = -1;
// Find the largest item in the list
for(int i = 0; i < Items.Size(); i++)
{
if (Items[i].health > maxhealth)
{
index = i;
maxhealth = Items[i].Amount;
}
}
while (player.health < 50)
{
let item = Items[index];
if (item == null || !UseInventory (item))
break;
}
if (player.health >= 50) return;
// Using all of this item was not enough so delete it and restart with the next best one
Items.Delete(index);
}
}
}
}