diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp
index c0ea00f5e..e2c5a6ea3 100644
--- a/src/d_dehacked.cpp
+++ b/src/d_dehacked.cpp
@@ -3157,7 +3157,7 @@ FString ADehackedPickup::PickupMessage ()
 bool ADehackedPickup::ShouldStay ()
 {
 	if (RealPickup != nullptr)
-		return RealPickup->ShouldStay ();
+		return RealPickup->CallShouldStay ();
 	else return true;
 }
 
diff --git a/src/g_inventory/a_ammo.h b/src/g_inventory/a_ammo.h
index fe5e1759e..643d4b924 100644
--- a/src/g_inventory/a_ammo.h
+++ b/src/g_inventory/a_ammo.h
@@ -18,11 +18,11 @@ class AAmmo : public AInventory
 	DECLARE_CLASS_WITH_META(AAmmo, AInventory, PClassAmmo)
 public:
 	
-	void Serialize(FSerializer &arc);
-	AInventory *CreateCopy (AActor *other);
-	bool HandlePickup (AInventory *item);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual AInventory *CreateTossable () override;
 	PClassActor *GetParentAmmo () const;
-	AInventory *CreateTossable ();
 
 	int BackpackAmount, BackpackMaxAmount;
 };
@@ -35,11 +35,11 @@ class ABackpackItem : public AInventory
 	DECLARE_CLASS (ABackpackItem, AInventory)
 public:
 	
-	void Serialize(FSerializer &arc);
-	bool HandlePickup (AInventory *item);
-	AInventory *CreateCopy (AActor *other);
-	AInventory *CreateTossable ();
-	void DetachFromOwner ();
+	virtual void Serialize(FSerializer &arc) override;
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual AInventory *CreateTossable () override;
+	virtual void DetachFromOwner () override;
 
 	bool bDepleted;
 };
diff --git a/src/g_inventory/a_armor.h b/src/g_inventory/a_armor.h
index b7b2dad3e..63febda91 100644
--- a/src/g_inventory/a_armor.h
+++ b/src/g_inventory/a_armor.h
@@ -16,11 +16,11 @@ class ABasicArmor : public AArmor
 	DECLARE_CLASS (ABasicArmor, AArmor)
 public:
 	
-	virtual void Serialize(FSerializer &arc);
-	virtual void Tick ();
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual bool HandlePickup (AInventory *item);
-	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual void Tick () override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
 
 	int AbsorbCount;
 	double SavePercent;
@@ -37,9 +37,9 @@ class ABasicArmorPickup : public AArmor
 	DECLARE_CLASS (ABasicArmorPickup, AArmor)
 public:
 	
-	virtual void Serialize(FSerializer &arc);
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual bool Use (bool pickup);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual bool Use (bool pickup) override;
 
 	double SavePercent;
 	int MaxAbsorb;
@@ -53,9 +53,9 @@ class ABasicArmorBonus : public AArmor
 	DECLARE_CLASS (ABasicArmorBonus, AArmor)
 public:
 	
-	virtual void Serialize(FSerializer &arc);
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual bool Use (bool pickup);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual bool Use (bool pickup) override;
 
 	double SavePercent;	// The default, for when you don't already have armor
 	int MaxSaveAmount;
@@ -73,12 +73,12 @@ class AHexenArmor : public AArmor
 	DECLARE_CLASS (AHexenArmor, AArmor)
 public:
 	
-	virtual void Serialize(FSerializer &arc);
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual AInventory *CreateTossable ();
-	virtual bool HandlePickup (AInventory *item);
-	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage);
-	void DepleteOrDestroy();
+	virtual void Serialize(FSerializer &arc) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual AInventory *CreateTossable () override;
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
+	virtual void DepleteOrDestroy() override;
 
 	double Slots[5];
 	double SlotsIncrement[4];
diff --git a/src/g_inventory/a_artifacts.cpp b/src/g_inventory/a_artifacts.cpp
index 1198b9153..a75b9ecd4 100644
--- a/src/g_inventory/a_artifacts.cpp
+++ b/src/g_inventory/a_artifacts.cpp
@@ -373,18 +373,6 @@ void APowerup::OwnerDied ()
 	Destroy ();
 }
 
-//===========================================================================
-//
-// AInventory :: GetNoTeleportFreeze
-//
-//===========================================================================
-
-bool APowerup::GetNoTeleportFreeze ()
-{
-	if (ItemFlags & IF_NOTELEPORTFREEZE) return true;
-	return Super::GetNoTeleportFreeze();
-}
-
 // Invulnerability Powerup ---------------------------------------------------
 
 IMPLEMENT_CLASS(APowerInvulnerable, false, false)
@@ -1213,20 +1201,6 @@ void APowerSpeed::Serialize(FSerializer &arc)
 	arc("speedflags", SpeedFlags);
 }
 
