diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs index 9e34ddadb8..1592033307 100644 --- a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -46,7 +46,17 @@ namespace OpenRA.Traits public DamageState DamageState { get; private set; } readonly IHealth health; + // The Visible flag is tied directly to the actor visibility under the fog. + // If Visible is true, the actor is made invisible (via FrozenUnderFog/IDefaultVisibility) + // and this FrozenActor is rendered instead. + // The Hidden flag covers the edge case that occurs when the backing actor was last "seen" + // to be cloaked or otherwise not CanBeViewedByPlayer()ed. Setting Visible to true when + // the actor is hidden under the fog would leak the actors position via the tooltips and + // AutoTargetability, and keeping Visible as false would cause the actor to be rendered + // under the fog. public bool Visible = true; + public bool Hidden = false; + public bool Shrouded { get; private set; } public bool NeedRenderables { get; set; } public IRenderable[] Renderables = NoRenderables; @@ -101,6 +111,7 @@ namespace OpenRA.Traits { Owner = actor.Owner; TargetTypes = actor.GetEnabledTargetTypes(); + Hidden = !actor.CanBeViewedByPlayer(viewer); if (health != null) { @@ -145,6 +156,11 @@ namespace OpenRA.Traits NeedRenderables |= Visible && !wasVisible; } + public void Invalidate() + { + Owner = null; + } + public void Flash() { flashTicks = 5; diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs index 35ad5b0b54..390360d666 100644 --- a/OpenRA.Game/Traits/Target.cs +++ b/OpenRA.Game/Traits/Target.cs @@ -80,13 +80,21 @@ namespace OpenRA.Traits public bool IsValidFor(Actor targeter) { - if (targeter == null || Type == TargetType.Invalid) + if (targeter == null) return false; - if (actor != null && !actor.IsTargetableBy(targeter)) - return false; - - return true; + switch (Type) + { + case TargetType.Actor: + return actor.IsTargetableBy(targeter); + case TargetType.FrozenActor: + return frozen.IsValid && frozen.Visible && !frozen.Hidden; + case TargetType.Invalid: + return false; + default: + case TargetType.Terrain: + return true; + } } // Currently all or nothing. diff --git a/OpenRA.Mods.Cnc/Traits/FrozenUnderFogUpdatedByGps.cs b/OpenRA.Mods.Cnc/Traits/FrozenUnderFogUpdatedByGps.cs index f3082d65bf..927d1d2877 100644 --- a/OpenRA.Mods.Cnc/Traits/FrozenUnderFogUpdatedByGps.cs +++ b/OpenRA.Mods.Cnc/Traits/FrozenUnderFogUpdatedByGps.cs @@ -39,6 +39,7 @@ namespace OpenRA.Mods.Cnc.Traits static readonly FrozenActorAction Remove = (fufubg, fal, gps, fa) => { // Removes the frozen actor. Once done, we no longer need to track GPS updates. + fa.Invalidate(); fal.Remove(fa); gps.UnregisterForOnGpsRefreshed(fufubg.self, fufubg); }; diff --git a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs index b4e8d2be7d..3fcac99216 100644 --- a/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.Common/Traits/Modifiers/FrozenUnderFog.cs @@ -68,11 +68,13 @@ namespace OpenRA.Mods.Common.Traits frozenStates = new PlayerDictionary(self.World, (player, playerIndex) => { var frozenActor = new FrozenActor(self, footprint, player, startsRevealed); - if (startsRevealed) - UpdateFrozenActor(self, frozenActor, playerIndex); player.PlayerActor.Trait().Add(frozenActor); return new FrozenState(frozenActor) { IsVisible = startsRevealed }; }); + + if (startsRevealed) + for (var playerIndex = 0; playerIndex < frozenStates.Count; playerIndex++) + UpdateFrozenActor(self, frozenStates[playerIndex].FrozenActor, playerIndex); } void UpdateFrozenActor(Actor self, FrozenActor frozenActor, int playerIndex) diff --git a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs index 6fa70b7b2e..621e8938ee 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -246,26 +246,20 @@ namespace OpenRA.Mods.Common.Widgets } var frozen = world.ScreenMap.FrozenActorsAtMouse(world.RenderPlayer, worldPixel) - .Where(a => a.TooltipInfo != null && a.IsValid) + .Where(a => a.TooltipInfo != null && a.IsValid && a.Visible && !a.Hidden) .WithHighestSelectionPriority(worldPixel); if (frozen != null) { - var actor = frozen.Actor; + FrozenActorTooltip = frozen; - // HACK: This leaks the cloak state through the fog (cloaked buildings will not show tooltips) - if (actor == null || actor.TraitsImplementing().All(t => t.IsVisible(actor, world.RenderPlayer))) - { - FrozenActorTooltip = frozen; + // HACK: This leaks the tooltip state through the fog + // This will cause issues for any downstream mods that use IProvideTooltipInfo on enemy actors + if (frozen.Actor != null) + ActorTooltipExtra = frozen.Actor.TraitsImplementing().ToArray(); - // HACK: This leaks the tooltip state through the fog - // This will cause issues for any downstream mods that use IProvideTooltipInfo on enemy actors - if (frozen.Actor != null) - ActorTooltipExtra = frozen.Actor.TraitsImplementing().ToArray(); - - TooltipType = WorldTooltipType.FrozenActor; - return; - } + TooltipType = WorldTooltipType.FrozenActor; + return; } if (resourceLayer != null)