diff --git a/src/g_inventory/a_armor.cpp b/src/g_inventory/a_armor.cpp index 74ebd52d6..eb90ba000 100644 --- a/src/g_inventory/a_armor.cpp +++ b/src/g_inventory/a_armor.cpp @@ -46,7 +46,6 @@ #include "cmdlib.h" IMPLEMENT_CLASS(AArmor, false, false) -IMPLEMENT_CLASS(AHexenArmor, false, false) //=========================================================================== // @@ -56,178 +55,3 @@ IMPLEMENT_CLASS(AHexenArmor, false, false) // //=========================================================================== -DEFINE_FIELD(AHexenArmor, Slots) -DEFINE_FIELD(AHexenArmor, SlotsIncrement) - -//=========================================================================== -// -// AHexenArmor :: Serialize -// -//=========================================================================== - -void AHexenArmor::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - auto def = (AHexenArmor *)GetDefault(); - arc.Array("slots", Slots, def->Slots, 5, true) - .Array("slotsincrement", SlotsIncrement, def->SlotsIncrement, 4); -} - -//=========================================================================== -// -// AHexenArmor :: CreateCopy -// -//=========================================================================== - -AInventory *AHexenArmor::CreateCopy (AActor *other) -{ - // Like BasicArmor, HexenArmor is used in the inventory but not the map. - // health is the slot this armor occupies. - // Amount is the quantity to give (0 = normal max). - AHexenArmor *copy = Spawn (); - copy->AddArmorToSlot (other, health, Amount); - GoAwayAndDie (); - return copy; -} - -//=========================================================================== -// -// AHexenArmor :: CreateTossable -// -// Since this isn't really a single item, you can't drop it. Ever. -// -//=========================================================================== - -AInventory *AHexenArmor::CreateTossable () -{ - return NULL; -} - -//=========================================================================== -// -// AHexenArmor :: HandlePickup -// -//=========================================================================== - -bool AHexenArmor::HandlePickup (AInventory *item) -{ - if (item->IsKindOf (RUNTIME_CLASS(AHexenArmor))) - { - if (AddArmorToSlot (Owner, item->health, item->Amount)) - { - item->ItemFlags |= IF_PICKUPGOOD; - } - return true; - } - return false; -} - -//=========================================================================== -// -// AHexenArmor :: AddArmorToSlot -// -//=========================================================================== - -bool AHexenArmor::AddArmorToSlot (AActor *actor, int slot, int amount) -{ - APlayerPawn *ppawn; - double hits; - - if (actor->player != NULL) - { - ppawn = static_cast(actor); - } - else - { - ppawn = NULL; - } - - if (slot < 0 || slot > 3) - { - return false; - } - if (amount <= 0) - { - hits = SlotsIncrement[slot]; - if (Slots[slot] < hits) - { - Slots[slot] = hits; - return true; - } - } - else - { - hits = amount * 5; - auto total = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; - auto max = SlotsIncrement[0] + SlotsIncrement[1] + SlotsIncrement[2] + SlotsIncrement[3] + Slots[4] + 4 * 5; - if (total < max) - { - Slots[slot] += hits; - return true; - } - } - return false; -} - -//=========================================================================== -// -// AHexenArmor :: AbsorbDamage -// -//=========================================================================== - -void AHexenArmor::AbsorbDamage (int damage, FName damageType, int &newdamage) -{ - if (!DamageTypeDefinition::IgnoreArmor(damageType)) - { - double savedPercent = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; - - if (savedPercent) - { // armor absorbed some damage - if (savedPercent > 100) - { - savedPercent = 100; - } - for (int i = 0; i < 4; i++) - { - if (Slots[i]) - { - // 300 damage always wipes out the armor unless some was added - // with the dragon skin bracers. - if (damage < 10000) - { - Slots[i] -= damage * SlotsIncrement[i] / 300.; - if (Slots[i] < 2) - { - Slots[i] = 0; - } - } - else - { - Slots[i] = 0; - } - } - } - int saved = int(damage * savedPercent / 100.); - if (saved > savedPercent*2) - { - saved = int(savedPercent*2); - } - newdamage -= saved; - damage = newdamage; - } - } -} - -//=========================================================================== -// -// AHexenArmor :: DepleteOrDestroy -// -//=========================================================================== - -void AHexenArmor::DepleteOrDestroy() -{ - for (int i = 0; i < 4; i++) - { - Slots[i] = 0; - } -} \ No newline at end of file diff --git a/src/g_inventory/a_armor.h b/src/g_inventory/a_armor.h index d208306a1..7ed8f27ee 100644 --- a/src/g_inventory/a_armor.h +++ b/src/g_inventory/a_armor.h @@ -8,24 +8,3 @@ class AArmor : public AInventory DECLARE_CLASS (AArmor, AInventory) }; -// Hexen armor consists of four separate armor types plus a conceptual armor -// type (the player himself) that work together as a single armor. -class AHexenArmor : public AArmor -{ - DECLARE_CLASS (AHexenArmor, AArmor) -public: - - virtual void Serialize(FSerializer &arc) override; - virtual AInventory *CreateCopy (AActor *other) override; - virtual AInventory *CreateTossable () override; - virtual bool HandlePickup (AInventory *item) override; - virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override; - virtual void DepleteOrDestroy() override; - - double Slots[5]; - double SlotsIncrement[4]; - -protected: - bool AddArmorToSlot (AActor *actor, int slot, int amount); -}; - diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 93500adc7..2a7f0b3bf 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -1051,19 +1051,10 @@ void AInventory::OnDestroy () 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) + IFVIRTUAL(AInventory, DepleteOrDestroy) { - Amount = 0; - } - else - { - Destroy(); + VMValue params[1] = { (DObject*)this }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); } } diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 3ef2fd285..4ad0e66b8 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -82,7 +82,7 @@ public: // virtual methods that only get overridden by special internal classes, like DehackedPickup. // There is no need to expose these to scripts. - virtual void DepleteOrDestroy (); + void DepleteOrDestroy (); virtual bool ShouldRespawn (); virtual void DoPickupSpecial (AActor *toucher); diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index a1db0a15a..e58165f35 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -364,10 +364,11 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } pmo->Destroy (); // Restore playerclass armor to its normal amount. - AHexenArmor *hxarmor = mo->FindInventory(); + auto hxarmor = mo->FindInventory(NAME_HexenArmor); if (hxarmor != nullptr) { - hxarmor->Slots[4] = mo->GetClass()->HexenArmor[0]; + double *Slots = (double*)hxarmor->ScriptVar(NAME_Slots, nullptr); + Slots[4] = mo->GetClass()->HexenArmor[0]; } return true; } diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 6bbbadc54..8cb90a350 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -276,13 +276,15 @@ class CommandDrawImage : public SBarInfoCommandFlowControl { int armorType = type - HEXENARMOR_ARMOR; - AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); + auto harmor = statusBar->CPlayer->mo->FindInventory(NAME_HexenArmor); if (harmor != NULL) { - if (harmor->Slots[armorType] > 0 && harmor->SlotsIncrement[armorType] > 0) + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); + double *SlotsIncrement = (double*)harmor->ScriptVar(NAME_SlotsIncrement, nullptr); + if (Slots[armorType] > 0 && SlotsIncrement[armorType] > 0) { //combine the alpha values - alpha *= MIN(1., harmor->Slots[armorType] / harmor->SlotsIncrement[armorType]); + alpha *= MIN(1., Slots[armorType] / SlotsIncrement[armorType]); texture = statusBar->Images[image]; } else @@ -1409,11 +1411,11 @@ class CommandDrawNumber : public CommandDrawString case SAVEPERCENT: { double add = 0; - AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); + auto harmor = statusBar->CPlayer->mo->FindInventory(NAME_HexenArmor); if(harmor != NULL) { - add = harmor->Slots[0] + harmor->Slots[1] + - harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); + add = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; } //Hexen counts basic armor also so we should too. if(statusBar->armor != NULL) @@ -2842,12 +2844,13 @@ class CommandDrawBar : public SBarInfoCommand case SAVEPERCENT: { double add = 0; - AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory(); - if(harmor != NULL) + auto harmor = statusBar->CPlayer->mo->FindInventory(NAME_HexenArmor); + if (harmor != NULL) { - add = harmor->Slots[0] + harmor->Slots[1] + - harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); + add = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; } + //Hexen counts basic armor also so we should too. if(statusBar->armor != NULL) { diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index b6ee15602..34590aa6e 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -310,14 +310,15 @@ static void DrawHealth(player_t *CPlayer, int x, int y) // //=========================================================================== -static void DrawArmor(AInventory * barmor, AHexenArmor * harmor, int x, int y) +static void DrawArmor(AInventory * barmor, AInventory * harmor, int x, int y) { int ap = 0; int bestslot = 4; if (harmor) { - auto ac = (harmor->Slots[0] + harmor->Slots[1] + harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]); + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); + auto ac = (Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]); ap += int(ac); if (ac) @@ -326,7 +327,7 @@ static void DrawArmor(AInventory * barmor, AHexenArmor * harmor, int x, int y) bestslot = 0; for (int i = 1; i < 4; ++i) { - if (harmor->Slots[i] > harmor->Slots[bestslot]) + if (Slots[i] > Slots[bestslot]) { bestslot = i; } @@ -1141,8 +1142,7 @@ void DrawHUD() DrawFrags(CPlayer, 5, hudheight-70); } DrawHealth(CPlayer, 5, hudheight-45); - DrawArmor(CPlayer->mo->FindInventory(NAME_BasicArmor), - CPlayer->mo->FindInventory(), 5, hudheight-20); + DrawArmor(CPlayer->mo->FindInventory(NAME_BasicArmor), CPlayer->mo->FindInventory(NAME_HexenArmor), 5, hudheight-20); i=DrawKeys(CPlayer, hudwidth-4, hudheight-10); i=DrawAmmo(CPlayer, hudwidth-5, i); if (hud_showweapons) DrawWeapons(CPlayer, hudwidth - 5, i); diff --git a/src/namedef.h b/src/namedef.h index ff117df45..b730e93aa 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -92,6 +92,9 @@ xx(MaxFullAbsorb) xx(MaxAmount) xx(ActualSaveAmount) xx(ArmorType) +xx(HexenArmor) +xx(Slots) +xx(SlotsIncrement) xx(BulletPuff) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f45332b86..5c9440deb 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -894,9 +894,6 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate 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 && diff --git a/src/p_user.cpp b/src/p_user.cpp index 322353704..9e72c502e 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -1153,12 +1153,11 @@ void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer) item->IntVar(NAME_SavePercent) = defitem->IntVar(NAME_SavePercent); item->Amount = defitem->Amount; } - else if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor))) + else if (item->IsKindOf(PClass::FindActor(NAME_HexenArmor))) { - static_cast(item)->Slots[0] = static_cast(defitem)->Slots[0]; - static_cast(item)->Slots[1] = static_cast(defitem)->Slots[1]; - static_cast(item)->Slots[2] = static_cast(defitem)->Slots[2]; - static_cast(item)->Slots[3] = static_cast(defitem)->Slots[3]; + double *SlotsTo = (double*)item->ScriptVar(NAME_Slots, nullptr); + double *SlotsFrom = (double*)defitem->ScriptVar(NAME_Slots, nullptr); + memcpy(SlotsTo, SlotsFrom, 4 * sizeof(double)); } } else if ((dmflags & DF_COOP_LOSE_POWERUPS) && @@ -1364,12 +1363,15 @@ void APlayerPawn::GiveDefaultInventory () // it provides player class based protection that should not affect // any other protection item. PClassPlayerPawn *myclass = GetClass(); - GiveInventoryType(RUNTIME_CLASS(AHexenArmor)); - AHexenArmor *harmor = FindInventory(); - harmor->Slots[4] = myclass->HexenArmor[0]; + GiveInventoryType(PClass::FindActor(NAME_HexenArmor)); + auto harmor = FindInventory(NAME_HexenArmor); + + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); + double *SlotsIncrement = (double*)harmor->ScriptVar(NAME_SlotsIncrement, nullptr); + Slots[4] = myclass->HexenArmor[0]; for (int i = 0; i < 4; ++i) { - harmor->SlotsIncrement[i] = myclass->HexenArmor[i + 1]; + SlotsIncrement[i] = myclass->HexenArmor[i + 1]; } // BasicArmor must come right after that. It should not affect any diff --git a/wadsrc/static/zscript/inventory/armor.txt b/wadsrc/static/zscript/inventory/armor.txt index 195de5f55..477363edf 100644 --- a/wadsrc/static/zscript/inventory/armor.txt +++ b/wadsrc/static/zscript/inventory/armor.txt @@ -452,16 +452,165 @@ class BasicArmorPickup : Armor // //=========================================================================== -class HexenArmor : Armor native +class HexenArmor : Armor { - native double Slots[5]; - native double SlotsIncrement[4]; + double Slots[5]; + double SlotsIncrement[4]; Default { +Inventory.KEEPDEPLETED +Inventory.UNTOSSABLE } + + //=========================================================================== + // + // AHexenArmor :: CreateCopy + // + //=========================================================================== + + override Inventory CreateCopy (Actor other) + { + // Like BasicArmor, HexenArmor is used in the inventory but not the map. + // health is the slot this armor occupies. + // Amount is the quantity to give (0 = normal max). + let copy = HexenArmor(Spawn("HexenArmor")); + copy.AddArmorToSlot (health, Amount); + GoAwayAndDie (); + return copy; + } + + //=========================================================================== + // + // AHexenArmor :: CreateTossable + // + // Since this isn't really a single item, you can't drop it. Ever. + // + //=========================================================================== + + override Inventory CreateTossable () + { + return NULL; + } + + //=========================================================================== + // + // AHexenArmor :: HandlePickup + // + //=========================================================================== + + override bool HandlePickup (Inventory item) + { + if (item is "HexenArmor") + { + if (AddArmorToSlot (item.health, item.Amount)) + { + item.bPickupGood = true; + } + return true; + } + return false; + } + + //=========================================================================== + // + // AHexenArmor :: AddArmorToSlot + // + //=========================================================================== + + protected bool AddArmorToSlot (int slot, int amount) + { + double hits; + + if (slot < 0 || slot > 3) + { + return false; + } + if (amount <= 0) + { + hits = SlotsIncrement[slot]; + if (Slots[slot] < hits) + { + Slots[slot] = hits; + return true; + } + } + else + { + hits = amount * 5; + let total = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; + let max = SlotsIncrement[0] + SlotsIncrement[1] + SlotsIncrement[2] + SlotsIncrement[3] + Slots[4] + 4 * 5; + if (total < max) + { + Slots[slot] += hits; + return true; + } + } + return false; + } + + //=========================================================================== + // + // AHexenArmor :: AbsorbDamage + // + //=========================================================================== + + override void AbsorbDamage (int damage, Name damageType, out int newdamage) + { + if (!DamageTypeDefinition.IgnoreArmor(damageType)) + { + double savedPercent = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]; + + if (savedPercent) + { // armor absorbed some damage + if (savedPercent > 100) + { + savedPercent = 100; + } + for (int i = 0; i < 4; i++) + { + if (Slots[i]) + { + // 300 damage always wipes out the armor unless some was added + // with the dragon skin bracers. + if (damage < 10000) + { + Slots[i] -= damage * SlotsIncrement[i] / 300.; + if (Slots[i] < 2) + { + Slots[i] = 0; + } + } + else + { + Slots[i] = 0; + } + } + } + int saved = int(damage * savedPercent / 100.); + if (saved > savedPercent*2) + { + saved = int(savedPercent*2); + } + newdamage -= saved; + damage = newdamage; + } + } + } + + //=========================================================================== + // + // AHexenArmor :: DepleteOrDestroy + // + //=========================================================================== + + override void DepleteOrDestroy() + { + for (int i = 0; i < 4; i++) + { + Slots[i] = 0; + } + } } diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index fbd4ed088..2b0d6d0c7 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -138,6 +138,32 @@ class Inventory : Actor native } } + //=========================================================================== + // + // AInventory :: DepleteOrDestroy + // + // If the item is depleted, just change its amount to 0, otherwise it's destroyed. + // + //=========================================================================== + + virtual void 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 (bKeepDepleted) + { + Amount = 0; + } + else + { + Destroy(); + } + } + //=========================================================================== // // AInventory :: Travelled