-//===========================================================================
-//
-// APowerSpeed :: GetSpeedFactor
-//
-//===========================================================================
-
-double APowerSpeed ::GetSpeedFactor ()
-{
-	if (Inventory != NULL)
-		return Speed * Inventory->GetSpeedFactor();
-	else
-		return Speed;
-}
-
 //===========================================================================
 //
 // APowerSpeed :: DoEffect
diff --git a/src/g_inventory/a_artifacts.h b/src/g_inventory/a_artifacts.h
index c3a0f31f0..7e9956d73 100644
--- a/src/g_inventory/a_artifacts.h
+++ b/src/g_inventory/a_artifacts.h
@@ -11,18 +11,15 @@ class APowerup : public AInventory
 {
 	DECLARE_CLASS (APowerup, AInventory)
 public:
-	virtual void Tick ();
+	virtual void Tick () override;
 	virtual void Destroy () override;
-	virtual bool HandlePickup (AInventory *item);
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual AInventory *CreateTossable ();
-
-	
-	virtual void Serialize(FSerializer &arc);
-	virtual void OwnerDied ();
-	virtual bool GetNoTeleportFreeze();
-	virtual PalEntry GetBlend ();
-	virtual bool DrawPowerup (int x, int y);
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual AInventory *CreateTossable () override;
+	virtual void Serialize(FSerializer &arc) override;
+	virtual void OwnerDied () override;
+	virtual PalEntry GetBlend () override;
+	virtual bool DrawPowerup (int x, int y) override;
 
 	int EffectTics;
 	PalEntry BlendColor;
@@ -31,7 +28,7 @@ public:
 
 protected:
 	virtual void InitEffect ();
-	virtual void DoEffect ();
+	virtual void DoEffect () override;
 	virtual void EndEffect ();
 
 	friend void EndAllPowerupEffects(AInventory *item);
@@ -52,9 +49,8 @@ class APowerupGiver : public AInventory
 {
 	DECLARE_CLASS_WITH_META (APowerupGiver, AInventory, PClassPowerupGiver)
 public:
-	virtual bool Use (bool pickup);
-	
-	virtual void Serialize(FSerializer &arc);
+	virtual bool Use (bool pickup) override;
+	virtual void Serialize(FSerializer &arc) override;
 
 
 	PClassActor *PowerupType;
@@ -68,10 +64,10 @@ class APowerInvulnerable : public APowerup
 {
 	DECLARE_CLASS (APowerInvulnerable, APowerup)
 protected:
-	void InitEffect ();
-	void DoEffect ();
-	void EndEffect ();
-	int AlterWeaponSprite (visstyle_t *vis);
+	virtual void InitEffect () override;
+	virtual void DoEffect () override;
+	virtual void EndEffect () override;
+	virtual int AlterWeaponSprite (visstyle_t *vis) override;
 };
 
 class APowerStrength : public APowerup
@@ -80,44 +76,44 @@ class APowerStrength : public APowerup
 public:
 	PalEntry GetBlend ();
 protected:
-	void InitEffect ();
-	void Tick ();
-	bool HandlePickup (AInventory *item);
+	virtual void InitEffect () override;
+	virtual void Tick () override;
+	virtual bool HandlePickup (AInventory *item) override;
 };
 
 class APowerInvisibility : public APowerup
 {
 	DECLARE_CLASS (APowerInvisibility, APowerup)
 protected:
-	bool HandlePickup (AInventory *item);
-	void InitEffect ();
-	void DoEffect ();
-	void EndEffect ();
-	int AlterWeaponSprite (visstyle_t *vis);
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual void InitEffect () override;
+	virtual void DoEffect () override;
+	virtual void EndEffect () override;
+	virtual int AlterWeaponSprite (visstyle_t *vis) override;
 };
 
 class APowerIronFeet : public APowerup
 {
 	DECLARE_CLASS (APowerIronFeet, APowerup)
 public:
-	void AbsorbDamage (int damage, FName damageType, int &newdamage);
-	void DoEffect ();
+	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
+	virtual void DoEffect () override;
 };
 
 class APowerMask : public APowerIronFeet
 {
 	DECLARE_CLASS (APowerMask, APowerIronFeet)
 public:
-	void AbsorbDamage (int damage, FName damageType, int &newdamage);
-	void DoEffect ();
+	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage) override;
+	virtual void DoEffect () override;
 };
 
 class APowerLightAmp : public APowerup
 {
 	DECLARE_CLASS (APowerLightAmp, APowerup)
 protected:
-	void DoEffect ();
-	void EndEffect ();
+	virtual void DoEffect () override;
+	virtual void EndEffect () override;
 };
 
 class APowerTorch : public APowerLightAmp
@@ -125,9 +121,9 @@ class APowerTorch : public APowerLightAmp
 	DECLARE_CLASS (APowerTorch, APowerLightAmp)
 public:
 	
