struct VisStyle { bool Invert; float Alpha; int RenderStyle; } class Inventory : Actor native { const BLINKTHRESHOLD = (4*32); 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 native int DropTime; // Countdown after dropping native Class SpawnPointClass; // For respawning like Heretic's mace native Class PickupFlash; // actor to spawn as pickup flash native Sound PickupSound; native bool bPickupGood; native bool bCreateCopyMoved; native bool bInitEffectFailed; Default { Inventory.Amount 1; Inventory.MaxAmount 1; Inventory.InterHubAmount 1; Inventory.UseSound "misc/invuse"; Inventory.PickupSound "misc/i_pkup"; Inventory.PickupMessage "$TXT_DEFAULTPICKUPMSG"; } 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); 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 bool CallStateChain (Actor actor, State state); States(Actor, Overlay, Weapon, Item) { HideDoomish: TNT1 A 1050; TNT1 A 0 A_RestoreSpecialPosition; TNT1 A 1 A_RestoreSpecialDoomThing; Stop; HideSpecial: ACLO E 1400; ACLO A 0 A_RestoreSpecialPosition; ACLO A 4 A_RestoreSpecialThing1; ACLO BABCBCDC 4; ACLO D 4 A_RestoreSpecialThing2; Stop; Held: TNT1 A -1; Stop; HoldAndDestroy: TNT1 A 1; Stop; } // These are regular functions for the item itself. //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing1 // // Make a special thing visible again. // //--------------------------------------------------------------------------- void A_RestoreSpecialThing1() { bInvisible = false; if (DoRespawn ()) { A_PlaySound ("misc/spawn", CHAN_VOICE); } } //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialThing2 // //--------------------------------------------------------------------------- void A_RestoreSpecialThing2() { bSpecial = true; if (!Default.bNoGravity) { bNoGravity = false; } SetState (SpawnState); } //--------------------------------------------------------------------------- // // PROC A_RestoreSpecialDoomThing // //--------------------------------------------------------------------------- void A_RestoreSpecialDoomThing() { bInvisible = false; bSpecial = true; if (!Default.bNoGravity) { bNoGravity = false; } if (DoRespawn ()) { SetState (SpawnState); A_PlaySound ("misc/spawn", CHAN_VOICE); Spawn ("ItemFog", Pos, ALLOW_REPLACE); } } //=========================================================================== // // 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 // // If the item is depleted, just change its amount to 0, otherwise it's destroyed. // //=========================================================================== virtual void DepleteOrDestroy () { // If it's not ammo or an internal armor, destroy it. // Ammo needs to stick around, even when it's zero for the benefit // of the weapons that use it and to maintain the maximum ammo // amounts a backpack might have given. // Armor shouldn't be removed because they only work properly when // they are the last items in the inventory. if (bKeepDepleted) { Amount = 0; } else { Destroy(); } } //=========================================================================== // // AInventory :: Travelled // // Called when an item in somebody's inventory is carried over to another // map, in case it needs to do special reinitialization. // //=========================================================================== virtual void Travelled() {} //=========================================================================== // // AInventory :: DoEffect // // Handles any effect an item might apply to its owner // Normally only used by subclasses of Powerup // //=========================================================================== 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) {} } class DehackedPickup : Inventory native { } class FakeInventory : Inventory native { native bool Respawnable; }