No need to maintain these clunky meta class for one single property. The overhead the mere existence of this class creates is far more than 100 spawned ammo items would cost. There is no need to serialize AAmmo::DropAmount, this value has no meaning on an already spawned item.
365 lines
11 KiB
C++
365 lines
11 KiB
C++
/*
|
|
** a_ammo.cpp
|
|
** Implements ammo and backpack items.
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 2000-2016 Randy Heit
|
|
** Copyright 2006-2016 Cheistoph Oelckers
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include "c_dispatch.h"
|
|
#include "d_player.h"
|
|
#include "serializer.h"
|
|
|
|
IMPLEMENT_CLASS(AAmmo, false, false)
|
|
|
|
DEFINE_FIELD(AAmmo, BackpackAmount)
|
|
DEFINE_FIELD(AAmmo, BackpackMaxAmount)
|
|
|
|
//===========================================================================
|
|
//
|
|
// AAmmo :: Serialize
|
|
//
|
|
//===========================================================================
|
|
|
|
void AAmmo::Serialize(FSerializer &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
auto def = (AAmmo*)GetDefault();
|
|
arc("backpackamount", BackpackAmount, def->BackpackAmount)
|
|
("backpackmaxamount", BackpackMaxAmount, def->BackpackMaxAmount);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// AAmmo :: GetParentAmmo
|
|
//
|
|
// Returns the least-derived ammo type that this ammo is a descendant of.
|
|
// That is, if this ammo is an immediate subclass of Ammo, then this ammo's
|
|
// type is returned. If this ammo's superclass is not Ammo, then this
|
|
// function travels up the inheritance chain until it finds a type that is
|
|
// an immediate subclass of Ammo and returns that.
|
|
//
|
|
// The intent of this is that all unique ammo types will be immediate
|
|
// subclasses of Ammo. To make different pickups with different ammo amounts,
|
|
// you subclass the type of ammo you want a different amount for and edit
|
|
// that.
|
|
//
|
|
//===========================================================================
|
|
|
|
PClassActor *AAmmo::GetParentAmmo () const
|
|
{
|
|
PClass *type = GetClass();
|
|
|
|
while (type->ParentClass != RUNTIME_CLASS(AAmmo) && type->ParentClass != NULL)
|
|
{
|
|
type = type->ParentClass;
|
|
}
|
|
return static_cast<PClassActor *>(type);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// AAmmo :: HandlePickup
|
|
//
|
|
//===========================================================================
|
|
EXTERN_CVAR(Bool, sv_unlimited_pickup)
|
|
|
|
bool AAmmo::HandlePickup (AInventory *item)
|
|
{
|
|
if (GetClass() == item->GetClass() ||
|
|
(item->IsKindOf (RUNTIME_CLASS(AAmmo)) && static_cast<AAmmo*>(item)->GetParentAmmo() == GetClass()))
|
|
{
|
|
if (Amount < MaxAmount || sv_unlimited_pickup)
|
|
{
|
|
int receiving = item->Amount;
|
|
|
|
if (!(item->ItemFlags & IF_IGNORESKILL))
|
|
{ // extra ammo in baby mode and nightmare mode
|
|
receiving = int(receiving * G_SkillProperty(SKILLP_AmmoFactor));
|
|
}
|
|
int oldamount = Amount;
|
|
|
|
if (Amount > 0 && Amount + receiving < 0)
|
|
{
|
|
Amount = 0x7fffffff;
|
|
}
|
|
else
|
|
{
|
|
Amount += receiving;
|
|
}
|
|
if (Amount > MaxAmount && !sv_unlimited_pickup)
|
|
{
|
|
Amount = MaxAmount;
|
|
}
|
|
item->ItemFlags |= IF_PICKUPGOOD;
|
|
|
|
// If the player previously had this ammo but ran out, possibly switch
|
|
// to a weapon that uses it, but only if the player doesn't already
|
|
// have a weapon pending.
|
|
|
|
assert (Owner != NULL);
|
|
|
|
if (oldamount == 0 && Owner != NULL && Owner->player != NULL)
|
|
{
|
|
barrier_cast<APlayerPawn *>(Owner)->CheckWeaponSwitch(GetClass());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// AAmmo :: CreateCopy
|
|
//
|
|
//===========================================================================
|
|
|
|
AInventory *AAmmo::CreateCopy (AActor *other)
|
|
{
|
|
AInventory *copy;
|
|
int amount = Amount;
|
|
|
|
// extra ammo in baby mode and nightmare mode
|
|
if (!(ItemFlags&IF_IGNORESKILL))
|
|
{
|
|
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
|
|
}
|
|
|
|
if (GetClass()->ParentClass != RUNTIME_CLASS(AAmmo) && GetClass() != RUNTIME_CLASS(AAmmo))
|
|
{
|
|
PClassActor *type = GetParentAmmo();
|
|
if (!GoAway ())
|
|
{
|
|
Destroy ();
|
|
}
|
|
|
|
copy = static_cast<AInventory *>(Spawn (type));
|
|
copy->Amount = amount;
|
|
copy->BecomeItem ();
|
|
}
|
|
else
|
|
{
|
|
copy = Super::CreateCopy (other);
|
|
copy->Amount = amount;
|
|
}
|
|
if (copy->Amount > copy->MaxAmount)
|
|
{ // Don't pick up more ammo than you're supposed to be able to carry.
|
|
copy->Amount = copy->MaxAmount;
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// AAmmo :: CreateTossable
|
|
//
|
|
//===========================================================================
|
|
|
|
AInventory *AAmmo::CreateTossable()
|
|
{
|
|
AInventory *copy = Super::CreateTossable();
|
|
if (copy != NULL)
|
|
{ // Do not increase ammo by dropping it and picking it back up at
|
|
// certain skill levels.
|
|
copy->ItemFlags |= IF_IGNORESKILL;
|
|
}
|
|
return copy;
|
|
}
|
|
|
|
|
|
// Backpack -----------------------------------------------------------------
|
|
|
|
IMPLEMENT_CLASS(ABackpackItem, false, false)
|
|
|
|
DEFINE_FIELD(ABackpackItem, bDepleted)
|
|
|
|
//===========================================================================
|
|
//
|
|
// ABackpackItem :: Serialize
|
|
//
|
|
//===========================================================================
|
|
|
|
void ABackpackItem::Serialize(FSerializer &arc)
|
|
{
|
|
Super::Serialize (arc);
|
|
auto def = (ABackpackItem*)GetDefault();
|
|
arc("bdepleted", bDepleted, def->bDepleted);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// ABackpackItem :: CreateCopy
|
|
//
|
|
// A backpack is being added to a player who doesn't yet have one. Give them
|
|
// every kind of ammo, and increase their max amounts.
|
|
//
|
|
//===========================================================================
|
|
|
|
AInventory *ABackpackItem::CreateCopy (AActor *other)
|
|
{
|
|
// Find every unique type of ammo. Give it to the player if
|
|
// he doesn't have it already, and double its maximum capacity.
|
|
for (unsigned int i = 0; i < PClassActor::AllActorClasses.Size(); ++i)
|
|
{
|
|
PClass *type = PClassActor::AllActorClasses[i];
|
|
|
|
if (type->ParentClass == RUNTIME_CLASS(AAmmo))
|
|
{
|
|
PClassActor *atype = static_cast<PClassActor *>(type);
|
|
AAmmo *ammo = static_cast<AAmmo *>(other->FindInventory(atype));
|
|
int amount = static_cast<AAmmo *>(GetDefaultByType(type))->BackpackAmount;
|
|
// extra ammo in baby mode and nightmare mode
|
|
if (!(ItemFlags&IF_IGNORESKILL))
|
|
{
|
|
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
|
|
}
|
|
if (amount < 0) amount = 0;
|
|
if (ammo == NULL)
|
|
{ // The player did not have the ammo. Add it.
|
|
ammo = static_cast<AAmmo *>(Spawn(atype));
|
|
ammo->Amount = bDepleted ? 0 : amount;
|
|
if (ammo->BackpackMaxAmount > ammo->MaxAmount)
|
|
{
|
|
ammo->MaxAmount = ammo->BackpackMaxAmount;
|
|
}
|
|
if (ammo->Amount > ammo->MaxAmount)
|
|
{
|
|
ammo->Amount = ammo->MaxAmount;
|
|
}
|
|
ammo->AttachToOwner (other);
|
|
}
|
|
else
|
|
{ // The player had the ammo. Give some more.
|
|
if (ammo->MaxAmount < ammo->BackpackMaxAmount)
|
|
{
|
|
ammo->MaxAmount = ammo->BackpackMaxAmount;
|
|
}
|
|
if (!bDepleted && ammo->Amount < ammo->MaxAmount)
|
|
{
|
|
ammo->Amount += amount;
|
|
if (ammo->Amount > ammo->MaxAmount)
|
|
{
|
|
ammo->Amount = ammo->MaxAmount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Super::CreateCopy (other);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// ABackpackItem :: HandlePickup
|
|
//
|
|
// When the player picks up another backpack, just give them more ammo.
|
|
//
|
|
//===========================================================================
|
|
|
|
bool ABackpackItem::HandlePickup (AInventory *item)
|
|
{
|
|
// Since you already have a backpack, that means you already have every
|
|
// kind of ammo in your inventory, so we don't need to look at the
|
|
// entire PClass list to discover what kinds of ammo exist, and we don't
|
|
// have to alter the MaxAmount either.
|
|
if (item->IsKindOf (RUNTIME_CLASS(ABackpackItem)))
|
|
{
|
|
for (AInventory *probe = Owner->Inventory; probe != NULL; probe = probe->Inventory)
|
|
{
|
|
if (probe->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo))
|
|
{
|
|
if (probe->Amount < probe->MaxAmount || sv_unlimited_pickup)
|
|
{
|
|
int amount = static_cast<AAmmo*>(probe->GetDefault())->BackpackAmount;
|
|
// extra ammo in baby mode and nightmare mode
|
|
if (!(item->ItemFlags&IF_IGNORESKILL))
|
|
{
|
|
amount = int(amount * G_SkillProperty(SKILLP_AmmoFactor));
|
|
}
|
|
probe->Amount += amount;
|
|
if (probe->Amount > probe->MaxAmount && !sv_unlimited_pickup)
|
|
{
|
|
probe->Amount = probe->MaxAmount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// The pickup always succeeds, even if you didn't get anything
|
|
item->ItemFlags |= IF_PICKUPGOOD;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// ABackpackItem :: CreateTossable
|
|
//
|
|
// The tossed backpack must not give out any more ammo, otherwise a player
|
|
// could cheat by dropping their backpack and picking it up for more ammo.
|
|
//
|
|
//===========================================================================
|
|
|
|
AInventory *ABackpackItem::CreateTossable ()
|
|
{
|
|
ABackpackItem *pack = static_cast<ABackpackItem *>(Super::CreateTossable());
|
|
if (pack != NULL)
|
|
{
|
|
pack->bDepleted = true;
|
|
}
|
|
return pack;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// ABackpackItem :: DetachFromOwner
|
|
//
|
|
//===========================================================================
|
|
|
|
void ABackpackItem::DetachFromOwner ()
|
|
{
|
|
// When removing a backpack, drop the player's ammo maximums to normal
|
|
AInventory *item;
|
|
|
|
for (item = Owner->Inventory; item != NULL; item = item->Inventory)
|
|
{
|
|
if (item->GetClass()->ParentClass == RUNTIME_CLASS(AAmmo) &&
|
|
item->MaxAmount == static_cast<AAmmo*>(item)->BackpackMaxAmount)
|
|
{
|
|
item->MaxAmount = static_cast<AInventory*>(item->GetDefault())->MaxAmount;
|
|
if (item->Amount > item->MaxAmount)
|
|
{
|
|
item->Amount = item->MaxAmount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|