diff --git a/OpenRA.Mods.Common/Traits/AffectsShroud.cs b/OpenRA.Mods.Common/Traits/AffectsShroud.cs index 1bb1143951..67bb82b4b7 100644 --- a/OpenRA.Mods.Common/Traits/AffectsShroud.cs +++ b/OpenRA.Mods.Common/Traits/AffectsShroud.cs @@ -32,7 +32,8 @@ namespace OpenRA.Mods.Common.Traits public readonly VisibilityType Type = VisibilityType.Footprint; } - public abstract class AffectsShroud : ConditionalTrait, ITick, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld, INotifyMoving + public abstract class AffectsShroud : ConditionalTrait, ISync, INotifyAddedToWorld, + INotifyRemovedFromWorld, INotifyMoving, INotifyVisualPositionChanged, ITick { static readonly PPos[] NoCells = { }; @@ -47,7 +48,6 @@ namespace OpenRA.Mods.Common.Traits [Sync] protected bool CachedTraitDisabled { get; private set; } - bool dirty; WPos cachedPos; protected abstract void AddCellsToPlayerShroud(Actor self, Player player, PPos[] uv); @@ -86,7 +86,7 @@ namespace OpenRA.Mods.Common.Traits .ToArray(); } - void ITick.Tick(Actor self) + void INotifyVisualPositionChanged.VisualPositionChanged(Actor self, byte oldLayer, byte newLayer) { if (!self.IsInWorld) return; @@ -94,22 +94,37 @@ namespace OpenRA.Mods.Common.Traits var centerPosition = self.CenterPosition; var projectedPos = centerPosition - new WVec(0, centerPosition.Z, centerPosition.Z); var projectedLocation = self.World.Map.CellContaining(projectedPos); - var traitDisabled = IsTraitDisabled; - var range = Range; var pos = self.CenterPosition; - if (Info.MoveRecalculationThreshold.Length > 0 && (pos - cachedPos).LengthSquared > Info.MoveRecalculationThreshold.LengthSquared) - dirty = true; + var dirty = Info.MoveRecalculationThreshold.Length > 0 && (pos - cachedPos).LengthSquared > Info.MoveRecalculationThreshold.LengthSquared; + if (!dirty && cachedLocation == projectedLocation) + return; - if (!dirty && cachedLocation == projectedLocation && cachedRange == range && traitDisabled == CachedTraitDisabled) + cachedLocation = projectedLocation; + cachedPos = pos; + + UpdateShroudCells(self); + } + + void ITick.Tick(Actor self) + { + if (!self.IsInWorld) + return; + + var traitDisabled = IsTraitDisabled; + var range = Range; + + if (cachedRange == range && traitDisabled == CachedTraitDisabled) return; cachedRange = range; - cachedLocation = projectedLocation; CachedTraitDisabled = traitDisabled; - cachedPos = pos; - dirty = false; + UpdateShroudCells(self); + } + + void UpdateShroudCells(Actor self) + { var cells = ProjectedCells(self); foreach (var p in self.World.Players) { @@ -123,6 +138,7 @@ namespace OpenRA.Mods.Common.Traits var centerPosition = self.CenterPosition; var projectedPos = centerPosition - new WVec(0, centerPosition.Z, centerPosition.Z); cachedLocation = self.World.Map.CellContaining(projectedPos); + cachedPos = centerPosition; CachedTraitDisabled = IsTraitDisabled; var cells = ProjectedCells(self); @@ -141,8 +157,18 @@ namespace OpenRA.Mods.Common.Traits void INotifyMoving.MovementTypeChanged(Actor self, MovementType type) { // Recalculate the visiblity at our final stop position - if (type == MovementType.None) - dirty = true; + if (type == MovementType.None && self.IsInWorld) + { + var centerPosition = self.CenterPosition; + var projectedPos = centerPosition - new WVec(0, centerPosition.Z, centerPosition.Z); + var projectedLocation = self.World.Map.CellContaining(projectedPos); + var pos = self.CenterPosition; + + cachedLocation = projectedLocation; + cachedPos = pos; + + UpdateShroudCells(self); + } } } } diff --git a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs index 1a2b372b78..b332635995 100644 --- a/OpenRA.Mods.Common/Traits/Air/Aircraft.cs +++ b/OpenRA.Mods.Common/Traits/Air/Aircraft.cs @@ -200,6 +200,7 @@ namespace OpenRA.Mods.Common.Traits IDisposable reservation; IEnumerable speedModifiers; INotifyMoving[] notifyMoving; + INotifyVisualPositionChanged[] notifyVisualPositionChanged; IOverrideAircraftLanding overrideAircraftLanding; [Sync] @@ -218,6 +219,8 @@ namespace OpenRA.Mods.Common.Traits bool requireForceMove; int creationActivityDelay; + bool notify = true; + public static WPos GroundPosition(Actor self) { return self.CenterPosition - new WVec(WDist.Zero, WDist.Zero, self.World.Map.DistanceAboveTerrain(self.CenterPosition)); @@ -296,7 +299,7 @@ namespace OpenRA.Mods.Common.Traits notifyMoving = self.TraitsImplementing().ToArray(); positionOffsets = self.TraitsImplementing().ToArray(); overrideAircraftLanding = self.TraitOrDefault(); - + notifyVisualPositionChanged = self.TraitsImplementing().ToArray(); base.Created(self); } @@ -382,7 +385,12 @@ namespace OpenRA.Mods.Common.Traits return; var speed = Info.RepulsionSpeed != -1 ? Info.RepulsionSpeed : MovementSpeed; + + // HACK: Prevent updating visibility twice per tick. We really shouldn't be + // moving twice in a tick in the first place. + notify = false; SetPosition(self, CenterPosition + FlyStep(speed, repulsionForce.Yaw.Facing)); + notify = true; } public virtual WVec GetRepulsionForce() @@ -732,6 +740,11 @@ namespace OpenRA.Mods.Common.Traits else if (!isCruising && cruising) OnCruisingAltitudeLeft(); + // NB: This can be called from the constructor before notifyVisualPositionChanged is assigned. + if (notify && notifyVisualPositionChanged != null) + foreach (var n in notifyVisualPositionChanged) + n.VisualPositionChanged(self, 0, 0); + FinishedMoving(self); }