From 3c30b59babc5b01314cc1cea988bd8ff5d71b0a2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 19 Jan 2017 23:42:12 +0100 Subject: [PATCH] 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. --- src/d_dehacked.cpp | 113 +---- src/d_dehacked.h | 23 - src/dobject.h | 1 + src/dobjtype.h | 5 + src/g_inventory/a_pickups.cpp | 394 ++---------------- src/g_inventory/a_pickups.h | 44 +- src/g_level.cpp | 4 +- src/gl/dynlights/gl_dynlight.cpp | 2 +- src/gl/scene/gl_scene.cpp | 2 +- src/namedef.h | 6 + src/p_enemy.cpp | 13 +- src/p_lnspec.cpp | 2 +- src/scripting/codegeneration/codegen.cpp | 11 +- src/scripting/decorate/olddecorations.cpp | 50 +-- src/scripting/decorate/thingdef_parse.cpp | 7 +- src/scripting/thingdef.cpp | 9 - src/scripting/zscript/zcc_compile.cpp | 16 +- wadsrc/static/zscript/constants.txt | 4 +- wadsrc/static/zscript/inventory/inventory.txt | 393 ++++++++++++++++- 19 files changed, 485 insertions(+), 614 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index eda79f744..b9d9b876d 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -222,23 +222,17 @@ DEFINE_FIELD_X(DehInfo, DehInfo, BlueAC) // Doom identified pickup items by their sprites. ZDoom prefers to use their // class type to identify them instead. To support the traditional Doom // behavior, for every thing touched by dehacked that has the MF_PICKUP flag, -// a new subclass of ADehackedPickup will be created with properties copied +// a new subclass of DehackedPickup will be created with properties copied // from the original actor's defaults. The original actor is then changed to // spawn the new class. -IMPLEMENT_CLASS(ADehackedPickup, false, true) - -IMPLEMENT_POINTERS_START(ADehackedPickup) - IMPLEMENT_POINTER(RealPickup) -IMPLEMENT_POINTERS_END - TArray TouchedActors; char *UnchangedSpriteNames; int NumUnchangedSprites; bool changedStates; -// Sprite<->Class map for ADehackedPickup::DetermineType +// Sprite<->Class map for DehackedPickup::DetermineType static struct DehSpriteMap { char Sprite[5]; @@ -3009,12 +3003,12 @@ void FinishDehPatch () // Create a new class that will serve as the actual pickup char typeNameBuilder[32]; // + auto dehtype = PClass::FindActor(NAME_DehackedPickup); do { // Retry until we find a free name. This is unlikely to happen but not impossible. mysnprintf(typeNameBuilder, countof(typeNameBuilder), "DehackedPickup%d", nameindex++); - subclass = static_cast(RUNTIME_CLASS(ADehackedPickup)-> - CreateDerivedClass(typeNameBuilder, sizeof(ADehackedPickup))); + subclass = static_cast(dehtype->CreateDerivedClass(typeNameBuilder, dehtype->Size)); } while (subclass == nullptr); @@ -3120,94 +3114,10 @@ void FinishDehPatch () WeaponNames.ShrinkToFit(); } -void ModifyDropAmount(AInventory *inv, int dropamount); - -bool ADehackedPickup::TryPickup (AActor *&toucher) +DEFINE_ACTION_FUNCTION(ADehackedPickup, DetermineType) { - PClassActor *type = DetermineType (); - if (type == NULL) - { - return false; - } - RealPickup = static_cast(Spawn (type, Pos(), NO_REPLACE)); - if (RealPickup != NULL) - { - // The internally spawned item should never count towards statistics. - RealPickup->ClearCounters(); - if (!(flags & MF_DROPPED)) - { - RealPickup->flags &= ~MF_DROPPED; - } - // If this item has been dropped by a monster the - // amount of ammo this gives must be adjusted. - if (droppedbymonster) - { - ModifyDropAmount(RealPickup, 0); - } - if (!RealPickup->CallTryPickup (toucher)) - { - RealPickup->Destroy (); - RealPickup = NULL; - return false; - } - GoAwayAndDie (); - return true; - } - return false; -} + PARAM_SELF_PROLOGUE(AInventory); -FString ADehackedPickup::PickupMessage () -{ - /* - if (RealPickup != nullptr) - return RealPickup->PickupMessage (); - else*/ return ""; -} - -bool ADehackedPickup::ShouldStay () -{ - if (RealPickup != nullptr) - return RealPickup->CallShouldStay (); - else return true; -} - -bool ADehackedPickup::ShouldRespawn () -{ - if (RealPickup != nullptr) - return RealPickup->ShouldRespawn (); - else return false; -} - -void ADehackedPickup::PlayPickupSound (AActor *toucher) -{ - if (RealPickup != nullptr) - RealPickup->CallPlayPickupSound (toucher); -} - -void ADehackedPickup::DoPickupSpecial (AActor *toucher) -{ - Super::DoPickupSpecial (toucher); - // If the real pickup hasn't joined the toucher's inventory, make sure it - // doesn't stick around. - if (RealPickup != nullptr && RealPickup->Owner != toucher) - { - RealPickup->Destroy (); - } - RealPickup = nullptr; -} - -void ADehackedPickup::OnDestroy () -{ - if (RealPickup != nullptr) - { - RealPickup->Destroy (); - RealPickup = nullptr; - } - Super::OnDestroy(); -} - -PClassActor *ADehackedPickup::DetermineType () -{ // Look at the actor's current sprite to determine what kind of // item to pretend to me. int min = 0; @@ -3216,10 +3126,10 @@ PClassActor *ADehackedPickup::DetermineType () while (min <= max) { int mid = (min + max) / 2; - int lex = memcmp (DehSpriteMappings[mid].Sprite, sprites[sprite].name, 4); + int lex = memcmp (DehSpriteMappings[mid].Sprite, sprites[self->sprite].name, 4); if (lex == 0) { - return PClass::FindActor(DehSpriteMappings[mid].ClassName); + ACTION_RETURN_OBJECT(PClass::FindActor(DehSpriteMappings[mid].ClassName)); } else if (lex < 0) { @@ -3230,11 +3140,6 @@ PClassActor *ADehackedPickup::DetermineType () max = mid - 1; } } - return NULL; + ACTION_RETURN_OBJECT(nullptr); } -void ADehackedPickup::Serialize(FSerializer &arc) -{ - Super::Serialize(arc); - arc("droppedbymonster", droppedbymonster); -} diff --git a/src/d_dehacked.h b/src/d_dehacked.h index 7b21e8694..f7fce9868 100644 --- a/src/d_dehacked.h +++ b/src/d_dehacked.h @@ -34,29 +34,6 @@ #ifndef __D_DEHACK_H__ #define __D_DEHACK_H__ -#include "a_pickups.h" - -class ADehackedPickup : public AInventory -{ - DECLARE_CLASS (ADehackedPickup, AInventory) - HAS_OBJECT_POINTERS -public: - void OnDestroy() override; - FString PickupMessage (); - bool ShouldRespawn (); - bool ShouldStay (); - bool TryPickup (AActor *&toucher); - void PlayPickupSound (AActor *toucher); - void DoPickupSpecial (AActor *toucher); - - void Serialize(FSerializer &arc); -private: - PClassActor *DetermineType (); - AInventory *RealPickup; -public: - bool droppedbymonster; -}; - int D_LoadDehLumps(); bool D_LoadDehLump(int lumpnum); bool D_LoadDehFile(const char *filename); diff --git a/src/dobject.h b/src/dobject.h index 2f907d493..6b9b87c75 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -478,6 +478,7 @@ public: void Destroy(); // Add other types as needed. + bool &BoolVar(FName field); int &IntVar(FName field); PalEntry &ColorVar(FName field); FName &NameVar(FName field); diff --git a/src/dobjtype.h b/src/dobjtype.h index 12245795a..316a2bccc 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -1073,6 +1073,11 @@ enum ETypeVal : BYTE VAL_Class, }; +inline bool &DObject::BoolVar(FName field) +{ + return *(bool*)ScriptVar(field, TypeBool); +} + inline int &DObject::IntVar(FName field) { return *(int*)ScriptVar(field, TypeSInt32); diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index b3826d46f..cd4d02f04 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -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 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); - } -} - - //=========================================================================== //=========================================================================== diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 4c5094ca6..7ca317397 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -78,39 +78,19 @@ public: virtual void Tick() override; virtual bool Grind(bool items) override; - // virtual methods that only get overridden by special internal classes, like DehackedPickup. - // There is no need to expose these to scripts. - void DepleteOrDestroy (); - virtual bool ShouldRespawn (); - virtual void DoPickupSpecial (AActor *toucher); + bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // Wrapper for script function. - bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // This wraps both virtual methods plus a few more checks. + void DepleteOrDestroy (); // virtual on the script side. + bool CallUse(bool pickup); // virtual on the script side. + PalEntry CallGetBlend(); // virtual on the script side. + double GetSpeedFactor(); // virtual on the script side. + bool GetNoTeleportFreeze(); // virtual on the script side. - bool CallUse(bool pickup); - - virtual PalEntry GetBlend(); - PalEntry CallGetBlend(); - - bool CallShouldStay(); - - virtual void PlayPickupSound(AActor *toucher); - void CallPlayPickupSound(AActor *toucher); - - void CallAttachToOwner(AActor *other); - - // virtual on the script side only. - double GetSpeedFactor(); - bool GetNoTeleportFreeze(); - - - bool GoAway(); - void GoAwayAndDie(); - - void Hide(); void BecomeItem (); void BecomePickup (); bool DoRespawn(); + AInventory *PrevItem(); // Returns the item preceding this one in the list. AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set. AInventory *NextInv(); // Returns the next item with IF_INVBAR set. @@ -128,13 +108,6 @@ public: PClassActor *PickupFlash; // actor to spawn as pickup flash FSoundIDNoInit PickupSound; - - -protected: - bool CanPickup(AActor * toucher); - void GiveQuest(AActor * toucher); - -private: }; class AStateProvider : public AInventory @@ -144,7 +117,4 @@ public: bool CallStateChain(AActor *actor, FState *state); }; -extern PClassActor *QuestItemClasses[31]; - - #endif //__A_PICKUPS_H__ diff --git a/src/g_level.cpp b/src/g_level.cpp index 030720a88..019106d75 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1097,8 +1097,8 @@ void G_WorldDone (void) // Strife needs a special case here to choose between good and sad ending. Bad is handled elsewhere. if (endsequence == NAME_Inter_Strife) { - if (players[0].mo->FindInventory (QuestItemClasses[24]) || - players[0].mo->FindInventory (QuestItemClasses[27])) + if (players[0].mo->FindInventory (NAME_QuestItem25) || + players[0].mo->FindInventory (NAME_QuestItem28)) { endsequence = NAME_Inter_Strife_Good; } diff --git a/src/gl/dynlights/gl_dynlight.cpp b/src/gl/dynlights/gl_dynlight.cpp index 641878979..a2fb14ff6 100644 --- a/src/gl/dynlights/gl_dynlight.cpp +++ b/src/gl/dynlights/gl_dynlight.cpp @@ -77,7 +77,7 @@ void gl_ParseVavoomSkybox(); inline PClassActor * GetRealType(PClassActor * ti) { PClassActor *rep = ti->GetReplacement(false); - if (rep != ti && rep != NULL && rep->IsDescendantOf(RUNTIME_CLASS(ADehackedPickup))) + if (rep != ti && rep != NULL && rep->IsDescendantOf(PClass::FindActor(NAME_DehackedPickup))) { return rep; } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index b1510d420..b67cf810e 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -771,7 +771,7 @@ void FGLRenderer::SetFixedColormap (player_t *player) auto litetype = PClass::FindActor(NAME_PowerLightAmp); for(AInventory * in = cplayer->mo->Inventory; in; in = in->Inventory) { - PalEntry color = in->GetBlend (); + PalEntry color = in->CallGetBlend (); // Need special handling for light amplifiers if (in->IsKindOf(torchtype)) diff --git a/src/namedef.h b/src/namedef.h index 926fac1b2..cc393ffaf 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -186,6 +186,7 @@ xx(HealthPickup) xx(autousemode) xx(Ammo) xx(WeaponGiver) +xx(DehackedPickup) xx(PowerTargeter) xx(PowerInvulnerable) xx(PowerStrength) @@ -197,6 +198,11 @@ xx(PowerFlight) xx(PowerSpeed) xx(PowerTorch) xx(CustomInventory) +xx(Inventory) +xx(CallTryPickup) +xx(QuestItem25) +xx(QuestItem28) +xx(QuestItem29) xx(AcolyteBlue) xx(SpectralLightningV1) diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index e42f9d08f..e93f41ce5 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -3261,13 +3261,22 @@ void ModifyDropAmount(AInventory *inv, int dropamount) static_cast(inv)->AmmoGive2 = int(static_cast(inv)->AmmoGive2 * dropammofactor); inv->ItemFlags |= flagmask; } - else if (inv->IsKindOf (RUNTIME_CLASS(ADehackedPickup))) + else if (inv->IsKindOf (PClass::FindClass(NAME_DehackedPickup))) { // For weapons and ammo modified by Dehacked we need to flag the item. - static_cast(inv)->droppedbymonster = true; + inv->BoolVar("droppedbymonster") = true; } } +// todo: make this a scripted virtual function so it can better deal with some of the classes involved. +DEFINE_ACTION_FUNCTION(AInventory, ModifyDropAmount) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_INT(dropamount); + ModifyDropAmount(self, dropamount); + return 0; +} + //--------------------------------------------------------------------------- // // PROC P_DropItem diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 900f79497..5375085dd 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -3230,7 +3230,7 @@ FUNC(LS_GlassBreak) } if (it != NULL) { - it->GiveInventoryType (QuestItemClasses[28]); + it->GiveInventoryType (PClass::FindActor("QuestItem29")); it->GiveInventoryType (PClass::FindActor("UpgradeAccuracy")); it->GiveInventoryType (PClass::FindActor("UpgradeStamina")); } diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 4e41fce14..4395d123e 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -420,6 +420,10 @@ PPrototype *FxExpression::ReturnProto() static int EncodeRegType(ExpEmit reg) { int regtype = reg.RegType; + if (reg.Fixed && reg.Target) + { + regtype |= REGT_ADDROF; + } if (reg.Konst) { regtype |= REGT_KONST; @@ -10008,10 +10012,9 @@ FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) { for (unsigned i = 0; i < Args.Size(); i++) { - auto &Value = Args[0]; - Value = new FxTypeCast(Value, ctx.ReturnProto->ReturnTypes[i], false, false); - Value = Value->Resolve(ctx); - if (Value == nullptr) fail = true; + Args[i] = new FxTypeCast(Args[i], ctx.ReturnProto->ReturnTypes[i], false, false); + Args[i] = Args[i]->Resolve(ctx); + if (Args[i] == nullptr) fail = true; } if (fail) { diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index 754d6deef..077a75ad6 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -65,40 +65,6 @@ struct FExtraInfo double DeathHeight, BurnHeight; }; -class AFakeInventory : public AInventory -{ - DECLARE_CLASS (AFakeInventory, AInventory); -public: - bool Respawnable; - - bool ShouldRespawn () - { - return Respawnable && Super::ShouldRespawn(); - } - - bool TryPickup (AActor *&toucher) - { - INTBOOL success = P_ExecuteSpecial(special, NULL, toucher, false, - args[0], args[1], args[2], args[3], args[4]); - - if (success) - { - GoAwayAndDie (); - return true; - } - return false; - } - - void DoPickupSpecial (AActor *toucher) - { - // The special was already executed by TryPickup, so do nothing here - } -}; - -IMPLEMENT_CLASS(AFakeInventory, false, false) - -DEFINE_FIELD(AFakeInventory, Respawnable) - // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- @@ -125,16 +91,6 @@ static const char *RenderStyles[] = // CODE -------------------------------------------------------------------- -//========================================================================== -// -//========================================================================== -DEFINE_CLASS_PROPERTY(respawns, 0, FakeInventory) -{ - defaults->Respawnable = true; -} - - - //========================================================================== // // ParseOldDecoration @@ -154,7 +110,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) PClassActor *parent; FName typeName; - parent = (def == DEF_Pickup) ? RUNTIME_CLASS(AFakeInventory) : RUNTIME_CLASS(AActor); + parent = (def == DEF_Pickup) ? PClass::FindActor("FakeInventory") : RUNTIME_CLASS(AActor); sc.MustGetString(); typeName = FName(sc.String); @@ -360,7 +316,7 @@ void ParseOldDecoration(FScanner &sc, EDefinitionType def) static void ParseInsideDecoration (Baggage &bag, AActor *defaults, FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray &StateArray, TArray &SourceLines) { - AFakeInventory *const inv = static_cast(defaults); + AInventory *const inv = static_cast(defaults); char sprite[5] = "TNT1"; sc.MustGetString (); @@ -588,7 +544,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, } else if (def == DEF_Pickup && sc.Compare ("Respawns")) { - inv->Respawnable = true; + inv->BoolVar("Respawnable") = true; } else if (def == DEF_BreakableDecoration && sc.Compare ("SolidOnDeath")) { diff --git a/src/scripting/decorate/thingdef_parse.cpp b/src/scripting/decorate/thingdef_parse.cpp index 7dceceb32..837a03a32 100644 --- a/src/scripting/decorate/thingdef_parse.cpp +++ b/src/scripting/decorate/thingdef_parse.cpp @@ -842,7 +842,12 @@ static void DispatchScriptProperty(FScanner &sc, PProperty *prop, AActor *defaul addr = ((char*)defaults) + f->Offset; } - if (f->Type->IsKindOf(RUNTIME_CLASS(PInt))) + if (f->Type == TypeBool) + { + bool val = sc.CheckNumber() ? !!sc.Number : true; + static_cast(f->Type)->SetValue(addr, !!val); + } + else if (f->Type->IsKindOf(RUNTIME_CLASS(PInt))) { sc.MustGetNumber(); static_cast(f->Type)->SetValue(addr, sc.Number); diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index eea1d56bf..9edb705bf 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -69,8 +69,6 @@ void InitThingdef(); // STATIC FUNCTION PROTOTYPES -------------------------------------------- -PClassActor *QuestItemClasses[31]; - static TMap StateSourceLines; static FScriptPosition unknownstatesource("unknown file", 0); @@ -448,12 +446,5 @@ void LoadActors() // Now we may call the scripted OnDestroy method. PClass::bVMOperational = true; - // Since these are defined in DECORATE now the table has to be initialized here. - for (int i = 0; i < 31; i++) - { - char fmt[20]; - mysnprintf(fmt, countof(fmt), "QuestItem%d", i + 1); - QuestItemClasses[i] = PClass::FindActor(fmt); - } StateSourceLines.Clear(); } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 1b0c0dfef..ab96813bd 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1926,6 +1926,7 @@ void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *proper void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag) { + ZCC_ExprConstant one; unsigned parmcount = 1; ZCC_TreeNode *x = property->Values; while (x->SiblingNext != property->Values) @@ -1933,7 +1934,16 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop x = x->SiblingNext; parmcount++; } - if (parmcount != prop->Variables.Size()) + if (parmcount == 0 && prop->Variables.Size() == 1 && prop->Variables[0]->Type == TypeBool) + { + // allow boolean properties to have the parameter omitted + one.Operation = PEX_ConstValue; + one.NodeType = AST_ExprConstant; + one.Type = TypeBool; + one.IntVal = 1; + property->Values = &one; + } + else if (parmcount != prop->Variables.Size()) { Error(x, "Argument count mismatch: Got %u, expected %u", parmcount, prop->Variables.Size()); return; @@ -1954,6 +1964,10 @@ void ZCCCompiler::DispatchScriptProperty(PProperty *prop, ZCC_PropertyStmt *prop addr = ((char*)defaults) + f->Offset; } + if (f->Type == TypeBool) + { + static_cast(f->Type)->SetValue(addr, !!GetInt(exp)); + } if (f->Type->IsKindOf(RUNTIME_CLASS(PInt))) { static_cast(f->Type)->SetValue(addr, GetInt(exp)); diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index d7139c650..f31493077 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -411,7 +411,9 @@ enum ESoundFlags CHAN_LISTENERZ = 8, CHAN_MAYBE_LOCAL = 16, CHAN_UI = 32, - CHAN_NOPAUSE = 64 + CHAN_NOPAUSE = 64, + CHAN_PICKUP = (CHAN_ITEM|CHAN_MAYBE_LOCAL) + }; // sound attenuation values diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index fced80e24..0a193978c 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -24,6 +24,7 @@ class Inventory : Actor native native bool bCreateCopyMoved; native bool bInitEffectFailed; native meta String PickupMsg; + native meta int GiveQuest; Default { @@ -35,19 +36,11 @@ class Inventory : Actor native Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG"; } - virtual native color GetBlend (); - virtual native void PlayPickupSound(Actor user); - virtual native void DoPickupSpecial (Actor toucher); - + native bool CanPickup(Actor toucher); native bool DoRespawn(); - native bool GoAway(); - native void GoAwayAndDie(); native void BecomeItem(); native void BecomePickup(); - - // In this case the caller function is more than a simple wrapper around the virtual method and - // is what must be actually called to pick up an item. - native bool, Actor CallTryPickup(Actor toucher); + native void ModifyDropAmount(int dropamount); native static void PrintPickupMessage (bool localview, String str); States(Actor, Overlay, Weapon, Item) @@ -307,6 +300,86 @@ class Inventory : Actor native return true; } + //=========================================================================== + // + // AInventory :: GiveQuest + // + //=========================================================================== + + void GiveQuestItem (Actor toucher) + { + if (GiveQuest > 0) + { + String qname = "QuestItem" .. GiveQuest; + class type = qname; + if (type != null) + { + toucher.GiveInventoryType (type); + } + } + } + + //=========================================================================== + // + // AInventory :: CallTryPickup + // + // In this case the caller function is more than a simple wrapper around the virtual method and + // is what must be actually called to pick up an item. + // + //=========================================================================== + + bool, Actor CallTryPickup(Actor toucher) + { + let saved_toucher = toucher; + let Invstack = Inv; // A pointer of the inventories item stack. + + // unmorphed versions of a currently morphed actor cannot pick up anything. + if (bUnmorphed) return false, null; + + bool res; + if (CanPickup(toucher)) + { + res = TryPickup(toucher); + } + else if (!bRestrictAbsolutely) + { + // let an item decide for itself how it will handle this + res = TryPickupRestricted(toucher); + } + else + return false, null; + + + if (!res && (bAlwaysPickup) && !ShouldStay()) + { + res = true; + GoAwayAndDie(); + } + + if (res) + { + GiveQuestItem(toucher); + + // Transfer all inventory across that the old object had, if requested. + if (bTransfer) + { + while (Invstack) + { + let titem = Invstack; + Invstack = titem.Inv; + if (titem.Owner == self) + { + if (!titem.CallTryPickup(toucher)) // The object no longer can exist + { + titem.Destroy(); + } + } + } + } + } + return res, toucher; + } + //=========================================================================== // // AInventory :: ShouldStay @@ -536,6 +609,121 @@ class Inventory : Actor native virtual void DoEffect() {} + //=========================================================================== + // + // AInventory :: Hide + // + // Hides this actor until it's time to respawn again. + // + //=========================================================================== + + virtual void Hide () + { + State HideSpecialState = NULL, HideDoomishState = NULL; + + bSpecial = false; + bNoGravity = true; + bInvisible = true; + + if (gameinfo.gametype & GAME_Raven) + { + HideSpecialState = FindState("HideSpecial"); + if (HideSpecialState == NULL) + { + HideDoomishState = FindState("HideDoomish"); + } + } + else + { + HideDoomishState = FindState("HideDoomish"); + if (HideDoomishState == NULL) + { + HideSpecialState = FindState("HideSpecial"); + } + } + + 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; + } + } + + + + //=========================================================================== + // + // AInventory :: ShouldRespawn + // + // Returns true if the item should hide itself and reappear later when picked + // up. + // + //=========================================================================== + + virtual bool ShouldRespawn () + { + if (bBigPowerup && !sv_respawnsuper) return false; + if (bNeverRespawn) return false; + return sv_itemrespawn || bAlwaysRespawn; + } + + //=========================================================================== + // + // 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. + // + //=========================================================================== + + protected bool GoAway () + { + // Dropped items never stick around + if (bDropped) + { + return false; + } + + if (!ShouldStay ()) + { + Hide (); + if (ShouldRespawn ()) + { + return true; + } + return false; + } + return true; + } + + //=========================================================================== + // + // 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. + // + //=========================================================================== + + protected void GoAwayAndDie () + { + if (!GoAway ()) + { + bSpecial = false; + SetStateLabel("HoldAndDestroy"); + } + } + //=========================================================================== // // AInventory :: ModifyDamage @@ -559,6 +747,61 @@ class Inventory : Actor native virtual bool GetNoTeleportFreeze() { return false; } virtual void AlterWeaponSprite(VisStyle vis, in out int changed) {} virtual void OwnerDied() {} + virtual Color GetBlend () { return 0; } + + //=========================================================================== + // + // AInventory :: DoPickupSpecial + // + // Executes this actor's special when it is picked up. + // + //=========================================================================== + + virtual void DoPickupSpecial (Actor toucher) + { + if (special) + { + toucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]); + special = 0; + } + } + //=========================================================================== + // + // AInventory :: PlayPickupSound + // + //=========================================================================== + + virtual void PlayPickupSound (Actor toucher) + { + double atten; + int chan; + + if (bNoAttenPickupSound) + { + atten = ATTN_NONE; + } + /* + else if ((ItemFlags & IF_FANCYPICKUPSOUND) && + (toucher == NULL || toucher->CheckLocalView(consoeplayer))) + { + atten = ATTN_NONE; + } + */ + else + { + atten = ATTN_NORM; + } + + if (toucher != NULL && toucher.CheckLocalView(consoleplayer)) + { + chan = CHAN_PICKUP|CHAN_NOPAUSE; + } + else + { + chan = CHAN_PICKUP; + } + toucher.A_PlaySound(PickupSound, chan, 1, false, atten); + } //=========================================================================== // @@ -602,11 +845,135 @@ class Inventory : Actor native } -class DehackedPickup : Inventory native +//=========================================================================== +// +// +// +//=========================================================================== + +class DehackedPickup : Inventory { + Inventory RealPickup; + bool droppedbymonster; + + private native class DetermineType(); + + override bool TryPickup (in out Actor toucher) + { + let type = DetermineType (); + if (type == NULL) + { + return false; + } + RealPickup = Inventory(Spawn (type, Pos, NO_REPLACE)); + if (RealPickup != NULL) + { + // The internally spawned item should never count towards statistics. + RealPickup.ClearCounters(); + if (!bDropped) + { + RealPickup.bDropped = false; + } + // If this item has been dropped by a monster the + // amount of ammo this gives must be adjusted. + if (droppedbymonster) + { + RealPickup.ModifyDropAmount(0); + } + if (!RealPickup.CallTryPickup (toucher)) + { + RealPickup.Destroy (); + RealPickup = NULL; + return false; + } + GoAwayAndDie (); + return true; + } + return false; + } + + override String PickupMessage () + { + if (RealPickup != null) + return RealPickup.PickupMessage (); + else return ""; + } + + override bool ShouldStay () + { + if (RealPickup != null) + return RealPickup.ShouldStay (); + else return true; + } + + override bool ShouldRespawn () + { + if (RealPickup != null) + return RealPickup.ShouldRespawn (); + else return false; + } + + override void PlayPickupSound (Actor toucher) + { + if (RealPickup != null) + RealPickup.PlayPickupSound (toucher); + } + + override void DoPickupSpecial (Actor toucher) + { + Super.DoPickupSpecial (toucher); + // If the real pickup hasn't joined the toucher's inventory, make sure it + // doesn't stick around. + if (RealPickup != null && RealPickup.Owner != toucher) + { + RealPickup.Destroy (); + } + RealPickup = null; + } + + override void OnDestroy () + { + if (RealPickup != null) + { + RealPickup.Destroy (); + RealPickup = null; + } + Super.OnDestroy(); + } } -class FakeInventory : Inventory native +//=========================================================================== +// +// +// +//=========================================================================== + +class FakeInventory : Inventory { - native bool Respawnable; + bool Respawnable; + + property respawns: Respawnable; + + override bool ShouldRespawn () + { + return Respawnable && Super.ShouldRespawn(); + } + + override bool TryPickup (in out Actor toucher) + { + let success = toucher.A_CallSpecial(special, args[0], args[1], args[2], args[3], args[4]); + + if (success) + { + GoAwayAndDie (); + return true; + } + return false; + } + + override void DoPickupSpecial (Actor toucher) + { + // The special was already executed by TryPickup, so do nothing here + } + }