From 1750ded7c45ca2bc199bc59d7be51ad19bc3ed98 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 19 Jan 2017 20:56:31 +0100 Subject: [PATCH 1/2] - more exporting of AInventory. --- src/b_bot.cpp | 2 + src/d_dehacked.cpp | 3 +- src/g_inventory/a_pickups.cpp | 367 ++---------------- src/g_inventory/a_pickups.h | 29 +- src/g_inventory/a_weapons.cpp | 2 - src/p_actionfunctions.cpp | 6 +- src/p_enemy.cpp | 17 +- src/p_local.h | 2 - src/p_mobj.cpp | 23 +- src/scripting/decorate/olddecorations.cpp | 2 +- src/scripting/thingdef_properties.cpp | 2 +- wadsrc/static/zscript/inventory/inventory.txt | 225 ++++++++++- .../zscript/inventory/stateprovider.txt | 5 +- wadsrc/static/zscript/shared/botstuff.txt | 3 +- 14 files changed, 291 insertions(+), 397 deletions(-) diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 3f4d0c592..54d154a3e 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -26,6 +26,8 @@ IMPLEMENT_POINTERS_START(DBot) IMPLEMENT_POINTER(last_mate) IMPLEMENT_POINTERS_END +DEFINE_FIELD(DBot, dest) + DBot::DBot () : DThinker(STAT_BOT) { diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index 85d10de02..eda79f744 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -3158,9 +3158,10 @@ bool ADehackedPickup::TryPickup (AActor *&toucher) FString ADehackedPickup::PickupMessage () { + /* if (RealPickup != nullptr) return RealPickup->PickupMessage (); - else return ""; + else*/ return ""; } bool ADehackedPickup::ShouldStay () diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index c65961de1..b3826d46f 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -39,7 +39,7 @@ void PClassInventory::DeriveData(PClass *newclass) Super::DeriveData(newclass); PClassInventory *newc = static_cast(newclass); - newc->PickupMessage = PickupMessage; + newc->PickupMsg = PickupMsg; newc->GiveQuest = GiveQuest; newc->AltHUDIcon = AltHUDIcon; newc->ForbiddenToPlayerClass = ForbiddenToPlayerClass; @@ -79,19 +79,16 @@ void PClassInventory::Finalize(FStateDefinitions &statedef) ((AActor*)Defaults)->flags |= MF_SPECIAL; } -int AInventory::StaticLastMessageTic; -FString AInventory::StaticLastMessage; - IMPLEMENT_CLASS(AInventory, false, true) IMPLEMENT_POINTERS_START(AInventory) - IMPLEMENT_POINTER(Owner) +IMPLEMENT_POINTER(Owner) IMPLEMENT_POINTERS_END DEFINE_FIELD_BIT(AInventory, ItemFlags, bPickupGood, IF_PICKUPGOOD) DEFINE_FIELD_BIT(AInventory, ItemFlags, bCreateCopyMoved, IF_CREATECOPYMOVED) DEFINE_FIELD_BIT(AInventory, ItemFlags, bInitEffectFailed, IF_INITEFFECTFAILED) -DEFINE_FIELD(AInventory, Owner) +DEFINE_FIELD(AInventory, Owner) DEFINE_FIELD(AInventory, Amount) DEFINE_FIELD(AInventory, MaxAmount) DEFINE_FIELD(AInventory, InterHubAmount) @@ -101,6 +98,7 @@ DEFINE_FIELD(AInventory, DropTime) DEFINE_FIELD(AInventory, SpawnPointClass) DEFINE_FIELD(AInventory, PickupFlash) DEFINE_FIELD(AInventory, PickupSound) +DEFINE_FIELD(PClassInventory, PickupMsg) //=========================================================================== // @@ -179,42 +177,6 @@ void AInventory::MarkPrecacheSounds() const PickupSound.MarkUsed(); } -//=========================================================================== -// -// AInventory :: SpecialDropAction -// -// Called by P_DropItem. Return true to prevent the standard drop tossing. -// A few Strife items that are meant to trigger actions rather than be -// picked up use this. Normal items shouldn't need it. -// -//=========================================================================== - -bool AInventory::SpecialDropAction (AActor *dropper) -{ - return false; -} - -DEFINE_ACTION_FUNCTION(AInventory, SpecialDropAction) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_OBJECT_NOT_NULL(dropper, AActor); - ACTION_RETURN_BOOL(self->SpecialDropAction(dropper)); -} - -bool AInventory::CallSpecialDropAction(AActor *dropper) -{ - IFVIRTUAL(AInventory, SpecialDropAction) - { - VMValue params[2] = { (DObject*)this, (DObject*)dropper }; - VMReturn ret; - int retval; - ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); - return !!retval; - } - return SpecialDropAction(dropper); -} - //=========================================================================== // // AInventory :: ShouldRespawn @@ -231,18 +193,6 @@ bool AInventory::ShouldRespawn () return !!((dmflags & DF_ITEMS_RESPAWN) || (ItemFlags & IF_ALWAYSRESPAWN)); } -//=========================================================================== -// -// AInventory :: BeginPlay -// -//=========================================================================== - -void AInventory::BeginPlay () -{ - Super::BeginPlay (); - flags |= MF_DROPPED; // [RH] Items are dropped by default -} - //=========================================================================== // // AInventory :: Grind @@ -270,68 +220,6 @@ bool AInventory::Grind(bool items) return Super::Grind(items); } -//=========================================================================== -// -// AInventory :: DoEffect -// -// Handles any effect an item might apply to its owner -// Normally only used by subclasses of Powerup -// -//=========================================================================== - -void AInventory::DoEffect() -{ - IFVIRTUAL(AInventory, DoEffect) - { - VMValue params[1] = { (DObject*)this }; - VMFrameStack stack; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); - } -} - - -//=========================================================================== -// -// AInventory :: HandlePickup -// -// Returns true if the pickup was handled (or should not happen at all), -// false if not. -// -//=========================================================================== - -bool AInventory::HandlePickup (AInventory *item) -{ - if (item->GetClass() == GetClass()) - { - if (Amount < MaxAmount || (sv_unlimited_pickup && !item->CallShouldStay())) - { - if (Amount > 0 && Amount + item->Amount < 0) - { - Amount = 0x7fffffff; - } - else - { - Amount += item->Amount; - } - - if (Amount > MaxAmount && !sv_unlimited_pickup) - { - Amount = MaxAmount; - } - item->ItemFlags |= IF_PICKUPGOOD; - } - return true; - } - return false; -} - -DEFINE_ACTION_FUNCTION(AInventory, HandlePickup) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_OBJECT_NOT_NULL(item, AInventory); - ACTION_RETURN_BOOL(self->HandlePickup(item)); -} - //=========================================================================== // // AInventory :: GoAway @@ -392,67 +280,6 @@ DEFINE_ACTION_FUNCTION(AInventory, GoAwayAndDie) return 0; } - -//=========================================================================== -// -// AInventory :: CreateCopy -// -// Returns an actor suitable for placing in an inventory, either itself or -// a copy based on whether it needs to respawn or not. Returning NULL -// indicates the item should not be picked up. -// -//=========================================================================== - -AInventory *AInventory::CreateCopy (AActor *other) -{ - AInventory *copy; - - Amount = MIN(Amount, MaxAmount); - if (GoAway ()) - { - copy = static_cast(Spawn (GetClass())); - copy->Amount = Amount; - copy->MaxAmount = MaxAmount; - } - else - { - copy = this; - } - return copy; -} - -DEFINE_ACTION_FUNCTION(AInventory, CreateCopy) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_OBJECT(other, AActor); - ACTION_RETURN_OBJECT(self->CreateCopy(other)); -} - - -//=========================================================================== -// -// AInventory::CreateTossable -// -// Creates a copy of the item suitable for dropping. If this actor embodies -// only one item, then it is tossed out itself. Otherwise, the count drops -// by one and a new item with an amount of 1 is spawned. -// -//=========================================================================== - -AInventory *AInventory::CreateTossable() -{ - IFVIRTUAL(AInventory, CreateTossable) - { - VMValue params[1] = { (DObject*)this }; - VMReturn ret; - AInventory *retval; - ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); - return retval; - } - else return CreateTossable(); -} - //=========================================================================== // // AInventory :: BecomeItem @@ -518,30 +345,6 @@ DEFINE_ACTION_FUNCTION(AInventory, BecomePickup) return 0; } -//=========================================================================== -// -// AInventory :: ModifyDamage -// -// Allows inventory items to manipulate the amount of damage -// inflicted. Damage is the amount of damage that would be done without manipulation, -// and newdamage is the amount that should be done after the item has changed -// it. -// 'active' means it is called by the inflictor, 'passive' by the target. -// It may seem that this is redundant and AbsorbDamage is the same. However, -// AbsorbDamage is called only for players and also depends on other settings -// which are undesirable for a protection artifact. -// -//=========================================================================== - -void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) -{ - IFVIRTUAL(AInventory, ModifyDamage) - { - VMValue params[5] = { (DObject*)this, damage, int(damageType), &newdamage, passive }; - GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr); - } -} - //=========================================================================== // // AInventory :: GetSpeedFactor @@ -556,10 +359,9 @@ double AInventory::GetSpeedFactor() { IFVIRTUALPTR(self, AInventory, GetSpeedFactor) { - VMValue params[2] = { (DObject*)self }; - VMReturn ret; + VMValue params[1] = { (DObject*)self }; double retval; - ret.FloatAt(&retval); + VMReturn ret(&retval); GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); factor *= retval; } @@ -581,10 +383,9 @@ bool AInventory::GetNoTeleportFreeze () { IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze) { - VMValue params[2] = { (DObject*)self }; - VMReturn ret; + VMValue params[1] = { (DObject*)self }; int retval; - ret.IntAt(&retval); + VMReturn ret(&retval); GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); if (retval) return true; } @@ -610,6 +411,7 @@ bool AInventory::CallUse(bool pickup) GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return !!retval; } + return false; } @@ -669,100 +471,25 @@ void AInventory::Hide () // // //=========================================================================== +static int StaticLastMessageTic; +static FString StaticLastMessage; -static void PrintPickupMessage (const char *str) +DEFINE_ACTION_FUNCTION(AInventory, PrintPickupMessage) { - if (str != NULL) + PARAM_PROLOGUE; + PARAM_BOOL(localview); + PARAM_STRING(str); + if (str.IsNotEmpty() && localview && (StaticLastMessageTic != gametic || StaticLastMessage.Compare(str))) { - if (str[0]=='$') - { - str=GStrings(str+1); - } - if (str[0] != 0) Printf (PRINT_LOW, "%s\n", str); - } -} - -//=========================================================================== -// -// AInventory :: Touch -// -// Handles collisions from another actor, possible adding itself to the -// collider's inventory. -// -//=========================================================================== - -void AInventory::Touch (AActor *toucher) -{ - player_t *player = toucher->player; - - // If a voodoo doll touches something, pretend the real player touched it instead. - if (player != NULL) - { - toucher = player->mo; - } - - bool localview = toucher->CheckLocalView(consoleplayer); - - if (!CallTryPickup (toucher, &toucher)) return; - - // This is the only situation when a pickup flash should ever play. - if (PickupFlash != NULL && !CallShouldStay()) - { - Spawn(PickupFlash, Pos(), ALLOW_REPLACE); - } - - if (!(ItemFlags & IF_QUIET)) - { - FString message = GetPickupMessage (); - - if (message.IsNotEmpty() && localview - && (StaticLastMessageTic != gametic || StaticLastMessage.Compare(message))) - { - StaticLastMessageTic = gametic; - StaticLastMessage = message; - PrintPickupMessage (message); - StatusBar->FlashCrosshair (); - } - - // Special check so voodoo dolls picking up items cause the - // real player to make noise. - if (player != NULL) - { - CallPlayPickupSound (player->mo); - if (!(ItemFlags & IF_NOSCREENFLASH)) - { - player->bonuscount = BONUSADD; - } - } - else - { - CallPlayPickupSound (toucher); - } - } - - // [RH] Execute an attached special (if any) - DoPickupSpecial (toucher); - - if (flags & MF_COUNTITEM) - { - if (player != NULL) - { - player->itemcount++; - } - level.found_items++; - } - - if (flags5 & MF5_COUNTSECRET) - { - P_GiveSecret(player != NULL? (AActor*)player->mo : toucher, true, true, -1); - } - - //Added by MC: Check if item taken was the roam destination of any bot - for (int i = 0; i < MAXPLAYERS; i++) - { - if (players[i].Bot != NULL && this == players[i].Bot->dest) - players[i].Bot->dest = NULL; + StaticLastMessageTic = gametic; + StaticLastMessage = str; + const char *pstr = str.GetChars(); + + if (pstr[0] == '$') pstr = GStrings(pstr + 1); + if (pstr[0] != 0) Printf(PRINT_LOW, "%s\n", pstr); + StatusBar->FlashCrosshair(); } + return 0; } //=========================================================================== @@ -783,37 +510,12 @@ void AInventory::DoPickupSpecial (AActor *toucher) } } -//=========================================================================== -// -// AInventory :: PickupMessage -// -// Returns the message to print when this actor is picked up. -// -//=========================================================================== - -FString AInventory::PickupMessage () -{ - return GetClass()->PickupMessage; -} - -DEFINE_ACTION_FUNCTION(AInventory, PickupMessage) +DEFINE_ACTION_FUNCTION(AInventory, DoPickupSpecial) { PARAM_SELF_PROLOGUE(AInventory); - ACTION_RETURN_STRING(self->PickupMessage()); -} - -FString AInventory::GetPickupMessage() -{ - IFVIRTUAL(AInventory, PickupMessage) - { - VMValue params[1] = { (DObject*)this }; - VMReturn ret; - FString retval; - ret.StringAt(&retval); - GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); - return retval; - } - else return PickupMessage(); + PARAM_OBJECT(toucher, AActor); + self->DoPickupSpecial(toucher); + return 0; } //=========================================================================== @@ -881,17 +583,6 @@ void AInventory::CallPlayPickupSound(AActor *other) // //=========================================================================== -bool AInventory::ShouldStay () -{ - return false; -} - -DEFINE_ACTION_FUNCTION(AInventory, ShouldStay) -{ - PARAM_SELF_PROLOGUE(AInventory); - ACTION_RETURN_BOOL(self->ShouldStay()); -} - bool AInventory::CallShouldStay() { IFVIRTUAL(AInventory, ShouldStay) @@ -903,7 +594,7 @@ bool AInventory::CallShouldStay() GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); return !!retval; } - else return ShouldStay(); + return false; } diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 4701d60e6..4c5094ca6 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -59,7 +59,7 @@ public: virtual size_t PointerSubstitution(DObject *oldclass, DObject *newclass); void Finalize(FStateDefinitions &statedef); - FString PickupMessage; + FString PickupMsg; int GiveQuest; // Optionally give one of the quest items. FTextureID AltHUDIcon; TArray RestrictedToPlayerClass; @@ -72,10 +72,8 @@ class AInventory : public AActor HAS_OBJECT_POINTERS public: - virtual void Touch (AActor *toucher) override; virtual void Serialize(FSerializer &arc) override; virtual void MarkPrecacheSounds() const override; - virtual void BeginPlay () override; virtual void OnDestroy() override; virtual void Tick() override; virtual bool Grind(bool items) override; @@ -86,39 +84,20 @@ public: virtual bool ShouldRespawn (); virtual void DoPickupSpecial (AActor *toucher); - // methods that can be overridden by scripts, plus their callers. - virtual bool SpecialDropAction (AActor *dropper); - bool CallSpecialDropAction(AActor *dropper); - bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // This wraps both virtual methods plus a few more checks. - virtual AInventory *CreateCopy(AActor *other); - - AInventory *CreateTossable(); - - virtual FString PickupMessage(); - FString GetPickupMessage(); - - virtual bool HandlePickup(AInventory *item); - bool CallUse(bool pickup); virtual PalEntry GetBlend(); PalEntry CallGetBlend(); - virtual bool ShouldStay(); bool CallShouldStay(); - void DoEffect(); - virtual void PlayPickupSound(AActor *toucher); void CallPlayPickupSound(AActor *toucher); void CallAttachToOwner(AActor *other); - // still need to be done. - void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive); - // virtual on the script side only. double GetSpeedFactor(); bool GetNoTeleportFreeze(); @@ -136,8 +115,6 @@ public: AInventory *PrevInv(); // Returns the previous item with IF_INVBAR set. AInventory *NextInv(); // Returns the next item with IF_INVBAR set. - bool CallStateChain(AActor *actor, FState *state); - TObjPtr Owner; // Who owns this item? NULL if it's still a pickup. int Amount; // Amount of item this instance has int MaxAmount; // Max amount of item this instance can have @@ -158,13 +135,13 @@ protected: void GiveQuest(AActor * toucher); private: - static int StaticLastMessageTic; - static FString StaticLastMessage; }; class AStateProvider : public AInventory { DECLARE_CLASS (AStateProvider, AInventory) +public: + bool CallStateChain(AActor *actor, FState *state); }; extern PClassActor *QuestItemClasses[31]; diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index bf7f04928..24661dfd1 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -56,8 +56,6 @@ #include "virtual.h" -#define BONUSADD 6 - extern FFlagDef WeaponFlagDefs[]; IMPLEMENT_CLASS(AWeapon, false, true) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index f3e1a0c8d..65f7be9a1 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -108,7 +108,7 @@ static FRandom pr_bfgselfdamage("BFGSelfDamage"); // //========================================================================== -bool AInventory::CallStateChain (AActor *actor, FState *state) +bool AStateProvider::CallStateChain (AActor *actor, FState *state) { INTBOOL result = false; int counter = 0; @@ -223,9 +223,9 @@ bool AInventory::CallStateChain (AActor *actor, FState *state) return !!result; } -DEFINE_ACTION_FUNCTION(AInventory, CallStateChain) +DEFINE_ACTION_FUNCTION(ACustomInventory, CallStateChain) { - PARAM_SELF_PROLOGUE(AInventory); + PARAM_SELF_PROLOGUE(AStateProvider); PARAM_OBJECT(affectee, AActor); PARAM_STATE(state); ACTION_RETURN_BOOL(self->CallStateChain(affectee, state)); diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index bb90e976f..e42f9d08f 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -52,6 +52,7 @@ #include "p_checkposition.h" #include "math/cmath.h" #include "g_levellocals.h" +#include "virtual.h" #include "gi.h" @@ -3312,11 +3313,19 @@ AInventory *P_DropItem (AActor *source, PClassActor *type, int dropamount, int c AInventory *inv = static_cast(mo); ModifyDropAmount(inv, dropamount); inv->ItemFlags |= IF_TOSSED; - if (inv->CallSpecialDropAction (source)) + + IFVIRTUALPTR(inv, AInventory, SpecialDropAction) { - // The special action indicates that the item should not spawn - inv->Destroy(); - return NULL; + VMValue params[2] = { inv, source }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + if (retval) + { + // The special action indicates that the item should not spawn + inv->Destroy(); + return NULL; + } } return inv; } diff --git a/src/p_local.h b/src/p_local.h index 8ebdf9fae..d50d2be52 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -48,8 +48,6 @@ struct FLinePortal; #define STEEPSLOPE (46342/65536.) // [RH] Minimum floorplane.c value for walking -#define BONUSADD 6 - // Inspired by Maes extern int bmapnegx; extern int bmapnegy; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 6691a1d0e..c3bbcf076 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1038,12 +1038,14 @@ DEFINE_ACTION_FUNCTION(AActor, UseInventory) AInventory *AActor::DropInventory (AInventory *item) { - AInventory *drop = item->CreateTossable (); - - if (drop == NULL) + AInventory *drop = nullptr; + IFVIRTUALPTR(item, AInventory, CreateTossable) { - return NULL; + VMValue params[1] = { (DObject*)this }; + VMReturn ret((void**)&drop); + GlobalVMStack.Call(func, params, 1, &ret, 1, nullptr); } + if (drop == nullptr) return NULL; drop->SetOrigin(PosPlusZ(10.), false); drop->Angles.Yaw = Angles.Yaw; drop->VelFromAngle(5.); @@ -3945,7 +3947,11 @@ void AActor::Tick () // by the order in the inventory, not the order in the thinker table while (item != NULL && item->Owner == this) { - item->DoEffect(); + IFVIRTUALPTR(item, AInventory, DoEffect) + { + VMValue params[1] = { item }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } item = item->Inventory; } @@ -7592,10 +7598,13 @@ int AActor::GetModifiedDamage(FName damagetype, int damage, bool passive) auto inv = Inventory; while (inv != nullptr) { - inv->ModifyDamage(damage, damagetype, damage, passive); + IFVIRTUALPTR(inv, AInventory, ModifyDamage) + { + VMValue params[5] = { (DObject*)inv, damage, int(damagetype), &damage, passive }; + GlobalVMStack.Call(func, params, 5, nullptr, 0, nullptr); + } inv = inv->Inventory; } - return damage; } diff --git a/src/scripting/decorate/olddecorations.cpp b/src/scripting/decorate/olddecorations.cpp index 400444340..754d6deef 100644 --- a/src/scripting/decorate/olddecorations.cpp +++ b/src/scripting/decorate/olddecorations.cpp @@ -584,7 +584,7 @@ static void ParseInsideDecoration (Baggage &bag, AActor *defaults, else if (def == DEF_Pickup && sc.Compare ("PickupMessage")) { sc.MustGetString (); - static_cast(bag.Info)->PickupMessage = sc.String; + static_cast(bag.Info)->PickupMsg = sc.String; } else if (def == DEF_Pickup && sc.Compare ("Respawns")) { diff --git a/src/scripting/thingdef_properties.cpp b/src/scripting/thingdef_properties.cpp index 6cb482ed8..11e5dc96d 100644 --- a/src/scripting/thingdef_properties.cpp +++ b/src/scripting/thingdef_properties.cpp @@ -1802,7 +1802,7 @@ DEFINE_CLASS_PROPERTY(pickupmessage, T, Inventory) { PROP_STRING_PARM(str, 0); assert(info->IsKindOf(RUNTIME_CLASS(PClassInventory))); - static_cast(info)->PickupMessage = str; + static_cast(info)->PickupMsg = str; } //========================================================================== diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index d798dba26..fced80e24 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -8,6 +8,7 @@ struct VisStyle class Inventory : Actor native { const BLINKTHRESHOLD = (4*32); + const BONUSADD = 6; native Actor Owner; // Who owns self item? NULL if it's still a pickup. native int Amount; // Amount of item self instance has @@ -22,6 +23,7 @@ class Inventory : Actor native native bool bPickupGood; native bool bCreateCopyMoved; native bool bInitEffectFailed; + native meta String PickupMsg; Default { @@ -34,12 +36,8 @@ class Inventory : Actor native } virtual native color GetBlend (); - virtual native bool HandlePickup(Inventory item); - virtual native Inventory CreateCopy(Actor other); - virtual native bool SpecialDropAction (Actor dropper); - virtual native String PickupMessage(); - virtual native bool ShouldStay(); virtual native void PlayPickupSound(Actor user); + virtual native void DoPickupSpecial (Actor toucher); native bool DoRespawn(); native bool GoAway(); @@ -50,7 +48,7 @@ class Inventory : Actor native // 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 bool CallStateChain (Actor actor, State state); + native static void PrintPickupMessage (bool localview, String str); States(Actor, Overlay, Weapon, Item) { @@ -74,8 +72,18 @@ class Inventory : Actor native Stop; } - // These are regular functions for the item itself. - + //=========================================================================== + // + // AInventory :: BeginPlay + // + //=========================================================================== + + override void BeginPlay () + { + Super.BeginPlay (); + bDropped = true; // [RH] Items are dropped by default + } + //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing1 @@ -132,6 +140,69 @@ class Inventory : Actor native } + //=========================================================================== + // + // AInventory :: CreateCopy + // + // Returns an actor suitable for placing in an inventory, either itself or + // a copy based on whether it needs to respawn or not. Returning NULL + // indicates the item should not be picked up. + // + //=========================================================================== + + virtual Inventory CreateCopy (Actor other) + { + Inventory copy; + + Amount = MIN(Amount, MaxAmount); + if (GoAway ()) + { + copy = Inventory(Spawn (GetClass())); + copy.Amount = Amount; + copy.MaxAmount = MaxAmount; + } + else + { + copy = self; + } + return copy; + } + + //=========================================================================== + // + // AInventory :: HandlePickup + // + // Returns true if the pickup was handled (or should not happen at all), + // false if not. + // + //=========================================================================== + + virtual bool HandlePickup (Inventory item) + { + if (item.GetClass() == GetClass()) + { + if (Amount < MaxAmount || (sv_unlimited_pickup && !item.ShouldStay())) + { + if (Amount > 0 && Amount + item.Amount < 0) + { + Amount = 0x7fffffff; + } + else + { + Amount += item.Amount; + } + + if (Amount > MaxAmount && !sv_unlimited_pickup) + { + Amount = MaxAmount; + } + item.bPickupGood = true; + } + return true; + } + return false; + } + //=========================================================================== // // AInventory :: CallHandlePickup @@ -236,6 +307,19 @@ class Inventory : Actor native return true; } + //=========================================================================== + // + // AInventory :: ShouldStay + // + // Returns true if the item should not disappear, even temporarily. + // + //=========================================================================== + + virtual bool ShouldStay () + { + return false; + } + //=========================================================================== // // AInventory :: TryPickupRestricted @@ -313,6 +397,97 @@ class Inventory : Actor native return copy; } + + //=========================================================================== + // + // AInventory :: PickupMessage + // + // Returns the message to print when this actor is picked up. + // + //=========================================================================== + + virtual String PickupMessage () + { + return PickupMsg; + } + + //=========================================================================== + // + // AInventory :: Touch + // + // Handles collisions from another actor, possible adding itself to the + // collider's inventory. + // + //=========================================================================== + + override void Touch (Actor toucher) + { + let player = toucher.player; + + // If a voodoo doll touches something, pretend the real player touched it instead. + if (player != NULL) + { + toucher = player.mo; + } + + bool localview = toucher.CheckLocalView(consoleplayer); + + bool res; + [res, toucher] = CallTryPickup(toucher); + if (!res) return; + + // This is the only situation when a pickup flash should ever play. + if (PickupFlash != NULL && !ShouldStay()) + { + Spawn(PickupFlash, Pos, ALLOW_REPLACE); + } + + if (!bQuiet) + { + PrintPickupMessage(localview, PickupMessage ()); + + // Special check so voodoo dolls picking up items cause the + // real player to make noise. + if (player != NULL) + { + PlayPickupSound (player.mo); + if (!bNoScreenFlash) + { + player.bonuscount = BONUSADD; + } + } + else + { + PlayPickupSound (toucher); + } + } + + // [RH] Execute an attached special (if any) + DoPickupSpecial (toucher); + + if (bCountItem) + { + if (player != NULL) + { + player.itemcount++; + } + level.found_items++; + } + + if (bCountSecret) + { + Actor ac = player != NULL? Actor(player.mo) : toucher; + ac.GiveSecret(true, true); + } + + //Added by MC: Check if item taken was the roam destination of any bot + for (int i = 0; i < MAXPLAYERS; i++) + { + if (players[i].Bot != NULL && self == players[i].Bot.dest) + players[i].Bot.dest = NULL; + } + } + //=========================================================================== // // AInventory :: DepleteOrDestroy @@ -361,11 +536,27 @@ class Inventory : Actor native virtual void DoEffect() {} + //=========================================================================== + // + // AInventory :: ModifyDamage + // + // Allows inventory items to manipulate the amount of damage + // inflicted. Damage is the amount of damage that would be done without manipulation, + // and newdamage is the amount that should be done after the item has changed + // it. + // 'active' means it is called by the inflictor, 'passive' by the target. + // It may seem that this is redundant and AbsorbDamage is the same. However, + // AbsorbDamage is called only for players and also depends on other settings + // which are undesirable for a protection artifact. + // + //=========================================================================== + + virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {} + virtual bool Use (bool pickup) { return false; } virtual double GetSpeedFactor() { return 1; } virtual bool GetNoTeleportFreeze() { return false; } - virtual void ModifyDamage(int damage, Name damageType, out int newdamage, bool passive) {} virtual void AlterWeaponSprite(VisStyle vis, in out int changed) {} virtual void OwnerDied() {} @@ -393,6 +584,22 @@ class Inventory : Actor native virtual void AbsorbDamage (int damage, Name damageType, out int newdamage) {} + //=========================================================================== + // + // AInventory :: SpecialDropAction + // + // Called by P_DropItem. Return true to prevent the standard drop tossing. + // A few Strife items that are meant to trigger actions rather than be + // picked up use this. Normal items shouldn't need it. + // + //=========================================================================== + + virtual bool SpecialDropAction (Actor dropper) + { + return false; + } + + } class DehackedPickup : Inventory native diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index 8f5571629..3f4f1fcda 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -13,7 +13,7 @@ class StateProvider : Inventory native action native void A_ResetReloadCounter(); } -class CustomInventory : StateProvider native +class CustomInventory : StateProvider { Default { @@ -36,7 +36,8 @@ class CustomInventory : StateProvider native deprecated action void A_Lower() {} deprecated action void A_Raise() {} deprecated action void A_CheckReload() {} - + native bool CallStateChain (Actor actor, State state); + //=========================================================================== // // ACustomInventory :: SpecialDropAction diff --git a/wadsrc/static/zscript/shared/botstuff.txt b/wadsrc/static/zscript/shared/botstuff.txt index 3ae2d1889..991e4876e 100644 --- a/wadsrc/static/zscript/shared/botstuff.txt +++ b/wadsrc/static/zscript/shared/botstuff.txt @@ -24,6 +24,7 @@ class CajunTrace : Actor } } -struct Bot native +class Bot native { + native Actor dest; } From 3c30b59babc5b01314cc1cea988bd8ff5d71b0a2 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 19 Jan 2017 23:42:12 +0100 Subject: [PATCH 2/2] 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 + } + }