gzdoom-gles/src/g_inventory/a_pickups.cpp

624 lines
15 KiB
C++
Raw Normal View History

2016-03-01 15:47:10 +00:00
#include <assert.h>
#include "info.h"
#include "m_random.h"
#include "p_local.h"
#include "s_sound.h"
#include "gi.h"
#include "p_lnspec.h"
#include "sbar.h"
#include "statnums.h"
#include "c_dispatch.h"
#include "gstrings.h"
#include "templates.h"
#include "a_morph.h"
#include "a_specialspot.h"
#include "g_level.h"
#include "g_game.h"
#include "doomstat.h"
#include "d_player.h"
#include "p_spec.h"
#include "serializer.h"
#include "virtual.h"
#include "c_functions.h"
#include "g_levellocals.h"
2016-03-01 15:47:10 +00:00
EXTERN_CVAR(Bool, sv_unlimited_pickup)
2016-03-01 15:47:10 +00:00
IMPLEMENT_CLASS(PClassInventory, false, false)
2016-03-01 15:47:10 +00:00
PClassInventory::PClassInventory()
{
GiveQuest = 0;
AltHUDIcon.SetNull();
}
void PClassInventory::DeriveData(PClass *newclass)
{
assert(newclass->IsKindOf(RUNTIME_CLASS(PClassInventory)));
Super::DeriveData(newclass);
PClassInventory *newc = static_cast<PClassInventory *>(newclass);
2017-01-19 19:56:31 +00:00
newc->PickupMsg = PickupMsg;
2016-03-01 15:47:10 +00:00
newc->GiveQuest = GiveQuest;
newc->AltHUDIcon = AltHUDIcon;
newc->ForbiddenToPlayerClass = ForbiddenToPlayerClass;
newc->RestrictedToPlayerClass = RestrictedToPlayerClass;
}
size_t PClassInventory::PointerSubstitution(DObject *oldclass, DObject *newclass)
2016-03-01 15:47:10 +00:00
{
size_t changed = Super::PointerSubstitution(oldclass, newclass);
2016-03-01 15:47:10 +00:00
AInventory *def = (AInventory*)Defaults;
if (def != NULL)
{
if (def->PickupFlash == oldclass) def->PickupFlash = static_cast<PClassActor *>(newclass);
for (unsigned i = 0; i < ForbiddenToPlayerClass.Size(); i++)
{
if (ForbiddenToPlayerClass[i] == oldclass)
{
2016-03-01 15:47:10 +00:00
ForbiddenToPlayerClass[i] = static_cast<PClassPlayerPawn*>(newclass);
changed++;
}
2016-03-01 15:47:10 +00:00
}
for (unsigned i = 0; i < RestrictedToPlayerClass.Size(); i++)
{
if (RestrictedToPlayerClass[i] == oldclass)
{
2016-03-01 15:47:10 +00:00
RestrictedToPlayerClass[i] = static_cast<PClassPlayerPawn*>(newclass);
changed++;
}
2016-03-01 15:47:10 +00:00
}
}
return changed;
2016-03-01 15:47:10 +00:00
}
void PClassInventory::Finalize(FStateDefinitions &statedef)
{
Super::Finalize(statedef);
((AActor*)Defaults)->flags |= MF_SPECIAL;
}
IMPLEMENT_CLASS(AInventory, false, true)
IMPLEMENT_POINTERS_START(AInventory)
2017-01-19 19:56:31 +00:00
IMPLEMENT_POINTER(Owner)
IMPLEMENT_POINTERS_END
2016-03-01 15:47:10 +00:00
DEFINE_FIELD_BIT(AInventory, ItemFlags, bPickupGood, IF_PICKUPGOOD)
DEFINE_FIELD_BIT(AInventory, ItemFlags, bCreateCopyMoved, IF_CREATECOPYMOVED)
DEFINE_FIELD_BIT(AInventory, ItemFlags, bInitEffectFailed, IF_INITEFFECTFAILED)
2017-01-19 19:56:31 +00:00
DEFINE_FIELD(AInventory, Owner)
DEFINE_FIELD(AInventory, Amount)
DEFINE_FIELD(AInventory, MaxAmount)
DEFINE_FIELD(AInventory, InterHubAmount)
DEFINE_FIELD(AInventory, RespawnTics)
DEFINE_FIELD(AInventory, Icon)
DEFINE_FIELD(AInventory, DropTime)
DEFINE_FIELD(AInventory, SpawnPointClass)
DEFINE_FIELD(AInventory, PickupFlash)
DEFINE_FIELD(AInventory, PickupSound)
2017-01-19 19:56:31 +00:00
DEFINE_FIELD(PClassInventory, PickupMsg)
DEFINE_FIELD(PClassInventory, GiveQuest)
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// AInventory :: Tick
//
//===========================================================================
void AInventory::Tick ()
{
if (Owner == NULL)
{
// AActor::Tick is only handling interaction with the world
// and we don't want that for owned inventory items.
Super::Tick ();
}
else if (tics != -1) // ... but at least we have to advance the states
{
tics--;
// you can cycle through multiple states in a tic
// [RH] Use <= 0 instead of == 0 so that spawnstates
// of 0 tics work as expected.
if (tics <= 0)
{
assert (state != NULL);
if (state == NULL)
{
Destroy();
return;
}
if (!SetState (state->GetNextState()))
return; // freed itself
}
}
if (DropTime)
{
if (--DropTime == 0)
{
flags |= GetDefault()->flags & (MF_SPECIAL|MF_SOLID);
}
}
}
//===========================================================================
//
// AInventory :: Serialize
//
//===========================================================================
void AInventory::Serialize(FSerializer &arc)
2016-03-01 15:47:10 +00:00
{
Super::Serialize (arc);
auto def = (AInventory*)GetDefault();
arc("owner", Owner)
("amount", Amount, def->Amount)
("maxamount", MaxAmount, def->MaxAmount)
("interhubamount", InterHubAmount, def->InterHubAmount)
("respawntics", RespawnTics, def->RespawnTics)
("itemflags", ItemFlags, def->ItemFlags)
("icon", Icon, def->Icon)
("pickupsound", PickupSound, def->PickupSound)
("spawnpointclass", SpawnPointClass, def->SpawnPointClass)
("droptime", DropTime, def->DropTime);
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// AInventory :: MarkPrecacheSounds
//
//===========================================================================
void AInventory::MarkPrecacheSounds() const
{
Super::MarkPrecacheSounds();
PickupSound.MarkUsed();
}
//===========================================================================
//
// AInventory :: Grind
//
//===========================================================================
bool AInventory::Grind(bool items)
{
// Does this grind request even care about items?
if (!items)
{
return false;
}
// Dropped items are normally destroyed by crushers. Set the DONTGIB flag,
// and they'll act like corpses with it set and be immune to crushers.
if (flags & MF_DROPPED)
{
if (!(flags3 & MF3_DONTGIB))
{
Destroy();
}
return false;
}
// Non-dropped items call the super method for compatibility.
return Super::Grind(items);
}
//===========================================================================
//
// AInventory :: BecomeItem
//
// Lets this actor know that it's about to be placed in an inventory.
//
//===========================================================================
void AInventory::BecomeItem ()
{
if (!(flags & (MF_NOBLOCKMAP|MF_NOSECTOR)))
{
UnlinkFromWorld (nullptr);
2016-03-01 15:47:10 +00:00
flags |= MF_NOBLOCKMAP|MF_NOSECTOR;
LinkToWorld (nullptr);
2016-03-01 15:47:10 +00:00
}
RemoveFromHash ();
flags &= ~MF_SPECIAL;
ChangeStatNum(STAT_INVENTORY);
// stop all sounds this item is playing.
for(int i = 1;i<=7;i++) S_StopSound(this, i);
2016-03-01 15:47:10 +00:00
SetState (FindState("Held"));
}
DEFINE_ACTION_FUNCTION(AInventory, BecomeItem)
{
PARAM_SELF_PROLOGUE(AInventory);
self->BecomeItem();
return 0;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// AInventory :: BecomePickup
//
// Lets this actor know it should wait to be picked up.
//
//===========================================================================
void AInventory::BecomePickup ()
{
if (Owner != NULL)
{
Owner->RemoveInventory (this);
}
if (flags & (MF_NOBLOCKMAP|MF_NOSECTOR))
{
UnlinkFromWorld (nullptr);
2016-03-01 15:47:10 +00:00
flags &= ~(MF_NOBLOCKMAP|MF_NOSECTOR);
LinkToWorld (nullptr);
2016-03-01 15:47:10 +00:00
P_FindFloorCeiling (this);
}
flags = (GetDefault()->flags | MF_DROPPED) & ~MF_COUNTITEM;
renderflags &= ~RF_INVISIBLE;
ChangeStatNum(STAT_DEFAULT);
2016-03-01 15:47:10 +00:00
SetState (SpawnState);
}
DEFINE_ACTION_FUNCTION(AInventory, BecomePickup)
{
PARAM_SELF_PROLOGUE(AInventory);
self->BecomePickup();
return 0;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// AInventory :: GetSpeedFactor
//
//===========================================================================
double AInventory::GetSpeedFactor()
2016-03-01 15:47:10 +00:00
{
double factor = 1.;
auto self = this;
while (self != nullptr)
2016-03-01 15:47:10 +00:00
{
IFVIRTUALPTR(self, AInventory, GetSpeedFactor)
{
2017-01-19 19:56:31 +00:00
VMValue params[1] = { (DObject*)self };
double retval;
2017-01-19 19:56:31 +00:00
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
factor *= retval;
}
self = self->Inventory;
2016-03-01 15:47:10 +00:00
}
return factor;
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// AInventory :: GetNoTeleportFreeze
//
//===========================================================================
bool AInventory::GetNoTeleportFreeze ()
{
auto self = this;
while (self != nullptr)
2016-03-01 15:47:10 +00:00
{
IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze)
{
2017-01-19 19:56:31 +00:00
VMValue params[1] = { (DObject*)self };
int retval;
2017-01-19 19:56:31 +00:00
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
if (retval) return true;
}
self = self->Inventory;
2016-03-01 15:47:10 +00:00
}
return false;
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// AInventory :: Use
//
//===========================================================================
bool AInventory::CallUse(bool pickup)
{
IFVIRTUAL(AInventory, Use)
{
VMValue params[2] = { (DObject*)this, pickup };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return !!retval;
}
2017-01-19 19:56:31 +00:00
return false;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
//
//===========================================================================
2017-01-19 19:56:31 +00:00
static int StaticLastMessageTic;
static FString StaticLastMessage;
2016-03-01 15:47:10 +00:00
2017-01-19 19:56:31 +00:00
DEFINE_ACTION_FUNCTION(AInventory, PrintPickupMessage)
2016-03-01 15:47:10 +00:00
{
2017-01-19 19:56:31 +00:00
PARAM_PROLOGUE;
PARAM_BOOL(localview);
PARAM_STRING(str);
if (str.IsNotEmpty() && localview && (StaticLastMessageTic != gametic || StaticLastMessage.Compare(str)))
2016-03-01 15:47:10 +00:00
{
2017-01-19 19:56:31 +00:00
StaticLastMessageTic = gametic;
StaticLastMessage = str;
const char *pstr = str.GetChars();
2016-03-01 15:47:10 +00:00
2017-01-19 19:56:31 +00:00
if (pstr[0] == '$') pstr = GStrings(pstr + 1);
if (pstr[0] != 0) Printf(PRINT_LOW, "%s\n", pstr);
StatusBar->FlashCrosshair();
2016-03-01 15:47:10 +00:00
}
2017-01-19 19:56:31 +00:00
return 0;
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// AInventory :: Destroy
//
//===========================================================================
void AInventory::OnDestroy ()
2016-03-01 15:47:10 +00:00
{
if (Owner != NULL)
{
Owner->RemoveInventory (this);
}
Inventory = NULL;
Super::OnDestroy();
2016-03-01 15:47:10 +00:00
// Although contrived it can theoretically happen that these variables still got a pointer to this item
if (SendItemUse == this) SendItemUse = NULL;
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 ()
{
2017-01-18 22:42:08 +00:00
IFVIRTUAL(AInventory, DepleteOrDestroy)
2016-03-01 15:47:10 +00:00
{
2017-01-18 22:42:08 +00:00
VMValue params[1] = { (DObject*)this };
GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr);
2016-03-01 15:47:10 +00:00
}
}
//===========================================================================
//
// AInventory :: GetBlend
//
// Returns a color to blend to the player's view as long as they possess this
// item.
//
//===========================================================================
PalEntry AInventory::CallGetBlend()
{
IFVIRTUAL(AInventory, GetBlend)
{
VMValue params[1] = { (DObject*)this };
int retval;
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
else return 0;
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// AInventory :: PrevItem
//
// Returns the previous item.
//
//===========================================================================
AInventory *AInventory::PrevItem ()
{
AInventory *item = Owner->Inventory;
while (item != NULL && item->Inventory != this)
{
item = item->Inventory;
}
return item;
}
//===========================================================================
//
// AInventory :: PrevInv
//
// Returns the previous item with IF_INVBAR set.
//
//===========================================================================
AInventory *AInventory::PrevInv ()
{
AInventory *lastgood = NULL;
AInventory *item = Owner->Inventory;
while (item != NULL && item != this)
{
if (item->ItemFlags & IF_INVBAR)
{
lastgood = item;
}
item = item->Inventory;
}
return lastgood;
}
//===========================================================================
//
// AInventory :: NextInv
//
// Returns the next item with IF_INVBAR set.
//
//===========================================================================
AInventory *AInventory::NextInv ()
{
AInventory *item = Inventory;
while (item != NULL && !(item->ItemFlags & IF_INVBAR))
{
item = item->Inventory;
}
return item;
}
//===========================================================================
//
// AInventory :: DoRespawn
//
//===========================================================================
bool AInventory::DoRespawn ()
{
if (SpawnPointClass != NULL)
{
AActor *spot = NULL;
DSpotState *state = DSpotState::GetSpotState();
if (state != NULL) spot = state->GetRandomSpot(SpawnPointClass);
if (spot != NULL)
{
SetOrigin (spot->Pos(), false);
SetZ(floorz);
2016-03-01 15:47:10 +00:00
}
}
return true;
}
DEFINE_ACTION_FUNCTION(AInventory, DoRespawn)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_BOOL(self->DoRespawn());
}
2016-03-01 15:47:10 +00:00
//===========================================================================
//
// AInventory :: CallTryPickup
2016-03-01 15:47:10 +00:00
//
//===========================================================================
bool AInventory::CallTryPickup(AActor *toucher, AActor **toucher_return)
2016-03-01 15:47:10 +00:00
{
static VMFunction *func = nullptr;
if (func == nullptr) func = PClass::FindFunction(NAME_Inventory, NAME_CallTryPickup);
VMValue params[2] = { (DObject*)this, toucher };
VMReturn ret[2];
int res;
AActor *tret;
ret[0].IntAt(&res);
ret[1].PointerAt((void**)&tret);
GlobalVMStack.Call(func, params, 2, ret, 2);
if (toucher_return) *toucher_return = tret;
return !!res;
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// AInventory :: CanPickup
2016-03-01 15:47:10 +00:00
//
//===========================================================================
DEFINE_ACTION_FUNCTION(AInventory, CanPickup)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(toucher, AActor);
2016-03-01 15:47:10 +00:00
if (!toucher)
ACTION_RETURN_BOOL(false);
2016-03-01 15:47:10 +00:00
PClassInventory *ai = self->GetClass();
2016-03-01 15:47:10 +00:00
// Is the item restricted to certain player classes?
if (ai->RestrictedToPlayerClass.Size() != 0)
{
for (unsigned i = 0; i < ai->RestrictedToPlayerClass.Size(); ++i)
{
if (toucher->IsKindOf(ai->RestrictedToPlayerClass[i]))
ACTION_RETURN_BOOL(true);
2016-03-01 15:47:10 +00:00
}
ACTION_RETURN_BOOL(false);
2016-03-01 15:47:10 +00:00
}
// Or is it forbidden to certain other classes?
else
{
for (unsigned i = 0; i < ai->ForbiddenToPlayerClass.Size(); ++i)
{
if (toucher->IsKindOf(ai->ForbiddenToPlayerClass[i]))
ACTION_RETURN_BOOL(false);
2016-03-01 15:47:10 +00:00
}
}
ACTION_RETURN_BOOL(true);
2016-03-01 15:47:10 +00:00
}
//===========================================================================
//
// CCMD printinv
//
// Prints the console player's current inventory.
//
//===========================================================================
CCMD (printinv)
{
int pnum = consoleplayer;
#ifdef _DEBUG
// Only allow peeking on other players' inventory in debug builds.
if (argv.argc() > 1)
{
pnum = atoi (argv[1]);
if (pnum < 0 || pnum >= MAXPLAYERS)
{
return;
}
}
#endif
C_PrintInv(players[pnum].mo);
2016-03-01 15:47:10 +00:00
}
CCMD (targetinv)
{
FTranslatedLineTarget t;
if (CheckCheatmode () || players[consoleplayer].mo == NULL)
return;
C_AimLine(&t, true);
if (t.linetarget)
{
C_PrintInv(t.linetarget);
}
else Printf("No target found. Targetinv cannot find actors that have "
"the NOBLOCKMAP flag or have height/radius of 0.\n");
}
//===========================================================================
//===========================================================================
IMPLEMENT_CLASS(AStateProvider, false, false)
2016-03-01 15:47:10 +00:00