-	virtual void Serialize(FSerializer &arc);
+	virtual void Serialize(FSerializer &arc) override;
 protected:
-	void DoEffect ();
+	virtual void DoEffect () override;
 	int NewTorch, NewTorchDelta;
 };
 
@@ -135,14 +131,13 @@ class APowerFlight : public APowerup
 {
 	DECLARE_CLASS (APowerFlight, APowerup)
 public:
-	bool DrawPowerup (int x, int y);
-	
-	virtual void Serialize(FSerializer &arc);
+	virtual bool DrawPowerup (int x, int y) override;
+	virtual void Serialize(FSerializer &arc) override;
 
 protected:
-	void InitEffect ();
-	void Tick ();
-	void EndEffect ();
+	virtual void InitEffect () override;
+	virtual void Tick () override;
+	virtual void EndEffect () override;
 
 private:
 	bool HitCenterFrame;
@@ -152,18 +147,17 @@ class APowerWeaponLevel2 : public APowerup
 {
 	DECLARE_CLASS (APowerWeaponLevel2, APowerup)
 protected:
-	void InitEffect ();
-	void EndEffect ();
+	virtual void InitEffect () override;
+	virtual void EndEffect () override;
 };
 
 class APowerSpeed : public APowerup
 {
 	DECLARE_CLASS (APowerSpeed, APowerup)
 protected:
-	void DoEffect ();
+	virtual void DoEffect () override;
 	
-	virtual void Serialize(FSerializer &arc);
-	double GetSpeedFactor();
+	virtual void Serialize(FSerializer &arc) override;
 public:
 	int SpeedFlags;
 };
@@ -184,95 +178,95 @@ class APowerTargeter : public APowerup
 {
 	DECLARE_CLASS (APowerTargeter, APowerup)
 protected:
-	void InitEffect ();
-	void DoEffect ();
-	void EndEffect ();
+	virtual void InitEffect () override;
+	virtual void DoEffect () override;
+	virtual void EndEffect () override;
 	void PositionAccuracy ();
-	void Travelled ();
-	void AttachToOwner(AActor *other);
-	bool HandlePickup(AInventory *item);
+	virtual void Travelled () override;
+	virtual void AttachToOwner(AActor *other) override;
+	virtual bool HandlePickup(AInventory *item) override;
 };
 
 class APowerFrightener : public APowerup
 {
 	DECLARE_CLASS (APowerFrightener, APowerup)
 protected:
-	void InitEffect ();
-	void EndEffect ();
+	virtual void InitEffect () override;
+	virtual void EndEffect () override;
 };
 
 class APowerBuddha : public APowerup
 {
 	DECLARE_CLASS (APowerBuddha, APowerup)
 protected:
-	void InitEffect ();
-	void EndEffect ();
+	virtual void InitEffect () override;
+	virtual void EndEffect () override;
 };
 
 class APowerTimeFreezer : public APowerup
 {
 	DECLARE_CLASS( APowerTimeFreezer, APowerup )
 protected:
-	void InitEffect( );
-	void DoEffect( );
-	void EndEffect( );
+	virtual void InitEffect() override;
+	virtual void DoEffect() override;
+	virtual void EndEffect() override;
 };
 
 class APowerDamage : public APowerup
 {
 	DECLARE_CLASS( APowerDamage, APowerup )
 protected:
-	void InitEffect ();
-	void EndEffect ();
-	virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive);
+	virtual void InitEffect () override;
+	virtual void EndEffect () override;
+	virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) override;
 };
 
 class APowerProtection : public APowerup
 {
 	DECLARE_CLASS( APowerProtection, APowerup )
 protected:
-	void InitEffect ();
-	void EndEffect ();
-	virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive);
+	virtual void InitEffect () override;
+	virtual void EndEffect () override;
+	virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive) override;
 };
 
 class APowerDrain : public APowerup
 {
 	DECLARE_CLASS( APowerDrain, APowerup )
 protected:
-	void InitEffect( );
-	void EndEffect( );
+	virtual void InitEffect() override;
+	virtual void EndEffect() override;
 };
 
 class APowerRegeneration : public APowerup
 {
 	DECLARE_CLASS( APowerRegeneration, APowerup )
 protected:
-	void DoEffect();
+	virtual void DoEffect() override;
 };
 
 class APowerHighJump : public APowerup
 {
 	DECLARE_CLASS( APowerHighJump, APowerup )
 protected:
-	void InitEffect( );
-	void EndEffect( );
+	virtual void InitEffect() override;
+	virtual void EndEffect() override;
 };
 
 class APowerDoubleFiringSpeed : public APowerup
 {
 	DECLARE_CLASS( APowerDoubleFiringSpeed, APowerup )
 protected:
-	void InitEffect( );
-	void EndEffect( );
+	virtual void InitEffect() override;
+	virtual void EndEffect() override;
 };
 
 class APowerInfiniteAmmo : public APowerup
 {
 	DECLARE_CLASS( APowerInfiniteAmmo, APowerup )
 protected:
-	void InitEffect( );
-	void EndEffect( );
+	virtual void InitEffect() override;
+	virtual void EndEffect() override;
 };
 
 class APowerMorph : public APowerup
