diff --git a/OpenRA.Game/Activities/Activity.cs b/OpenRA.Game/Activities/Activity.cs
index 9dec312bde..efa4a5c7db 100644
--- a/OpenRA.Game/Activities/Activity.cs
+++ b/OpenRA.Game/Activities/Activity.cs
@@ -200,6 +200,24 @@ namespace OpenRA.Activities
///
protected virtual void OnLastRun(Actor self) { }
+ ///
+ /// Runs once on Actor.Dispose() (through OnActorDisposeOuter) and can be used to perform activity clean-up on actor death/disposal,
+ /// for example by force-triggering OnLastRun (which would otherwise be skipped).
+ ///
+ protected virtual void OnActorDispose(Actor self) { }
+
+ ///
+ /// Runs once on Actor.Dispose().
+ /// Main purpose is to ensure ChildActivity.OnActorDispose runs as well (which isn't otherwise accessible due to protection level).
+ ///
+ internal void OnActorDisposeOuter(Actor self)
+ {
+ if (ChildActivity != null)
+ ChildActivity.OnActorDisposeOuter(self);
+
+ OnActorDispose(self);
+ }
+
public virtual bool Cancel(Actor self, bool keepQueue = false)
{
if (!IsInterruptible)
diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs
index 7e9b402620..f5bfca8f6f 100644
--- a/OpenRA.Game/Actor.cs
+++ b/OpenRA.Game/Actor.cs
@@ -265,6 +265,11 @@ namespace OpenRA
public void Dispose()
{
+ // If CurrentActivity isn't null, run OnActorDisposeOuter in case some cleanups are needed.
+ // This should be done before the FrameEndTask to avoid dependency issues.
+ if (CurrentActivity != null)
+ CurrentActivity.RootActivity.OnActorDisposeOuter(this);
+
World.AddFrameEndTask(w =>
{
if (Disposed)