- scriptified HexenArmor.

This commit is contained in:
Christoph Oelckers 2017-01-18 23:42:08 +01:00
parent 2fcffd1fc1
commit 632a29e365
12 changed files with 217 additions and 242 deletions

View file

@ -46,7 +46,6 @@
#include "cmdlib.h" #include "cmdlib.h"
IMPLEMENT_CLASS(AArmor, false, false) 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<AHexenArmor> ();
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<APlayerPawn *>(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;
}
}

View file

@ -8,24 +8,3 @@ class AArmor : public AInventory
DECLARE_CLASS (AArmor, 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);
};

View file

@ -1051,19 +1051,10 @@ void AInventory::OnDestroy ()
void AInventory::DepleteOrDestroy () void AInventory::DepleteOrDestroy ()
{ {
// If it's not ammo or an internal armor, destroy it. IFVIRTUAL(AInventory, DepleteOrDestroy)
// 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; VMValue params[1] = { (DObject*)this };
} GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
else
{
Destroy();
} }
} }

View file

@ -82,7 +82,7 @@ public:
// virtual methods that only get overridden by special internal classes, like DehackedPickup. // virtual methods that only get overridden by special internal classes, like DehackedPickup.
// There is no need to expose these to scripts. // There is no need to expose these to scripts.
virtual void DepleteOrDestroy (); void DepleteOrDestroy ();
virtual bool ShouldRespawn (); virtual bool ShouldRespawn ();
virtual void DoPickupSpecial (AActor *toucher); virtual void DoPickupSpecial (AActor *toucher);

View file

@ -364,10 +364,11 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
} }
pmo->Destroy (); pmo->Destroy ();
// Restore playerclass armor to its normal amount. // Restore playerclass armor to its normal amount.
AHexenArmor *hxarmor = mo->FindInventory<AHexenArmor>(); auto hxarmor = mo->FindInventory(NAME_HexenArmor);
if (hxarmor != nullptr) 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; return true;
} }

View file