@@ -280,7 +274,7 @@ class APowerMorph : public APowerup
 	DECLARE_CLASS( APowerMorph, APowerup )
 public:
 	
-	virtual void Serialize(FSerializer &arc);
+	virtual void Serialize(FSerializer &arc) override;
 	void SetNoCallUndoMorph() { bInUndoMorph = true; }
 
 	// Variables
@@ -291,8 +285,8 @@ public:
 	bool bInUndoMorph;	// Because P_UndoPlayerMorph() can call EndEffect recursively
 
 protected:
-	void InitEffect ();
-	void EndEffect ();
+	virtual void InitEffect () override;
+	virtual void EndEffect () override;
 };
 
 #endif //__A_ARTIFACTS_H__
diff --git a/src/g_inventory/a_health.h b/src/g_inventory/a_health.h
index a462bbfbd..af282031c 100644
--- a/src/g_inventory/a_health.h
+++ b/src/g_inventory/a_health.h
@@ -21,8 +21,8 @@ class AHealth : public AInventory
 
 public:
 	int PrevHealth;
-	virtual bool TryPickup (AActor *&other);
-	virtual FString PickupMessage ();
+	virtual bool TryPickup (AActor *&other) override;
+	virtual FString PickupMessage () override;
 };
 
 // HealthPickup is some item that gives the player health when used.
@@ -33,10 +33,10 @@ public:
 	int autousemode;
 
 	
-	virtual void Serialize(FSerializer &arc);
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual AInventory *CreateTossable ();
-	virtual bool HandlePickup (AInventory *item);
-	virtual bool Use (bool pickup);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual AInventory *CreateTossable () override;
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual bool Use (bool pickup) override;
 };
 
diff --git a/src/g_inventory/a_keys.h b/src/g_inventory/a_keys.h
index fd46b416c..eda0473a3 100644
--- a/src/g_inventory/a_keys.h
+++ b/src/g_inventory/a_keys.h
@@ -7,12 +7,12 @@ class AKey : public AInventory
 {
 	DECLARE_CLASS (AKey, AInventory)
 public:
-	virtual bool HandlePickup (AInventory *item);
+	virtual bool HandlePickup (AInventory *item) override;
 
 	BYTE KeyNumber;
 
 protected:
-	virtual bool ShouldStay ();
+	virtual bool ShouldStay () override;
 };
 
 bool P_CheckKeys (AActor *owner, int keynum, bool remote);
@@ -37,9 +37,9 @@ class APuzzleItem : public AInventory
 	DECLARE_CLASS_WITH_META(APuzzleItem, AInventory, PClassPuzzleItem)
 public:
 	
-	bool ShouldStay ();
-	bool Use (bool pickup);
-	bool HandlePickup (AInventory *item);
+	virtual bool ShouldStay () override;
+	virtual bool Use (bool pickup) override;
+	virtual bool HandlePickup (AInventory *item) override;
 
 	int PuzzleItemNumber;
 };
diff --git a/src/g_inventory/a_pickups.cpp b/src/g_inventory/a_pickups.cpp
index cf5dddcf3..237de2ce9 100644
--- a/src/g_inventory/a_pickups.cpp
+++ b/src/g_inventory/a_pickups.cpp
@@ -399,6 +399,25 @@ void AInventory::DoEffect ()
 {
 }
 
+DEFINE_ACTION_FUNCTION(AInventory, DoEffect)
+{
+	PARAM_SELF_PROLOGUE(AInventory);
+	self->DoEffect();
+	return 0;
+}
+
+void AInventory::CallDoEffect()
+{
+	IFVIRTUAL(AInventory, DoEffect)
+	{
+		VMValue params[1] = { (DObject*)this };
+		VMFrameStack stack;
+		stack.Call(func, params, 1, nullptr, 0, nullptr);
+	}
+	else DoEffect();
+}
+
+
 //===========================================================================
 //
 // AInventory :: Travelled
