more inventory scriptification

* completely scriptified DehackedPickup and FakeInventory.
* scriptified all remaining virtual functions of Inventory, so that its inheritance is now 100% script-side.
* scriptified CallTryPickup and most of the code called by that.

- fixed: Passing local variables by reference did not work in the VM.
This commit is contained in:
Christoph Oelckers 2017-01-19 23:42:12 +01:00
commit 3c30b59bab
19 changed files with 483 additions and 612 deletions

View file

@ -99,6 +99,7 @@ DEFINE_FIELD(AInventory, SpawnPointClass)
DEFINE_FIELD(AInventory, PickupFlash)
DEFINE_FIELD(AInventory, PickupSound)
DEFINE_FIELD(PClassInventory, PickupMsg)
DEFINE_FIELD(PClassInventory, GiveQuest)
//===========================================================================
//
@ -177,22 +178,6 @@ void AInventory::MarkPrecacheSounds() const
PickupSound.MarkUsed();
}
//===========================================================================
//
// AInventory :: ShouldRespawn
//
// Returns true if the item should hide itself and reappear later when picked
// up.
//
//===========================================================================
bool AInventory::ShouldRespawn ()
{
if ((ItemFlags & IF_BIGPOWERUP) && !(dmflags2 & DF2_RESPAWN_SUPER)) return false;
if (ItemFlags & IF_NEVERRESPAWN) return false;
return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN));
}
//===========================================================================
//
// AInventory :: Grind
@ -220,66 +205,6 @@ bool AInventory::Grind(bool items)
return Super::Grind(items);
}
//===========================================================================
//
// AInventory :: GoAway
//
// Returns true if you must create a copy of this item to give to the player
// or false if you can use this one instead.
//
//===========================================================================
bool AInventory::GoAway ()
{
// Dropped items never stick around
if (flags & MF_DROPPED)
{
return false;
}
if (!CallShouldStay ())
{
Hide ();
if (ShouldRespawn ())
{
return true;
}
return false;
}
return true;
}
DEFINE_ACTION_FUNCTION(AInventory, GoAway)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_BOOL(self->GoAway());
}
//===========================================================================
//
// AInventory :: GoAwayAndDie
//
// Like GoAway but used by items that don't insert themselves into the
// inventory. If they won't be respawning, then they can destroy themselves.
//
//===========================================================================
void AInventory::GoAwayAndDie ()
{
if (!GoAway ())
{
flags &= ~MF_SPECIAL;
SetState (FindState("HoldAndDestroy"));
}
}
DEFINE_ACTION_FUNCTION(AInventory, GoAwayAndDie)
{
PARAM_SELF_PROLOGUE(AInventory);
self->GoAwayAndDie();
return 0;
}
//===========================================================================
//
// AInventory :: BecomeItem
@ -405,9 +330,8 @@ bool AInventory::CallUse(bool pickup)
IFVIRTUAL(AInventory, Use)
{
VMValue params[2] = { (DObject*)this, pickup };
VMReturn ret;
int retval;
ret.IntAt(&retval);
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
return !!retval;
}
@ -415,58 +339,6 @@ bool AInventory::CallUse(bool pickup)
}
//===========================================================================
//
// AInventory :: Hide
//
// Hides this actor until it's time to respawn again.
//
//===========================================================================
void AInventory::Hide ()
{
FState *HideSpecialState = NULL, *HideDoomishState = NULL;
flags = (flags & ~MF_SPECIAL) | MF_NOGRAVITY;
renderflags |= RF_INVISIBLE;
if (gameinfo.gametype & GAME_Raven)
{
HideSpecialState = FindState("HideSpecial");
if (HideSpecialState == NULL)
{
HideDoomishState = FindState("HideDoomish");
}
}
else
{
HideDoomishState = FindState("HideDoomish");
if (HideDoomishState == NULL)
{
HideSpecialState = FindState("HideSpecial");
}
}
assert(HideDoomishState != NULL || HideSpecialState != NULL);
if (HideSpecialState != NULL)
{
SetState (HideSpecialState);
tics = 1400;
if (PickupFlash != NULL) tics += 30;
}
else if (HideDoomishState != NULL)
{
SetState (HideDoomishState);
tics = 1050;
}
if (RespawnTics != 0)
{
tics = RespawnTics;
}
}
//===========================================================================
//
//
@ -492,112 +364,6 @@ DEFINE_ACTION_FUNCTION(AInventory, PrintPickupMessage)
return 0;
}
//===========================================================================
//
// AInventory :: DoPickupSpecial
//
// Executes this actor's special when it is picked up.
//
//===========================================================================
void AInventory::DoPickupSpecial (AActor *toucher)
{
if (special)
{
P_ExecuteSpecial(special, NULL, toucher, false,
args[0], args[1], args[2], args[3], args[4]);
special = 0;
}
}
DEFINE_ACTION_FUNCTION(AInventory, DoPickupSpecial)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(toucher, AActor);
self->DoPickupSpecial(toucher);
return 0;
}
//===========================================================================
//
// AInventory :: PlayPickupSound
//
//===========================================================================
void AInventory::PlayPickupSound (AActor *toucher)
{
float atten;
int chan;
if (ItemFlags & IF_NOATTENPICKUPSOUND)
{
atten = ATTN_NONE;
}
#if 0
else if ((ItemFlags & IF_FANCYPICKUPSOUND) &&
(toucher == NULL || toucher->CheckLocalView(consoeplayer)))
{
atten = ATTN_NONE;
}
#endif
else
{
atten = ATTN_NORM;
}
if (toucher != NULL && toucher->CheckLocalView(consoleplayer))
{
chan = CHAN_PICKUP|CHAN_NOPAUSE;
}
else
{
chan = CHAN_PICKUP;
}
S_Sound (toucher, chan, PickupSound, 1, atten);
}
DEFINE_ACTION_FUNCTION(AInventory, PlayPickupSound)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(other, AActor);
self->PlayPickupSound(other);
return 0;
}
void AInventory::CallPlayPickupSound(AActor *other)
{
IFVIRTUAL(AInventory, PlayPickupSound)
{
VMValue params[2] = { (DObject*)this, (DObject*)other };
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
}
else PlayPickupSound(other);
}
//===========================================================================
//
// AInventory :: ShouldStay
//
// Returns true if the item should not disappear, even temporarily.
//
//===========================================================================
bool AInventory::CallShouldStay()
{
IFVIRTUAL(AInventory, ShouldStay)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return !!retval;
}
return false;
}
//===========================================================================
//
// AInventory :: Destroy
@ -644,29 +410,17 @@ void AInventory::DepleteOrDestroy ()
//
//===========================================================================
PalEntry AInventory::GetBlend ()
{
return 0;
}
DEFINE_ACTION_FUNCTION(AInventory, GetBlend)
{
PARAM_SELF_PROLOGUE(AInventory);
ACTION_RETURN_INT(self->GetBlend());
}
PalEntry AInventory::CallGetBlend()
{
IFVIRTUAL(AInventory, GetBlend)
{
VMValue params[1] = { (DObject*)this };
VMReturn ret;
int retval;
ret.IntAt(&retval);
VMReturn ret(&retval);
GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr);
return retval;
}
else return GetBlend();
else return 0;
}
//===========================================================================
@ -759,129 +513,51 @@ DEFINE_ACTION_FUNCTION(AInventory, DoRespawn)
ACTION_RETURN_BOOL(self->DoRespawn());
}
//===========================================================================
//
// AInventory :: GiveQuest
//
//===========================================================================
void AInventory::GiveQuest (AActor *toucher)
{
int quest = GetClass()->GiveQuest;
if (quest > 0 && quest <= (int)countof(QuestItemClasses))
{
toucher->GiveInventoryType (QuestItemClasses[quest-1]);
}
}
//===========================================================================
//
// AInventory :: CallTryPickup
//
//===========================================================================
bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return)
bool AInventory::CallTryPickup(AActor *toucher, AActor **toucher_return)
{
TObjPtr<AInventory> Invstack = Inventory; // A pointer of the inventories item stack.
// unmorphed versions of a currently morphed actor cannot pick up anything.
if (toucher->flags & MF_UNMORPHED) return false;
bool res;
if (CanPickup(toucher))
{
IFVIRTUAL(AInventory, TryPickup)
{
VMValue params[2] = { (DObject*)this, (void*)&toucher };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
res = !!retval;
}
}
else if (!(ItemFlags & IF_RESTRICTABSOLUTELY))
{
// let an item decide for itself how it will handle this
IFVIRTUAL(AInventory, TryPickupRestricted)
{
VMValue params[2] = { (DObject*)this, (void*)&toucher };
VMReturn ret;
int retval;
ret.IntAt(&retval);
GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr);
res = !!retval;
}
}
else
return false;
// Morph items can change the toucher so we need an option to return this info.
if (toucher_return != NULL) *toucher_return = toucher;
if (!res && (ItemFlags & IF_ALWAYSPICKUP) && !CallShouldStay())
{
res = true;
GoAwayAndDie();
}
if (res)
{
GiveQuest(toucher);
// Transfer all inventory accross that the old object had, if requested.
if ((ItemFlags & IF_TRANSFER))
{
while (Invstack)
{
AInventory* titem = Invstack;
Invstack = titem->Inventory;
if (titem->Owner == this)
{
if (!titem->CallTryPickup(toucher)) // The object no longer can exist
{
titem->Destroy();
}
}
}
}
}
return res;
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;
}
DEFINE_ACTION_FUNCTION(AInventory, CallTryPickup)
{
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(toucher, AActor);
AActor *t_ret;
bool res = self->CallTryPickup(toucher, &t_ret);
if (numret > 0) ret[0].SetInt(res);
if (numret > 1) ret[1].SetPointer(t_ret, ATAG_OBJECT), numret = 2;
return numret;
}
//===========================================================================
//
// AInventory :: CanPickup
//
//===========================================================================
bool AInventory::CanPickup (AActor *toucher)
DEFINE_ACTION_FUNCTION(AInventory, CanPickup)
{
if (!toucher)
return false;
PARAM_SELF_PROLOGUE(AInventory);
PARAM_OBJECT(toucher, AActor);
PClassInventory *ai = GetClass();
if (!toucher)
ACTION_RETURN_BOOL(false);
PClassInventory *ai = self->GetClass();
// 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]))
return true;
ACTION_RETURN_BOOL(true);
}
return false;
ACTION_RETURN_BOOL(false);
}
// Or is it forbidden to certain other classes?
else
@ -889,10 +565,10 @@ bool AInventory::CanPickup (AActor *toucher)
for (unsigned i = 0; i < ai->ForbiddenToPlayerClass.Size(); ++i)
{
if (toucher->IsKindOf(ai->ForbiddenToPlayerClass[i]))
return false;
ACTION_RETURN_BOOL(false);
}
}
return true;
ACTION_RETURN_BOOL(true);
}
//===========================================================================
@ -938,22 +614,6 @@ CCMD (targetinv)
"the NOBLOCKMAP flag or have height/radius of 0.\n");
}
//===========================================================================
//
// AInventory :: AttachToOwner
//
//===========================================================================
void AInventory::CallAttachToOwner(AActor *other)
{
IFVIRTUAL(AInventory, AttachToOwner)
{
VMValue params[2] = { (DObject*)this, (DObject*)other };
GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr);
}
}
//===========================================================================
//===========================================================================