diff --git a/src/fragglescript/t_func.cpp b/src/fragglescript/t_func.cpp index d3746a49e6..5aaa402e8c 100644 --- a/src/fragglescript/t_func.cpp +++ b/src/fragglescript/t_func.cpp @@ -2620,9 +2620,9 @@ void FParser::SF_MaxPlayerAmmo() if(amount < 0) amount = 0; if (!iammo) { - iammo = static_cast(Spawn (ammotype)); + players[playernum].mo->GiveAmmo(ammotype, 1); + iammo = players[playernum].mo->FindInventory(ammotype); iammo->Amount = 0; - iammo->AttachToOwner (players[playernum].mo); } iammo->MaxAmount = amount; diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp index 2a7f0b3bf0..c65961de1d 100644 --- a/src/g_inventory/a_pickups.cpp +++ b/src/g_inventory/a_pickups.cpp @@ -332,28 +332,6 @@ DEFINE_ACTION_FUNCTION(AInventory, HandlePickup) ACTION_RETURN_BOOL(self->HandlePickup(item)); } -bool AInventory::CallHandlePickup(AInventory *item) -{ - auto self = this; - while (self != nullptr) - { - IFVIRTUALPTR(self, AInventory, HandlePickup) - { - // Without the type cast this picks the 'void *' assignment... - VMValue params[2] = { (DObject*)self, (DObject*)item }; - VMReturn ret; - int retval; - ret.IntAt(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); - if (retval) return true; - } - else if (self->HandlePickup(item)) return true; - self = self->Inventory; - } - return false; -} - - //=========================================================================== // // AInventory :: GoAway @@ -450,20 +428,6 @@ DEFINE_ACTION_FUNCTION(AInventory, CreateCopy) ACTION_RETURN_OBJECT(self->CreateCopy(other)); } -AInventory *AInventory::CallCreateCopy(AActor *other) -{ - IFVIRTUAL(AInventory, CreateCopy) - { - VMValue params[2] = { (DObject*)this, (DObject*)other }; - VMReturn ret; - AInventory *retval; - ret.PointerAt((void**)&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); - return retval; - } - else return CreateCopy(other); -} - //=========================================================================== // @@ -475,47 +439,7 @@ AInventory *AInventory::CallCreateCopy(AActor *other) // //=========================================================================== -AInventory *AInventory::CreateTossable () -{ - AInventory *copy; - - // If this actor lacks a SpawnState, don't drop it. (e.g. A base weapon - // like the fist can't be dropped because you'll never see it.) - if (SpawnState == ::GetDefault()->SpawnState || - SpawnState == NULL) - { - return NULL; - } - if ((ItemFlags & (IF_UNDROPPABLE|IF_UNTOSSABLE)) || Owner == NULL || Amount <= 0) - { - return NULL; - } - if (Amount == 1 && !(ItemFlags & IF_KEEPDEPLETED)) - { - BecomePickup (); - DropTime = 30; - flags &= ~(MF_SPECIAL|MF_SOLID); - return this; - } - copy = static_cast(Spawn (GetClass(), Owner->Pos(), NO_REPLACE)); - if (copy != NULL) - { - copy->MaxAmount = MaxAmount; - copy->Amount = 1; - copy->DropTime = 30; - copy->flags &= ~(MF_SPECIAL|MF_SOLID); - Amount--; - } - return copy; -} - -DEFINE_ACTION_FUNCTION(AInventory, CreateTossable) -{ - PARAM_SELF_PROLOGUE(AInventory); - ACTION_RETURN_OBJECT(self->CreateTossable()); -} - -AInventory *AInventory::CallCreateTossable() +AInventory *AInventory::CreateTossable() { IFVIRTUAL(AInventory, CreateTossable) { @@ -594,30 +518,6 @@ DEFINE_ACTION_FUNCTION(AInventory, BecomePickup) return 0; } -//=========================================================================== -// -// AInventory :: AbsorbDamage -// -// Allows inventory items (primarily armor) to reduce the amount of damage -// taken. Damage is the amount of damage that would be done without armor, -// and newdamage is the amount that should be done after the armor absorbs -// it. -// -//=========================================================================== - -void AInventory::AbsorbDamage (int damage, FName damageType, int &newdamage) -{ -} - -DEFINE_ACTION_FUNCTION(AInventory, AbsorbDamage) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_INT(damage); - PARAM_NAME(type); - PARAM_POINTER(newdmg, int); - self->AbsorbDamage(damage, type, *newdmg); - return 0; -} //=========================================================================== // // AInventory :: ModifyDamage @@ -699,18 +599,6 @@ bool AInventory::GetNoTeleportFreeze () // //=========================================================================== -bool AInventory::Use (bool pickup) -{ - return false; -} - -DEFINE_ACTION_FUNCTION(AInventory, Use) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_BOOL(pickup); - ACTION_RETURN_BOOL(self->Use(pickup)); -} - bool AInventory::CallUse(bool pickup) { IFVIRTUAL(AInventory, Use) @@ -721,9 +609,7 @@ bool AInventory::CallUse(bool pickup) ret.IntAt(&retval); GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); return !!retval; - } - else return Use(pickup); } @@ -1153,28 +1039,6 @@ AInventory *AInventory::NextInv () return item; } -//=========================================================================== -// -// AInventory :: DrawPowerup -// -// Gives this item a chance to draw a special status indicator on the screen. -// Returns false if it didn't draw anything. -// -//=========================================================================== - -bool AInventory::DrawPowerup (int x, int y) -{ - return false; -} - -DEFINE_ACTION_FUNCTION(AInventory, DrawPowerup) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_INT(x); - PARAM_INT(y); - ACTION_RETURN_BOOL(self->DrawPowerup(x, y)); -} - //=========================================================================== // // AInventory :: DoRespawn @@ -1219,117 +1083,6 @@ void AInventory::GiveQuest (AActor *toucher) } } -//=========================================================================== -// -// AInventory :: TryPickup -// -//=========================================================================== - -bool AInventory::TryPickup (AActor *&toucher) -{ - AActor *newtoucher = toucher; // in case changed by the powerup - - // If HandlePickup() returns true, it will set the IF_PICKUPGOOD flag - // to indicate that this item has been picked up. If the item cannot be - // picked up, then it leaves the flag cleared. - - ItemFlags &= ~IF_PICKUPGOOD; - if (toucher->Inventory != NULL && toucher->Inventory->CallHandlePickup (this)) - { - // Let something else the player is holding intercept the pickup. - if (!(ItemFlags & IF_PICKUPGOOD)) - { - return false; - } - ItemFlags &= ~IF_PICKUPGOOD; - GoAwayAndDie (); - } - else if (MaxAmount > 0) - { - // Add the item to the inventory. It is not already there, or HandlePickup - // would have already taken care of it. - AInventory *copy = CallCreateCopy (toucher); - if (copy == NULL) - { - return false; - } - // Some powerups cannot activate absolutely, for - // example, PowerMorph; fail the pickup if so. - if (copy->ItemFlags & IF_INITEFFECTFAILED) - { - if (copy != this) copy->Destroy(); - else ItemFlags &= ~IF_INITEFFECTFAILED; - return false; - } - // Handle owner-changing powerups - if (copy->ItemFlags & IF_CREATECOPYMOVED) - { - newtoucher = copy->Owner; - copy->Owner = NULL; - copy->ItemFlags &= ~IF_CREATECOPYMOVED; - } - // Continue onwards with the rest - copy->CallAttachToOwner (newtoucher); - if (ItemFlags & IF_AUTOACTIVATE) - { - if (copy->CallUse (true)) - { - if (--copy->Amount <= 0) - { - copy->flags &= ~MF_SPECIAL; - copy->SetState (copy->FindState("HoldAndDestroy")); - } - } - } - } - else if (ItemFlags & IF_AUTOACTIVATE) - { - // Special case: If an item's MaxAmount is 0, you can still pick it - // up if it is autoactivate-able. - - // The item is placed in the inventory just long enough to be used. - toucher->AddInventory(this); - bool usegood = CallUse(true); - toucher->RemoveInventory(this); - - if (usegood) - { - GoAwayAndDie(); - } - else - { - return false; - } - } - return true; -} - -DEFINE_ACTION_FUNCTION(AInventory, TryPickup) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_POINTER_NOT_NULL(toucher, AActor*); - ACTION_RETURN_BOOL(self->TryPickup(*toucher)); -} - -//=========================================================================== -// -// AInventory :: TryPickupRestricted -// -//=========================================================================== - -bool AInventory::TryPickupRestricted (AActor *&toucher) -{ - return false; -} - -DEFINE_ACTION_FUNCTION(AInventory, TryPickupRestricted) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_POINTER_NOT_NULL(toucher, AActor*); - ACTION_RETURN_BOOL(self->TryPickupRestricted(*toucher)); -} - - //=========================================================================== // // AInventory :: CallTryPickup @@ -1355,7 +1108,6 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return) GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); res = !!retval; } - else res = TryPickup(toucher); } else if (!(ItemFlags & IF_RESTRICTABSOLUTELY)) { @@ -1369,7 +1121,6 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return) GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); res = !!retval; } - else res = TryPickupRestricted(toucher); } else return false; @@ -1502,20 +1253,6 @@ CCMD (targetinv) // //=========================================================================== -void AInventory::AttachToOwner (AActor *other) -{ - BecomeItem (); - other->AddInventory (this); -} - -DEFINE_ACTION_FUNCTION(AInventory, AttachToOwner) -{ - PARAM_SELF_PROLOGUE(AInventory); - PARAM_OBJECT_NOT_NULL(other, AActor); - self->AttachToOwner(other); - return 0; -} - void AInventory::CallAttachToOwner(AActor *other) { IFVIRTUAL(AInventory, AttachToOwner) @@ -1523,87 +1260,13 @@ void AInventory::CallAttachToOwner(AActor *other) VMValue params[2] = { (DObject*)this, (DObject*)other }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } - else AttachToOwner(other); } -//=========================================================================== -// -// AInventory :: DetachFromOwner -// -// Performs any special work needed when the item leaves an inventory, -// either through destruction or becoming a pickup. -// -//=========================================================================== - -void AInventory::DetachFromOwner () -{ -} - -DEFINE_ACTION_FUNCTION(AInventory, DetachFromOwner) -{ - PARAM_SELF_PROLOGUE(AInventory); - self->DetachFromOwner(); - return 0; -} - -void AInventory::CallDetachFromOwner() -{ - IFVIRTUAL(AInventory, DetachFromOwner) - { - VMValue params[1] = { (DObject*)this }; - GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); - } - else DetachFromOwner(); -} - //=========================================================================== //=========================================================================== IMPLEMENT_CLASS(AStateProvider, false, false) -IMPLEMENT_CLASS(ACustomInventory, false, false) -//=========================================================================== -// -// ACustomInventory :: SpecialDropAction -// -//=========================================================================== - -bool ACustomInventory::SpecialDropAction (AActor *dropper) -{ - return CallStateChain (dropper, FindState(NAME_Drop)); -} - -//=========================================================================== -// -// ACustomInventory :: Use -// -//=========================================================================== - -bool ACustomInventory::Use (bool pickup) -{ - return CallStateChain (Owner, FindState(NAME_Use)); -} - -//=========================================================================== -// -// ACustomInventory :: TryPickup -// -//=========================================================================== - -bool ACustomInventory::TryPickup (AActor *&toucher) -{ - FState *pickupstate = FindState(NAME_Pickup); - bool useok = CallStateChain (toucher, pickupstate); - if ((useok || pickupstate == NULL) && FindState(NAME_Use) != NULL) - { - useok = Super::TryPickup (toucher); - } - else if (useok) - { - GoAwayAndDie(); - } - return useok; -} diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h index 4ad0e66b8e..4701d60e6e 100644 --- a/src/g_inventory/a_pickups.h +++ b/src/g_inventory/a_pickups.h @@ -90,23 +90,17 @@ public: virtual bool SpecialDropAction (AActor *dropper); bool CallSpecialDropAction(AActor *dropper); - virtual bool TryPickup(AActor *&toucher); - virtual bool TryPickupRestricted(AActor *&toucher); bool CallTryPickup(AActor *toucher, AActor **toucher_return = NULL); // This wraps both virtual methods plus a few more checks. virtual AInventory *CreateCopy(AActor *other); - AInventory *CallCreateCopy(AActor *other); - virtual AInventory *CreateTossable(); - AInventory *CallCreateTossable(); + AInventory *CreateTossable(); virtual FString PickupMessage(); FString GetPickupMessage(); virtual bool HandlePickup(AInventory *item); - bool CallHandlePickup(AInventory *item); - virtual bool Use(bool pickup); bool CallUse(bool pickup); virtual PalEntry GetBlend(); @@ -120,20 +114,11 @@ public: virtual void PlayPickupSound(AActor *toucher); void CallPlayPickupSound(AActor *toucher); - virtual void AttachToOwner(AActor *other); void CallAttachToOwner(AActor *other); - virtual void DetachFromOwner(); - void CallDetachFromOwner(); - // still need to be done. - virtual void AbsorbDamage(int damage, FName damageType, int &newdamage); void ModifyDamage(int damage, FName damageType, int &newdamage, bool passive); - // visual stuff is for later. Right now the VM has not yet access to the needed functionality. - virtual bool DrawPowerup(int x, int y); - - // virtual on the script side only. double GetSpeedFactor(); bool GetNoTeleportFreeze(); @@ -151,6 +136,8 @@ 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 @@ -180,20 +167,6 @@ class AStateProvider : public AInventory DECLARE_CLASS (AStateProvider, AInventory) }; -// CustomInventory: Supports the Use, Pickup, and Drop states from 96x -class ACustomInventory : public AStateProvider -{ - DECLARE_CLASS (ACustomInventory, AStateProvider) -public: - - // This is used when an inventory item's use state sequence is executed. - bool CallStateChain (AActor *actor, FState *state); - - virtual bool TryPickup (AActor *&toucher) override; - virtual bool Use (bool pickup) override; - virtual bool SpecialDropAction (AActor *dropper) override; -}; - extern PClassActor *QuestItemClasses[31]; diff --git a/src/namedef.h b/src/namedef.h index 20953a8fe4..926fac1b21 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -196,6 +196,7 @@ xx(PowerWeaponLevel2) xx(PowerFlight) xx(PowerSpeed) xx(PowerTorch) +xx(CustomInventory) xx(AcolyteBlue) xx(SpectralLightningV1) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 3c4978e0dd..f3e1a0c8dc 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -108,7 +108,7 @@ static FRandom pr_bfgselfdamage("BFGSelfDamage"); // //========================================================================== -bool ACustomInventory::CallStateChain (AActor *actor, FState *state) +bool AInventory::CallStateChain (AActor *actor, FState *state) { INTBOOL result = false; int counter = 0; @@ -223,6 +223,13 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state) return !!result; } +DEFINE_ACTION_FUNCTION(AInventory, CallStateChain) +{ + PARAM_SELF_PROLOGUE(AInventory); + PARAM_OBJECT(affectee, AActor); + PARAM_STATE(state); + ACTION_RETURN_BOOL(self->CallStateChain(affectee, state)); +} //========================================================================== // diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 1ca9baac88..6691a1d0e1 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -844,7 +844,13 @@ void AActor::RemoveInventory(AInventory *item) if (inv == item) { *invp = item->Inventory; - item->DetachFromOwner(); + + IFVIRTUALPTR(item, AInventory, DetachFromOwner) + { + VMValue params[1] = { item }; + GlobalVMStack.Call(func, params, 1, nullptr, 0, nullptr); + } + item->Owner = NULL; item->Inventory = NULL; break; @@ -1032,7 +1038,7 @@ DEFINE_ACTION_FUNCTION(AActor, UseInventory) AInventory *AActor::DropInventory (AInventory *item) { - AInventory *drop = item->CallCreateTossable (); + AInventory *drop = item->CreateTossable (); if (drop == NULL) { @@ -3622,7 +3628,6 @@ int AActor::AbsorbDamage(int damage, FName dmgtype) VMValue params[4] = { item, damage, dmgtype.GetIndex(), &damage }; GlobalVMStack.Call(func, params, 4, nullptr, 0, nullptr); } - else item->AbsorbDamage(damage, dmgtype, damage); } return damage; } diff --git a/src/scripting/thingdef.cpp b/src/scripting/thingdef.cpp index c126e3d57b..eea1d56bfa 100644 --- a/src/scripting/thingdef.cpp +++ b/src/scripting/thingdef.cpp @@ -253,12 +253,16 @@ static void CheckForUnsafeStates(PClassActor *obj) if (obj->Size == RUNTIME_CLASS(AWeapon)->Size) return; // This class cannot have user variables. test = weaponstates; } - else if (obj->IsDescendantOf(RUNTIME_CLASS(ACustomInventory))) + else { - if (obj->Size == RUNTIME_CLASS(ACustomInventory)->Size) return; // This class cannot have user variables. - test = pickupstates; + auto citype = PClass::FindActor(NAME_CustomInventory); + if (obj->IsDescendantOf(citype)) + { + if (obj->Size == citype->Size) return; // This class cannot have user variables. + test = pickupstates; + } + else return; // something else derived from AStateProvider. We do not know what this may be. } - else return; // something else derived from AStateProvider. We do not know what this may be. for (; *test != NAME_None; test++) { @@ -338,7 +342,7 @@ static void CheckStates(PClassActor *obj) { CheckStateLabels(obj, weaponstates, SUF_WEAPON, "weapon sprites"); } - else if (obj->IsDescendantOf(RUNTIME_CLASS(ACustomInventory))) + else if (obj->IsDescendantOf(PClass::FindActor(NAME_CustomInventory))) { CheckStateLabels(obj, pickupstates, SUF_ITEM, "CustomInventory state chain"); } diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index f03122edf4..1b0c0dfef8 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2464,7 +2464,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool PFunction *virtsym = nullptr; if (cls != nullptr && cls->ParentClass != nullptr) virtsym = dyn_cast(cls->ParentClass->Symbols.FindSymbol(FName(f->Name), true)); unsigned vindex = ~0u; - if (virtsym != nullptr) vindex = virtsym->Variants[0].Implementation->VirtualIndex; + if (virtsym != nullptr) + { + auto imp = virtsym->Variants[0].Implementation; + if (imp != nullptr) vindex = imp->VirtualIndex; + else Error(f, "Virtual base function %s not found in %s", FName(f->Name).GetChars(), cls->ParentClass->TypeName.GetChars()); + } if (!(f->Flags & ZCC_Native)) { diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index ea76ab1d9a..d798dba262 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -9,9 +9,9 @@ class Inventory : Actor native { const BLINKTHRESHOLD = (4*32); - native Actor Owner; // Who owns this item? NULL if it's still a pickup. - native int Amount; // Amount of item this instance has - native int MaxAmount; // Max amount of item this instance can have + native Actor Owner; // Who owns self item? NULL if it's still a pickup. + native int Amount; // Amount of item self instance has + native int MaxAmount; // Max amount of item self instance can have native int InterHubAmount; // Amount of item that can be kept between hubs or levels native int RespawnTics; // Tics from pickup time to respawn time native TextureID Icon; // Icon to show on status bar or HUD @@ -33,19 +33,13 @@ class Inventory : Actor native Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG"; } - virtual native bool Use (bool pickup); virtual native color GetBlend (); virtual native bool HandlePickup(Inventory item); virtual native Inventory CreateCopy(Actor other); - virtual native Inventory CreateTossable(); virtual native bool SpecialDropAction (Actor dropper); virtual native String PickupMessage(); virtual native bool ShouldStay(); virtual native void PlayPickupSound(Actor user); - virtual native void AttachToOwner(Actor user); - virtual native void DetachFromOwner(); - virtual native bool DrawPowerup(int x, int y); - virtual native void AbsorbDamage (int damage, Name damageType, out int newdamage); native bool DoRespawn(); native bool GoAway(); @@ -55,9 +49,8 @@ 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. - virtual protected native bool TryPickup(in out Actor toucher); - virtual protected native bool TryPickupRestricted(in out Actor toucher); native bool, Actor CallTryPickup(Actor toucher); + native bool CallStateChain (Actor actor, State state); States(Actor, Overlay, Weapon, Item) { @@ -138,6 +131,188 @@ class Inventory : Actor native } } + + //=========================================================================== + // + // AInventory :: CallHandlePickup + // + // Runs all HandlePickup methods in the chain + // + //=========================================================================== + + private bool CallHandlePickup(Inventory item) + { + let me = self; + while (me != null) + { + if (me.HandlePickup(item)) return true; + me = me.Inv; + } + return false; + } + + //=========================================================================== + // + // AInventory :: TryPickup + // + //=========================================================================== + + virtual protected bool TryPickup (in out Actor toucher) + { + Actor newtoucher = toucher; // in case changed by the powerup + + // If HandlePickup() returns true, it will set the IF_PICKUPGOOD flag + // to indicate that self item has been picked up. If the item cannot be + // picked up, then it leaves the flag cleared. + + bPickupGood = false; + if (toucher.Inv != NULL && toucher.Inv.CallHandlePickup (self)) + { + // Let something else the player is holding intercept the pickup. + if (!bPickupGood) + { + return false; + } + bPickupGood = false; + GoAwayAndDie (); + } + else if (MaxAmount > 0) + { + // Add the item to the inventory. It is not already there, or HandlePickup + // would have already taken care of it. + let copy = CreateCopy (toucher); + if (copy == NULL) + { + return false; + } + // Some powerups cannot activate absolutely, for + // example, PowerMorph; fail the pickup if so. + if (copy.bInitEffectFailed) + { + if (copy != self) copy.Destroy(); + else bInitEffectFailed; + return false; + } + // Handle owner-changing powerups + if (copy.bCreateCopyMoved) + { + newtoucher = copy.Owner; + copy.Owner = NULL; + bCreateCopyMoved = false; + } + // Continue onwards with the rest + copy.AttachToOwner (newtoucher); + if (bAutoActivate) + { + if (copy.Use (true)) + { + if (--copy.Amount <= 0) + { + copy.bSpecial = false; + copy.SetStateLabel ("HoldAndDestroy"); + } + } + } + } + else if (bAutoActivate) + { + // Special case: If an item's MaxAmount is 0, you can still pick it + // up if it is autoactivate-able. + + // The item is placed in the inventory just long enough to be used. + toucher.AddInventory(self); + bool usegood = Use(true); + toucher.RemoveInventory(self); + + if (usegood) + { + GoAwayAndDie(); + } + else + { + return false; + } + } + return true; + } + + //=========================================================================== + // + // AInventory :: TryPickupRestricted + // + //=========================================================================== + + virtual bool TryPickupRestricted (in out Actor toucher) + { + return false; + } + + //=========================================================================== + // + // AInventory :: AttachToOwner + // + //=========================================================================== + + virtual void AttachToOwner (Actor other) + { + BecomeItem (); + other.AddInventory (self); + } + + //=========================================================================== + // + // AInventory :: DetachFromOwner + // + // Performs any special work needed when the item leaves an inventory, + // either through destruction or becoming a pickup. + // + //=========================================================================== + + virtual void DetachFromOwner () + { + } + + //=========================================================================== + // + // 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. + // + //=========================================================================== + + virtual Inventory CreateTossable () + { + // If self actor lacks a SpawnState, don't drop it. (e.g. A base weapon + // like the fist can't be dropped because you'll never see it.) + if (SpawnState == GetDefaultByType("Actor").SpawnState || SpawnState == NULL) + { + return NULL; + } + if (bUndroppable || bUntossable || Owner == NULL || Amount <= 0) + { + return NULL; + } + if (Amount == 1 && !bKeepDepleted) + { + BecomePickup (); + DropTime = 30; + bSpecial = bSolid = false; + return self; + } + let copy = Inventory(Spawn (GetClass(), Owner.Pos, NO_REPLACE)); + if (copy != NULL) + { + copy.MaxAmount = MaxAmount; + copy.Amount = 1; + copy.DropTime = 30; + copy.bSpecial = copy.bSolid = false; + Amount--; + } + return copy; + } + //=========================================================================== // // AInventory :: DepleteOrDestroy @@ -186,11 +361,37 @@ class Inventory : Actor native virtual void DoEffect() {} + + 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() {} + + //=========================================================================== + // + // AInventory :: DrawPowerup + // + // Gives self item a chance to draw a special status indicator on the screen. + // Returns false if it didn't draw anything. + // + //=========================================================================== + + virtual bool DrawPowerup(int x, int y) { return false; } + + //=========================================================================== + // + // AInventory :: AbsorbDamage + // + // Allows inventory items (primarily armor) to reduce the amount of damage + // taken. Damage is the amount of damage that would be done without armor, + // and newdamage is the amount that should be done after the armor absorbs + // it. + // + //=========================================================================== + + virtual void AbsorbDamage (int damage, Name damageType, out int newdamage) {} } diff --git a/wadsrc/static/zscript/inventory/stateprovider.txt b/wadsrc/static/zscript/inventory/stateprovider.txt index fe5d39baca..8f55716296 100644 --- a/wadsrc/static/zscript/inventory/stateprovider.txt +++ b/wadsrc/static/zscript/inventory/stateprovider.txt @@ -36,4 +36,48 @@ class CustomInventory : StateProvider native deprecated action void A_Lower() {} deprecated action void A_Raise() {} deprecated action void A_CheckReload() {} + + //=========================================================================== + // + // ACustomInventory :: SpecialDropAction + // + //=========================================================================== + + override bool SpecialDropAction (Actor dropper) + { + return CallStateChain (dropper, FindState('Drop')); + } + + //=========================================================================== + // + // ACustomInventory :: Use + // + //=========================================================================== + + override bool Use (bool pickup) + { + return CallStateChain (Owner, FindState('Use')); + } + + //=========================================================================== + // + // ACustomInventory :: TryPickup + // + //=========================================================================== + + override bool TryPickup (in out Actor toucher) + { + let pickupstate = FindState('Pickup'); + bool useok = CallStateChain (toucher, pickupstate); + if ((useok || pickupstate == NULL) && FindState('Use') != NULL) + { + useok = Super.TryPickup (toucher); + } + else if (useok) + { + GoAwayAndDie(); + } + return useok; + } + }