@@ -437,7 +456,7 @@ bool AInventory::HandlePickup (AInventory *item)
 {
 	if (item->GetClass() == GetClass())
 	{
-		if (Amount < MaxAmount || (sv_unlimited_pickup && !item->ShouldStay()))
+		if (Amount < MaxAmount || (sv_unlimited_pickup && !item->CallShouldStay()))
 		{
 			if (Amount > 0 && Amount + item->Amount < 0)
 			{
@@ -506,7 +525,7 @@ bool AInventory::GoAway ()
 		return false;
 	}
 
-	if (!ShouldStay ())
+	if (!CallShouldStay ())
 	{
 		Hide ();
 		if (ShouldRespawn ())
@@ -776,16 +795,25 @@ void AInventory::ModifyDamage (int damage, FName damageType, int &newdamage, boo
 //
 //===========================================================================
 
-double AInventory::GetSpeedFactor ()
+double AInventory::GetSpeedFactor()
 {
-	if (Inventory != NULL)
+	double factor = 1.;
+	auto self = this;
+	while (self != nullptr)
 	{
-		return Inventory->GetSpeedFactor();
-	}
-	else
-	{
-		return 1.;
+		IFVIRTUALPTR(self, AInventory, GetSpeedFactor)
+		{
+			VMValue params[2] = { (DObject*)self };
+			VMReturn ret;
+			VMFrameStack stack;
+			double retval;
+			ret.FloatAt(&retval);
+			stack.Call(func, params, 1, &ret, 1, nullptr);
+			factor *= retval;
+		}
+		self = self->Inventory;
 	}
+	return factor;
 }
 
 //===========================================================================
@@ -796,15 +824,22 @@ double AInventory::GetSpeedFactor ()
 
 bool AInventory::GetNoTeleportFreeze ()
 {
-	// do not check the flag here because it's only active when used on PowerUps, not on PowerupGivers.
-	if (Inventory != NULL)
+	auto self = this;
+	while (self != nullptr)
 	{
-		return Inventory->GetNoTeleportFreeze();
-	}
-	else
-	{
-		return false;
+		IFVIRTUALPTR(self, AInventory, GetNoTeleportFreeze)
+		{
+			VMValue params[2] = { (DObject*)self };
+			VMReturn ret;
+			VMFrameStack stack;
+			int retval;
+			ret.IntAt(&retval);
+			stack.Call(func, params, 1, &ret, 1, nullptr);
+			if (retval) return true;
+		}
+		self = self->Inventory;
 	}
+	return false;
 }
 
 //===========================================================================
@@ -953,7 +988,7 @@ void AInventory::Touch (AActor *toucher)
 	if (!CallTryPickup (toucher, &toucher)) return;
 
 	// This is the only situation when a pickup flash should ever play.
-	if (PickupFlash != NULL && !ShouldStay())
+	if (PickupFlash != NULL && !CallShouldStay())
 	{
 		Spawn(PickupFlash, Pos(), ALLOW_REPLACE);
 	}
@@ -1102,6 +1137,26 @@ void AInventory::PlayPickupSound (AActor *toucher)
 	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 };
+		VMFrameStack stack;
+		stack.Call(func, params, 2, nullptr, 0, nullptr);
+	}
+	else PlayPickupSound(other);
+}
+
+
 //===========================================================================
 //
 // AInventory :: ShouldStay
@@ -1115,6 +1170,28 @@ 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)
+	{
+		VMValue params[1] = { (DObject*)this };
+		VMReturn ret;
+		VMFrameStack stack;
+		int retval;
+		ret.IntAt(&retval);
+		stack.Call(func, params, 1, &ret, 1, nullptr);
+		return !!retval;
+	}
+	else return ShouldStay();
+}
+
+
 //===========================================================================
 //
 // AInventory :: Destroy
@@ -1416,6 +1493,14 @@ bool AInventory::TryPickupRestricted (AActor *&toucher)
 	return false;
 }
 
+DEFINE_ACTION_FUNCTION(AInventory, TryPickupRestricted)
+{
+	PARAM_SELF_PROLOGUE(AInventory);
+	PARAM_POINTER(toucher, AActor*);
+	ACTION_RETURN_BOOL(self->TryPickupRestricted(*toucher));
+}
+
+
 //===========================================================================
 //
 // AInventory :: CallTryPickup
@@ -1445,14 +1530,27 @@ bool AInventory::CallTryPickup (AActor *toucher, AActor **toucher_return)
 		else res = TryPickup(toucher);
 	}
 	else if (!(ItemFlags & IF_RESTRICTABSOLUTELY))
-		res = TryPickupRestricted(toucher);	// let an item decide for itself how it will handle this
+	{
+		// let an item decide for itself how it will handle this
+		IFVIRTUAL(AInventory, TryPickupRestricted)
+		{
+			VMValue params[2] = { (DObject*)this, (void*)&toucher };
+			VMReturn ret;
+			VMFrameStack stack;
+			int retval;
+			ret.IntAt(&retval);
+			stack.Call(func, params, 2, &ret, 1, nullptr);
+			res = !!retval;
+		}
+		else res = TryPickupRestricted(toucher);
+	}
 	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) && !ShouldStay())