@ -276,13 +276,15 @@ class CommandDrawImage : public SBarInfoCommandFlowControl
{ {
int armorType = type - HEXENARMOR_ARMOR; int armorType = type - HEXENARMOR_ARMOR;
AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory<AHexenArmor>(); auto harmor = statusBar->CPlayer->mo->FindInventory(NAME_HexenArmor);
if (harmor != NULL) 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 //combine the alpha values
alpha *= MIN(1., harmor->Slots[armorType] / harmor->SlotsIncrement[armorType]); alpha *= MIN(1., Slots[armorType] / SlotsIncrement[armorType]);
texture = statusBar->Images[image]; texture = statusBar->Images[image];
} }
else else
@ -1409,11 +1411,11 @@ class CommandDrawNumber : public CommandDrawString
case SAVEPERCENT: case SAVEPERCENT:
{ {
double add = 0; double add = 0;
AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory<AHexenArmor>(); auto harmor = statusBar->CPlayer->mo->FindInventory(NAME_HexenArmor);
if(harmor != NULL) if(harmor != NULL)
{ {
add = harmor->Slots[0] + harmor->Slots[1] + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr);
harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; add = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4];
} }
//Hexen counts basic armor also so we should too. //Hexen counts basic armor also so we should too.
if(statusBar->armor != NULL) if(statusBar->armor != NULL)
@ -2842,12 +2844,13 @@ class CommandDrawBar : public SBarInfoCommand
case SAVEPERCENT: case SAVEPERCENT:
{ {
double add = 0; double add = 0;
AHexenArmor *harmor = statusBar->CPlayer->mo->FindInventory<AHexenArmor>(); auto harmor = statusBar->CPlayer->mo->FindInventory(NAME_HexenArmor);
if(harmor != NULL) if (harmor != NULL)
{ {
add = harmor->Slots[0] + harmor->Slots[1] + double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr);
harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4]; add = Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4];
} }
//Hexen counts basic armor also so we should too. //Hexen counts basic armor also so we should too.
if(statusBar->armor != NULL) if(statusBar->armor != NULL)
{ {

View file

@ -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 ap = 0;
int bestslot = 4; int bestslot = 4;
if (harmor) 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); ap += int(ac);
if (ac) if (ac)
@ -326,7 +327,7 @@ static void DrawArmor(AInventory * barmor, AHexenArmor * harmor, int x, int y)
bestslot = 0; bestslot = 0;
for (int i = 1; i < 4; ++i) for (int i = 1; i < 4; ++i)
{ {
if (harmor->Slots[i] > harmor->Slots[bestslot]) if (Slots[i] > Slots[bestslot])
{ {
bestslot = i; bestslot = i;
} }
@ -1141,8 +1142,7 @@ void DrawHUD()
DrawFrags(CPlayer, 5, hudheight-70); DrawFrags(CPlayer, 5, hudheight-70);
} }
DrawHealth(CPlayer, 5, hudheight-45); DrawHealth(CPlayer, 5, hudheight-45);
DrawArmor(CPlayer->mo->FindInventory(NAME_BasicArmor), DrawArmor(CPlayer->mo->FindInventory(NAME_BasicArmor), CPlayer->mo->FindInventory(NAME_HexenArmor), 5, hudheight-20);
CPlayer->mo->FindInventory<AHexenArmor>(), 5, hudheight-20);
i=DrawKeys(CPlayer, hudwidth-4, hudheight-10); i=DrawKeys(CPlayer, hudwidth-4, hudheight-10);
i=DrawAmmo(CPlayer, hudwidth-5, i); i=DrawAmmo(CPlayer, hudwidth-5, i);
if (hud_showweapons) DrawWeapons(CPlayer, hudwidth - 5, i); if (hud_showweapons) DrawWeapons(CPlayer, hudwidth - 5, i);

View file

@ -92,6 +92,9 @@ xx(MaxFullAbsorb)
xx(MaxAmount) xx(MaxAmount)
xx(ActualSaveAmount) xx(ActualSaveAmount)
xx(ArmorType) xx(ArmorType)
xx(HexenArmor)
xx(Slots)
xx(SlotsIncrement)
xx(BulletPuff) xx(BulletPuff)

View file

@ -894,9 +894,6 @@ bool AActor::TakeInventory(PClassActor *itemclass, int amount, bool fromdecorate
result = true; 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 // Do not take ammo if the "no take infinite/take as ammo depletion" flag is set
// and infinite ammo is on // and infinite ammo is on
if (notakeinfinite && if (notakeinfinite &&

View file

@ -1153,12 +1153,11 @@ void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer)
item->IntVar(NAME_SavePercent) = defitem->IntVar(NAME_SavePercent); item->IntVar(NAME_SavePercent) = defitem->IntVar(NAME_SavePercent);
item->Amount = defitem->Amount; item->Amount = defitem->Amount;
} }
else if (item->IsKindOf(RUNTIME_CLASS(AHexenArmor))) else if (item->IsKindOf(PClass::FindActor(NAME_HexenArmor)))
{ {
static_cast<AHexenArmor*>(item)->Slots[0] = static_cast<AHexenArmor*>(defitem)->Slots[0]; double *SlotsTo = (double*)item->ScriptVar(NAME_Slots, nullptr);
static_cast<AHexenArmor*>(item)->Slots[1] = static_cast<AHexenArmor*>(defitem)->Slots[1]; double *SlotsFrom = (double*)defitem->ScriptVar(NAME_Slots, nullptr);
static_cast<AHexenArmor*>(item)->Slots[2] = static_cast<AHexenArmor*>(defitem)->Slots[2]; memcpy(SlotsTo, SlotsFrom, 4 * sizeof(double));
static_cast<AHexenArmor*>(item)->Slots[3] = static_cast<AHexenArmor*>(defitem)->Slots[3];
} }
} }
else if ((dmflags & DF_COOP_LOSE_POWERUPS) && else if ((dmflags & DF_COOP_LOSE_POWERUPS) &&
@ -1364,12 +1363,15 @@ void APlayerPawn::GiveDefaultInventory ()
// it provides player class based protection that should not affect // it provides player class based protection that should not affect
// any other protection item. // any other protection item.
PClassPlayerPawn *myclass = GetClass(); PClassPlayerPawn *myclass = GetClass();
GiveInventoryType(RUNTIME_CLASS(AHexenArmor)); GiveInventoryType(PClass::FindActor(NAME_HexenArmor));
AHexenArmor *harmor = FindInventory<AHexenArmor>(); auto harmor = FindInventory(NAME_HexenArmor);
harmor->Slots[4] = myclass->HexenArmor[0];
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) 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 // BasicArmor must come right after that. It should not affect any

View file

@ -452,16 +452,165 @@ class BasicArmorPickup : Armor
// //
//=========================================================================== //===========================================================================
class HexenArmor : Armor native class HexenArmor : Armor
{ {
native double Slots[5]; double Slots[5];
native double SlotsIncrement[4]; double SlotsIncrement[4];
Default Default
{ {
+Inventory.KEEPDEPLETED +Inventory.KEEPDEPLETED
+Inventory.UNTOSSABLE +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;
}
}
} }

View file

@ -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 // AInventory :: Travelled