+	if (!res && (ItemFlags & IF_ALWAYSPICKUP) && !CallShouldStay())
 	{
 		res = true;
 		GoAwayAndDie();
@@ -1576,6 +1674,26 @@ void AInventory::AttachToOwner (AActor *other)
 	other->AddInventory (this);
 }
 
+DEFINE_ACTION_FUNCTION(AInventory, AttachToOwner)
+{
+	PARAM_SELF_PROLOGUE(AInventory);
+	PARAM_OBJECT(other, AActor);
+	self->AttachToOwner(other);
+	return 0;
+}
+
+void AInventory::CallAttachToOwner(AActor *other)
+{
+	IFVIRTUAL(AInventory, AttachToOwner)
+	{
+		VMValue params[2] = { (DObject*)this, (DObject*)other };
+		VMFrameStack stack;
+		stack.Call(func, params, 2, nullptr, 0, nullptr);
+	}
+	else AttachToOwner(other);
+}
+
+
 //===========================================================================
 //
 // AInventory :: DetachFromOwner
@@ -1589,6 +1707,29 @@ 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 };
+		VMFrameStack stack;
+		stack.Call(func, params, 1, nullptr, 0, nullptr);
+	}
+	else DetachFromOwner();
+}
+
+//===========================================================================
+//===========================================================================
+
+
+
 IMPLEMENT_CLASS(AStateProvider, false, false)
 IMPLEMENT_CLASS(ACustomInventory, false, false)
 
diff --git a/src/g_inventory/a_pickups.h b/src/g_inventory/a_pickups.h
index b366a6031..99dad8713 100644
--- a/src/g_inventory/a_pickups.h
+++ b/src/g_inventory/a_pickups.h
@@ -70,31 +70,87 @@ class AInventory : public AActor
 	DECLARE_CLASS_WITH_META(AInventory, AActor, PClassInventory)
 	HAS_OBJECT_POINTERS
 public:
-	virtual void Touch (AActor *toucher);
 	
-	virtual void Serialize(FSerializer &arc);
-
-	virtual void MarkPrecacheSounds() const;
-	virtual void BeginPlay ();
+	virtual void Touch (AActor *toucher) override;
+	virtual void Serialize(FSerializer &arc) override;
+	virtual void MarkPrecacheSounds() const override;
+	virtual void BeginPlay () override;
 	virtual void Destroy () override;
+	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.
 	virtual void DepleteOrDestroy ();
-	virtual void Tick ();
 	virtual bool ShouldRespawn ();
-	virtual bool ShouldStay ();
-	virtual void Hide ();
-	bool CallTryPickup (AActor *toucher, AActor **toucher_return = NULL);
 	virtual void DoPickupSpecial (AActor *toucher);
+
+	// methods that can be overridden by scripts, plus their callers.
 	virtual bool SpecialDropAction (AActor *dropper);
 	bool CallSpecialDropAction(AActor *dropper);
-	virtual bool DrawPowerup (int x, int y);
-	virtual void DoEffect ();
-	virtual bool Grind(bool items);
 
-	virtual FString PickupMessage ();
+	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();
+
+	virtual FString PickupMessage();
 	FString GetPickupMessage();
-	virtual void PlayPickupSound (AActor *toucher);
 
-	bool DoRespawn ();
+	virtual bool HandlePickup(AInventory *item);
+	bool CallHandlePickup(AInventory *item);
+
+	virtual bool Use(bool pickup);
+	bool CallUse(bool pickup);
+
+	virtual PalEntry GetBlend();
+	PalEntry CallGetBlend();
+
+	virtual bool ShouldStay();
+	bool CallShouldStay();
+
+	virtual void DoEffect();
+	void CallDoEffect();
+
+	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);
+	virtual 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 int AlterWeaponSprite(visstyle_t *vis);
+
+
+	// virtual on the script side only.
+	double GetSpeedFactor();
+	bool GetNoTeleportFreeze();
+	// Stuff for later when more features are exported.
+	virtual void Travelled();
+	virtual void OwnerDied();
+
+
+	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.
@@ -113,34 +169,7 @@ public:
 
 	FSoundIDNoInit PickupSound;
 
-	void BecomeItem ();
-	void BecomePickup ();
-	virtual void AttachToOwner (AActor *other);
-	virtual void DetachFromOwner ();
-	virtual AInventory *CreateCopy (AActor *other);
-	AInventory *CallCreateCopy(AActor *other);
-	virtual AInventory *CreateTossable ();
-	AInventory *CallCreateTossable();
-	virtual bool GoAway ();
-	virtual void GoAwayAndDie ();
-	virtual bool HandlePickup (AInventory *item);
-	bool CallHandlePickup(AInventory *item);
-	virtual bool Use (bool pickup);
-	bool CallUse(bool pickup);
-	virtual void Travelled ();
-	virtual void OwnerDied ();
 
-	virtual void AbsorbDamage (int damage, FName damageType, int &newdamage);
-	virtual void ModifyDamage (int damage, FName damageType, int &newdamage, bool passive);
-	virtual double GetSpeedFactor();
-	virtual bool GetNoTeleportFreeze();
-	virtual int AlterWeaponSprite (visstyle_t *vis);
-
-	virtual PalEntry GetBlend ();
-	PalEntry CallGetBlend();
-
-	virtual bool TryPickup (AActor *&toucher);
-	virtual bool TryPickupRestricted (AActor *&toucher);
 protected:
 	bool CanPickup(AActor * toucher);
 	void GiveQuest(AActor * toucher);
@@ -164,9 +193,9 @@ public:
 	// This is used when an inventory item's use state sequence is executed.
 	bool CallStateChain (AActor *actor, FState *state);
 
-	bool TryPickup (AActor *&toucher);
-	bool Use (bool pickup);
-	bool SpecialDropAction (AActor *dropper);
+	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/g_inventory/a_weaponpiece.cpp b/src/g_inventory/a_weaponpiece.cpp
index b504559bd..e1a7fe33b 100644
--- a/src/g_inventory/a_weaponpiece.cpp
+++ b/src/g_inventory/a_weaponpiece.cpp
@@ -103,7 +103,7 @@ void AWeaponPiece::Serialize(FSerializer &arc)
 bool AWeaponPiece::TryPickupRestricted (AActor *&toucher)
 {
 	// Wrong class, but try to pick up for ammo
-	if (ShouldStay())
+	if (CallShouldStay())
 	{ // Can't pick up weapons for other classes in coop netplay
 		return false;
 	}
diff --git a/src/g_inventory/a_weaponpiece.h b/src/g_inventory/a_weaponpiece.h
index bcaa261cb..e0db15007 100644
--- a/src/g_inventory/a_weaponpiece.h
+++ b/src/g_inventory/a_weaponpiece.h
@@ -19,12 +19,12 @@ protected:
 	bool PrivateShouldStay ();
 public:
 	
-	void Serialize(FSerializer &arc);
-	bool TryPickup (AActor *&toucher);
-	bool TryPickupRestricted (AActor *&toucher);
-	bool ShouldStay ();
-	virtual FString PickupMessage ();
-	virtual void PlayPickupSound (AActor *toucher);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual bool TryPickup (AActor *&toucher) override;
+	virtual bool TryPickupRestricted (AActor *&toucher) override;
+	virtual bool ShouldStay () override;
+	virtual FString PickupMessage () override;
+	virtual void PlayPickupSound (AActor *toucher) override;
 
 	int PieceValue;
 	PClassActor *WeaponClass;
@@ -42,5 +42,5 @@ public:
 	PClassActor * PieceWeapon;
 
 	
-	void Serialize(FSerializer &arc);
+	virtual void Serialize(FSerializer &arc) override;
 };
diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp
index 90a217495..3fd4bc939 100644
--- a/src/g_inventory/a_weapons.cpp
+++ b/src/g_inventory/a_weapons.cpp
@@ -275,7 +275,7 @@ void AWeapon::MarkPrecacheSounds() const
 bool AWeapon::TryPickupRestricted (AActor *&toucher)
 {
 	// Wrong class, but try to pick up for ammo
-	if (ShouldStay())
+	if (CallShouldStay())
 	{ // Can't pick up weapons for other classes in coop netplay
 		return false;
 	}
@@ -398,7 +398,7 @@ bool AWeapon::PickupForAmmo (AWeapon *ownedWeapon)
 	bool gotstuff = false;
 
 	// Don't take ammo if the weapon sticks around.
-	if (!ShouldStay ())
+	if (!CallShouldStay ())
 	{
 		int oldamount1 = 0;
 		int oldamount2 = 0;
diff --git a/src/g_inventory/a_weapons.h b/src/g_inventory/a_weapons.h
index 786f6189b..c6424075d 100644
--- a/src/g_inventory/a_weapons.h
+++ b/src/g_inventory/a_weapons.h
@@ -137,26 +137,30 @@ public:
 
 	virtual void MarkPrecacheSounds() const;
 	
-	virtual void Serialize(FSerializer &arc);
-	virtual bool ShouldStay ();
-	virtual void AttachToOwner (AActor *other);
-	virtual bool HandlePickup (AInventory *item);
-	virtual AInventory *CreateCopy (AActor *other);
-	virtual AInventory *CreateTossable ();
-	virtual bool TryPickup (AActor *&toucher);
-	virtual bool TryPickupRestricted (AActor *&toucher);
-	virtual bool PickupForAmmo (AWeapon *ownedWeapon);
-	virtual bool Use (bool pickup);
+	virtual void Serialize(FSerializer &arc) override;
+	virtual bool ShouldStay () override;
+	virtual void AttachToOwner (AActor *other) override;
+	virtual bool HandlePickup (AInventory *item) override;
+	virtual AInventory *CreateCopy (AActor *other) override;
+	virtual AInventory *CreateTossable () override;
+	virtual bool TryPickup (AActor *&toucher) override;
+	virtual bool TryPickupRestricted (AActor *&toucher) override;
+	virtual bool Use (bool pickup) override;
 	virtual void Destroy() override;
 
+	bool PickupForAmmo(AWeapon *ownedWeapon);
+	void PostMorphWeapon();
+
+	// scripted virtuals.
 	FState *GetUpState ();
 	FState *GetDownState ();
 	FState *GetReadyState ();
 	FState *GetAtkState (bool hold);
 	FState *GetAltAtkState (bool hold);
+	
 	FState *GetStateForButtonName (FName button);
 
-	virtual void PostMorphWeapon ();
+
 	virtual void EndPowerup ();
 	void CallEndPowerup();
 
@@ -221,9 +225,8 @@ class AWeaponGiver : public AWeapon
 	DECLARE_CLASS(AWeaponGiver, AWeapon)
 
 public:
-	bool TryPickup(AActor *&toucher);
-	
-	void Serialize(FSerializer &arc);
+	virtual bool TryPickup(AActor *&toucher) override;
+	virtual void Serialize(FSerializer &arc) override;
 
 	double DropAmmoFactor;
 };
diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp
index ad15cb081..f22318839 100644
--- a/src/g_shared/a_morph.cpp
+++ b/src/g_shared/a_morph.cpp
@@ -138,20 +138,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
 		AInventory *next = item->Inventory;
 		if (item->IsKindOf (RUNTIME_CLASS(AArmor)))
 		{
-			if (item->IsKindOf (RUNTIME_CLASS(AHexenArmor)))
-			{
-				// Set the HexenArmor slots to 0, except the class slot.
-				AHexenArmor *hxarmor = static_cast<AHexenArmor *>(item);
-				hxarmor->Slots[0] = 0;
-				hxarmor->Slots[1] = 0;
-				hxarmor->Slots[2] = 0;
-				hxarmor->Slots[3] = 0;
-				hxarmor->Slots[4] = spawntype->HexenArmor[0];
-			}
-			else
-			{
-				item->DepleteOrDestroy();
-			}
+			item->DepleteOrDestroy();
 		}
 		item = next;
 	}
diff --git a/wadsrc/static/zscript/shared/inventory.txt b/wadsrc/static/zscript/shared/inventory.txt
index 4e5c13d88..06e4d7552 100644
--- a/wadsrc/static/zscript/shared/inventory.txt
+++ b/wadsrc/static/zscript/shared/inventory.txt
@@ -32,7 +32,15 @@ class Inventory : Actor native
 	virtual native Inventory CreateTossable();
 	virtual native bool SpecialDropAction (Actor dropper);
 	virtual native String PickupMessage();
+	virtual native bool ShouldStay();
+	virtual native void DoEffect();
+	virtual native void PlayPickupSound(Actor user);
+	virtual native void AttachToOwner(Actor user);
+	virtual native void DetachFromOwner();
 	
+	virtual double GetSpeedFactor() { return 1; }
+	virtual bool GetNoTeleportFreeze() { return false; }
+
 	native void GoAwayAndDie();
 	native void BecomeItem();
 	native void BecomePickup();
@@ -42,7 +50,10 @@ class Inventory : Actor native
 	private native void A_RestoreSpecialThing1();
 	private native void A_RestoreSpecialThing2();
 
-	virtual native bool TryPickup(in out Actor toucher);
+	// 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);
 
 	States(Actor, Overlay, Weapon, Item)
diff --git a/wadsrc/static/zscript/shared/powerups.txt b/wadsrc/static/zscript/shared/powerups.txt
index 4c7a84efe..0cd735c81 100644
--- a/wadsrc/static/zscript/shared/powerups.txt
+++ b/wadsrc/static/zscript/shared/powerups.txt
@@ -22,6 +22,10 @@ class Powerup : Inventory native
 	native color BlendColor;
 	native Name Mode;			// Meaning depends on powerup - used for Invulnerability and Invisibility
 	native double Strength;		// Meaning depends on powerup - currently used only by Invisibility
+
+	// Note, that while this is an inventory flag, it only has meaning on an active powerup.
+	virtual bool GetNoTeleportFreeze() { return bNoTeleportFreeze; }
+	
 }
 
 class PowerInvulnerable : Powerup native
@@ -136,6 +140,8 @@ class PowerSpeed : Powerup native
 		Inventory.Icon "SPBOOT0";
 		+INVENTORY.NOTELEPORTFREEZE
 	}
+	
+	override double GetSpeedFactor() { return Speed; }
 }
 
 // Player Speed Trail (used by the Speed Powerup) ----------------------------