diff --git a/OpenRA.Game/Game.cs b/OpenRA.Game/Game.cs index 4693533a10..36d6723990 100644 --- a/OpenRA.Game/Game.cs +++ b/OpenRA.Game/Game.cs @@ -191,7 +191,7 @@ namespace OpenRA if (orderManager.NetFrameNumber == 0) orderManager.LastTickTime = Environment.TickCount; - world.TickRender(worldRenderer); + Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer)); viewport.Tick(); } } diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 019e869c8b..91efe67c56 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -48,12 +48,15 @@ namespace OpenRA.GameRules [Desc("Whether we should prevent prone response for infantry.")] public readonly bool PreventProne = false; - public float EffectivenessAgainst(Actor self) + public float EffectivenessAgainst(ActorInfo ai) { - var health = self.Info.Traits.GetOrDefault(); - if (health == null) return 0f; - var armor = self.Info.Traits.GetOrDefault(); - if (armor == null || armor.Type == null) return 1; + var health = ai.Traits.GetOrDefault(); + if (health == null) + return 0f; + + var armor = ai.Traits.GetOrDefault(); + if (armor == null || armor.Type == null) + return 1; float versus; return Versus.TryGetValue(armor.Type, out versus) ? versus : 1; @@ -140,20 +143,34 @@ namespace OpenRA.GameRules if (targetable == null || !ValidTargets.Intersect(targetable.TargetTypes).Any()) return false; - if (Warheads.All(w => w.EffectivenessAgainst(a) <= 0)) + if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0)) return false; return true; } - public bool IsValidAgainst(Target target, World world) + public bool IsValidAgainst(FrozenActor a) { - if (!target.IsValid) + var targetable = a.Info.Traits.GetOrDefault(); + if (targetable == null || !ValidTargets.Intersect(targetable.GetTargetTypes()).Any()) return false; - if (target.IsActor) + if (Warheads.All(w => w.EffectivenessAgainst(a.Info) <= 0)) + return false; + + return true; + } + + + public bool IsValidAgainst(Target target, World world) + { + if (target.Type == TargetType.Actor) return IsValidAgainst(target.Actor); - else + + if (target.Type == TargetType.FrozenActor) + return IsValidAgainst(target.FrozenActor); + + if (target.Type == TargetType.Terrain) { var cell = target.CenterPosition.ToCPos(); if (ValidTargets.Contains("Ground") && world.GetTerrainType(cell) != "Water") @@ -164,6 +181,8 @@ namespace OpenRA.GameRules return false; } + + return false; } } } diff --git a/OpenRA.Game/Graphics/WorldRenderer.cs b/OpenRA.Game/Graphics/WorldRenderer.cs index fe75a8fa0c..bfd1a91990 100644 --- a/OpenRA.Game/Graphics/WorldRenderer.cs +++ b/OpenRA.Game/Graphics/WorldRenderer.cs @@ -73,9 +73,15 @@ namespace OpenRA.Graphics var bounds = Game.viewport.WorldBounds(world); var comparer = new RenderableComparer(this); - var actors = world.FindActorsInBox( - bounds.TopLeftAsCPos(), - bounds.BottomRightAsCPos()); + var tl = bounds.TopLeftAsCPos(); + var br = bounds.BottomRightAsCPos(); + var actors = world.FindActorsInBox(tl, br) + .Append(world.WorldActor) + .ToList(); + + // Include player actor for the rendered player + if (world.RenderPlayer != null) + actors.Add(world.RenderPlayer.PlayerActor); var worldRenderables = actors.SelectMany(a => a.Render(this)); if (world.OrderGenerator != null) diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index 90ec7d645a..fd0c4d7c46 100755 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -23,6 +23,7 @@ namespace OpenRA TargetString = 0x04, Queued = 0x08, ExtraLocation = 0x10, + ExtraData = 0x20 } static class OrderFieldsExts @@ -42,12 +43,13 @@ namespace OpenRA public CPos TargetLocation; public string TargetString; public CPos ExtraLocation; + public uint ExtraData; public bool IsImmediate; public Player Player { get { return Subject.Owner; } } Order(string orderString, Actor subject, - Actor targetActor, CPos targetLocation, string targetString, bool queued, CPos extraLocation) + Actor targetActor, CPos targetLocation, string targetString, bool queued, CPos extraLocation, uint extraData) { this.OrderString = orderString; this.Subject = subject; @@ -56,18 +58,19 @@ namespace OpenRA this.TargetString = targetString; this.Queued = queued; this.ExtraLocation = extraLocation; + this.ExtraData = extraData; } // For scripting special powers public Order() - : this(null, null, null, CPos.Zero, null, false, CPos.Zero) { } + : this(null, null, null, CPos.Zero, null, false, CPos.Zero, 0) { } public Order(string orderString, Actor subject, bool queued) - : this(orderString, subject, null, CPos.Zero, null, queued, CPos.Zero) { } + : this(orderString, subject, null, CPos.Zero, null, queued, CPos.Zero, 0) { } public Order(string orderstring, Order order) : this(orderstring, order.Subject, order.TargetActor, order.TargetLocation, - order.TargetString, order.Queued, order.ExtraLocation) {} + order.TargetString, order.Queued, order.ExtraLocation, order.ExtraData) {} public byte[] Serialize() { @@ -102,6 +105,7 @@ namespace OpenRA if (TargetString != null) fields |= OrderFields.TargetString; if (Queued) fields |= OrderFields.Queued; if (ExtraLocation != CPos.Zero) fields |= OrderFields.ExtraLocation; + if (ExtraData != 0) fields |= OrderFields.ExtraData; w.Write((byte)fields); @@ -113,6 +117,8 @@ namespace OpenRA w.Write(TargetString); if (ExtraLocation != CPos.Zero) w.Write(ExtraLocation.ToInt2()); + if (ExtraData != 0) + w.Write(ExtraData); return ret.ToArray(); } @@ -134,12 +140,13 @@ namespace OpenRA var targetString = flags.HasField(OrderFields.TargetString) ? r.ReadString() : null; var queued = flags.HasField(OrderFields.Queued); var extraLocation = (CPos)(flags.HasField(OrderFields.ExtraLocation) ? r.ReadInt2() : int2.Zero); + var extraData = flags.HasField(OrderFields.ExtraData) ? r.ReadUInt32() : 0; Actor subject, targetActor; - if( !TryGetActorFromUInt( world, subjectId, out subject ) || !TryGetActorFromUInt( world, targetActorId, out targetActor ) ) + if (!TryGetActorFromUInt(world, subjectId, out subject) || !TryGetActorFromUInt(world, targetActorId, out targetActor)) return null; - return new Order( order, subject, targetActor, targetLocation, targetString, queued, extraLocation); + return new Order(order, subject, targetActor, targetLocation, targetString, queued, extraLocation, extraData); } case 0xfe: diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index e3b4c1c2d7..e44917e4c5 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -234,6 +234,7 @@ + diff --git a/OpenRA.Game/Orders/UnitOrderGenerator.cs b/OpenRA.Game/Orders/UnitOrderGenerator.cs index 2c2a75950d..d322129ffe 100644 --- a/OpenRA.Game/Orders/UnitOrderGenerator.cs +++ b/OpenRA.Game/Orders/UnitOrderGenerator.cs @@ -21,11 +21,23 @@ namespace OpenRA.Orders { var underCursor = world.FindUnitsAtMouse(mi.Location) .Where(a => a.HasTrait()) - .OrderByDescending(a => a.SelectionPriority()) + .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); + Target target; + if (underCursor != null) + target = Target.FromActor(underCursor); + else + { + var frozen = world.FindFrozenActorsAtMouse(mi.Location) + .Where(a => a.Info.Traits.Contains()) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); + target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(xy); + } + var orders = world.Selection.Actors - .Select(a => OrderForUnit(a, xy, mi, underCursor)) + .Select(a => OrderForUnit(a, target, mi)) .Where(o => o != null) .ToArray(); @@ -51,7 +63,7 @@ namespace OpenRA.Orders var underCursor = world.FindUnitsAtMouse(mi.Location) .Where(a => a.HasTrait()) - .OrderByDescending(a => a.SelectionPriority()) + .OrderByDescending(a => a.Info.SelectionPriority()) .FirstOrDefault(); if (underCursor != null && (mi.Modifiers.HasModifier(Modifiers.Shift) || !world.Selection.Actors.Any())) @@ -61,8 +73,20 @@ namespace OpenRA.Orders useSelect = true; } + Target target; + if (underCursor != null) + target = Target.FromActor(underCursor); + else + { + var frozen = world.FindFrozenActorsAtMouse(mi.Location) + .Where(a => a.Info.Traits.Contains()) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); + target = frozen != null ? Target.FromFrozenActor(frozen) : Target.FromCell(xy); + } + var orders = world.Selection.Actors - .Select(a => OrderForUnit(a, xy, mi, underCursor)) + .Select(a => OrderForUnit(a, target, mi)) .Where(o => o != null) .ToArray(); @@ -70,12 +94,12 @@ namespace OpenRA.Orders return cursorName ?? (useSelect ? "select" : "default"); } - static UnitOrderResult OrderForUnit(Actor self, CPos xy, MouseInput mi, Actor underCursor) + static UnitOrderResult OrderForUnit(Actor self, Target target, MouseInput mi) { if (self.Owner != self.World.LocalPlayer) return null; - if (self.Destroyed) + if (self.Destroyed || target.Type == TargetType.Invalid) return null; if (mi.Button == Game.mouseButtonPreference.Action) @@ -85,7 +109,7 @@ namespace OpenRA.Orders .Select(x => new { Trait = trait, Order = x } )) .OrderByDescending(x => x.Order.OrderPriority)) { - var actorsAt = self.World.ActorMap.GetUnitsAt(xy).ToList(); + var actorsAt = self.World.ActorMap.GetUnitsAt(target.CenterPosition.ToCPos()).ToList(); var modifiers = TargetModifiers.None; if (mi.Modifiers.HasModifier(Modifiers.Ctrl)) @@ -96,11 +120,8 @@ namespace OpenRA.Orders modifiers |= TargetModifiers.ForceMove; string cursor = null; - if (underCursor != null) - if (o.Order.CanTargetActor(self, underCursor, modifiers, ref cursor)) - return new UnitOrderResult(self, o.Order, o.Trait, cursor, Target.FromActor(underCursor)); - if (o.Order.CanTargetLocation(self, xy, actorsAt, modifiers, ref cursor)) - return new UnitOrderResult(self, o.Order, o.Trait, cursor, Target.FromCell(xy)); + if (o.Order.CanTarget(self, target, actorsAt, modifiers, ref cursor)) + return new UnitOrderResult(self, o.Order, o.Trait, cursor, target); } } @@ -137,9 +158,9 @@ namespace OpenRA.Orders public static class SelectableExts { - public static int SelectionPriority(this Actor a) + public static int SelectionPriority(this ActorInfo a) { - var selectableInfo = a.Info.Traits.GetOrDefault(); + var selectableInfo = a.Traits.GetOrDefault(); return selectableInfo != null ? selectableInfo.Priority : int.MinValue; } } diff --git a/OpenRA.Game/Traits/DrawLineToTarget.cs b/OpenRA.Game/Traits/DrawLineToTarget.cs index a241af6715..319bf94fce 100644 --- a/OpenRA.Game/Traits/DrawLineToTarget.cs +++ b/OpenRA.Game/Traits/DrawLineToTarget.cs @@ -109,8 +109,10 @@ namespace OpenRA.Traits self.World.AddFrameEndTask(w => { - if (self.Destroyed) return; - if (target.IsActor && display) + if (self.Destroyed) + return; + + if (target.Type == TargetType.Actor && display) w.Add(new FlashTarget(target.Actor)); var line = self.TraitOrDefault(); @@ -118,6 +120,24 @@ namespace OpenRA.Traits line.SetTarget(self, target, color, display); }); } + + public static void SetTargetLine(this Actor self, FrozenActor target, Color color, bool display) + { + if (self.Owner != self.World.LocalPlayer) + return; + + self.World.AddFrameEndTask(w => + { + if (self.Destroyed) + return; + + target.Flash(); + + var line = self.TraitOrDefault(); + if (line != null) + line.SetTarget(self, Target.FromPos(target.CenterPosition), color, display); + }); + } } } diff --git a/OpenRA.Game/Traits/Player/FrozenActorLayer.cs b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs new file mode 100755 index 0000000000..850d69d865 --- /dev/null +++ b/OpenRA.Game/Traits/Player/FrozenActorLayer.cs @@ -0,0 +1,173 @@ +#region Copyright & License Information +/* + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.FileFormats; +using OpenRA.Graphics; +using OpenRA.Traits; + +namespace OpenRA.Traits +{ + public class FrozenActorLayerInfo : ITraitInfo + { + [Desc("Size of partition bins (screen pixels)")] + public readonly int BinSize = 250; + + public object Create(ActorInitializer init) { return new FrozenActorLayer(init.world, this); } + } + + public class FrozenActor + { + public readonly IEnumerable Footprint; + public readonly WPos CenterPosition; + public readonly Rectangle Bounds; + readonly Actor actor; + + public IRenderable[] Renderables { set; private get; } + public Player Owner; + + public string TooltipName; + public Player TooltipOwner; + + public int HP; + public DamageState DamageState; + + public bool Visible; + + public FrozenActor(Actor self, IEnumerable footprint) + { + actor = self; + Footprint = footprint; + CenterPosition = self.CenterPosition; + Bounds = self.Bounds.Value; + } + + public uint ID { get { return actor.ActorID; } } + public bool IsValid { get { return Owner != null; } } + public ActorInfo Info { get { return actor.Info; } } + public Actor Actor { get { return !actor.IsDead() ? actor : null; } } + + int flashTicks; + public void Tick(World world, Shroud shroud) + { + Visible = !Footprint.Any(c => shroud.IsVisible(c)); + + if (flashTicks > 0) + flashTicks--; + } + + public void Flash() + { + flashTicks = 5; + } + + public IEnumerable Render(WorldRenderer wr) + { + if (Renderables == null) + return SpriteRenderable.None; + + if (flashTicks > 0 && flashTicks % 2 == 0) + { + var highlight = wr.Palette("highlight"); + return Renderables.Concat(Renderables.Where(r => !r.IsDecoration) + .Select(r => r.WithPalette(highlight))); + } + return Renderables; + } + } + + public class FrozenActorLayer : IRender, ITick, ISync + { + [Sync] public int VisibilityHash; + [Sync] public int FrozenHash; + + readonly FrozenActorLayerInfo info; + Dictionary frozen; + List[,] bins; + + public FrozenActorLayer(World world, FrozenActorLayerInfo info) + { + this.info = info; + frozen = new Dictionary(); + bins = new List[ + world.Map.MapSize.X * Game.CellSize / info.BinSize, + world.Map.MapSize.Y * Game.CellSize / info.BinSize]; + + for (var j = 0; j <= bins.GetUpperBound(1); j++) + for (var i = 0; i <= bins.GetUpperBound(0); i++) + bins[i, j] = new List(); + } + + public void Add(FrozenActor fa) + { + frozen.Add(fa.ID, fa); + + var top = (int)Math.Max(0, fa.Bounds.Top / info.BinSize); + var left = (int)Math.Max(0, fa.Bounds.Left / info.BinSize); + var bottom = (int)Math.Min(bins.GetUpperBound(1), fa.Bounds.Bottom / info.BinSize); + var right = (int)Math.Min(bins.GetUpperBound(0), fa.Bounds.Right / info.BinSize); + for (var j = top; j <= bottom; j++) + for (var i = left; i <= right; i++) + bins[i, j].Add(fa); + } + + public void Tick(Actor self) + { + var remove = new List(); + VisibilityHash = 0; + FrozenHash = 0; + + foreach (var kv in frozen) + { + FrozenHash += (int)kv.Key; + + kv.Value.Tick(self.World, self.Owner.Shroud); + if (kv.Value.Visible) + VisibilityHash += (int)kv.Key; + + if (!kv.Value.Visible && kv.Value.Actor == null) + remove.Add(kv.Key); + } + + foreach (var r in remove) + { + foreach (var bin in bins) + bin.Remove(frozen[r]); + frozen.Remove(r); + } + } + + public virtual IEnumerable Render(Actor self, WorldRenderer wr) + { + return frozen.Values + .Where(f => f.Visible) + .SelectMany(ff => ff.Render(wr)); + } + + public IEnumerable FrozenActorsAt(int2 pxPos) + { + var x = (pxPos.X / info.BinSize).Clamp(0, bins.GetUpperBound(0)); + var y = (pxPos.Y / info.BinSize).Clamp(0, bins.GetUpperBound(1)); + return bins[x, y].Where(p => p.Bounds.Contains(pxPos) && p.IsValid); + } + + public FrozenActor FromID(uint id) + { + FrozenActor ret; + if (!frozen.TryGetValue(id, out ret)) + return null; + + return ret; + } + } +} diff --git a/OpenRA.Game/Traits/Target.cs b/OpenRA.Game/Traits/Target.cs index 6611dab916..bc7ae62151 100644 --- a/OpenRA.Game/Traits/Target.cs +++ b/OpenRA.Game/Traits/Target.cs @@ -14,18 +14,20 @@ using System.Linq; namespace OpenRA.Traits { + public enum TargetType { Invalid, Actor, Terrain, FrozenActor } public struct Target { - public static readonly Target[] NoTargets = {}; - public static readonly Target None = new Target(); + public static readonly Target[] None = {}; + public static readonly Target Invalid = new Target { type = TargetType.Invalid }; + TargetType type; Actor actor; + FrozenActor frozen; WPos pos; - bool valid; int generation; - public static Target FromPos(WPos p) { return new Target { pos = p, valid = true }; } - public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, valid = true }; } + public static Target FromPos(WPos p) { return new Target { pos = p, type = TargetType.Terrain }; } + public static Target FromCell(CPos c) { return new Target { pos = c.CenterPosition, type = TargetType.Terrain }; } public static Target FromOrder(Order o) { return o.TargetActor != null @@ -38,26 +40,53 @@ namespace OpenRA.Traits return new Target { actor = a, - valid = (a != null), + type = a != null ? TargetType.Actor : TargetType.Invalid, generation = a.Generation, }; } - public bool IsValid { get { return valid && (actor == null || (actor.IsInWorld && !actor.IsDead() && actor.Generation == generation)); } } - public Actor Actor { get { return IsActor ? actor : null; } } + public static Target FromFrozenActor(FrozenActor a) { return new Target { frozen = a, type = TargetType.FrozenActor }; } - // TODO: This should return true even if the actor is destroyed - public bool IsActor { get { return actor != null && !actor.Destroyed; } } + public bool IsValid { get { return Type != TargetType.Invalid; } } + public Actor Actor { get { return actor; } } + public FrozenActor FrozenActor { get { return frozen; } } + + public TargetType Type + { + get + { + if (type == TargetType.Actor) + { + // Actor is no longer in the world + if (!actor.IsInWorld || actor.IsDead()) + return TargetType.Invalid; + + // Actor generation has changed (teleported or captured) + if (actor.Generation != generation) + return TargetType.Invalid; + } + + return type; + } + } // Representative position - see Positions for the full set of targetable positions. public WPos CenterPosition { get { - if (!IsValid) + switch (Type) + { + case TargetType.Actor: + return actor.CenterPosition; + case TargetType.FrozenActor: + return frozen.CenterPosition; + case TargetType.Terrain: + return pos; + default: + case TargetType.Invalid: throw new InvalidOperationException("Attempting to query the position of an invalid Target"); - - return actor != null ? actor.CenterPosition : pos; + } } } @@ -67,23 +96,29 @@ namespace OpenRA.Traits { get { - if (!IsValid) + switch (Type) + { + case TargetType.Actor: + var targetable = actor.TraitOrDefault(); + if (targetable == null) + return new [] { actor.CenterPosition }; + + var positions = targetable.TargetablePositions(actor); + return positions.Any() ? positions : new [] { actor.CenterPosition }; + case TargetType.FrozenActor: + return new [] { frozen.CenterPosition }; + case TargetType.Terrain: + return new [] { pos }; + default: + case TargetType.Invalid: return NoPositions; - - if (actor == null) - return new []{pos}; - - var targetable = actor.TraitOrDefault(); - if (targetable == null) - return new []{actor.CenterPosition}; - - return targetable.TargetablePositions(actor); + } } } public bool IsInRange(WPos origin, WRange range) { - if (!IsValid) + if (Type == TargetType.Invalid) return false; // Target ranges are calculated in 2D, so ignore height differences diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index a625a5e030..6b434464e7 100755 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -55,8 +55,7 @@ namespace OpenRA.Traits { string OrderID { get; } int OrderPriority { get; } - bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor); - bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor); + bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor); bool IsQueued { get; } } @@ -204,6 +203,11 @@ namespace OpenRA.Traits } public interface IBodyOrientationInfo {} + public interface ITargetableInfo + { + string[] GetTargetTypes(); + } + public interface ITargetable { string[] TargetTypes { get; } diff --git a/OpenRA.Game/Traits/Util.cs b/OpenRA.Game/Traits/Util.cs index 02f1bd4d9d..5e9119e8da 100755 --- a/OpenRA.Game/Traits/Util.cs +++ b/OpenRA.Game/Traits/Util.cs @@ -134,13 +134,7 @@ namespace OpenRA.Traits public static IEnumerable AdjacentCells(Target target) { - var cells = target.IsActor - ? target.Actor.OccupiesSpace.OccupiedCells().Select(c => c.First).ToArray() - : new CPos[] { }; - - if (cells.Length == 0) - cells = new CPos[] { target.CenterPosition.ToCPos() }; - + var cells = target.Positions.Select(p => p.ToCPos()).Distinct(); return Util.ExpandFootprint(cells, true); } } diff --git a/OpenRA.Game/Widgets/ViewportControllerWidget.cs b/OpenRA.Game/Widgets/ViewportControllerWidget.cs index 23a1a597ca..25b4970904 100755 --- a/OpenRA.Game/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Game/Widgets/ViewportControllerWidget.cs @@ -14,11 +14,12 @@ using System.Linq; using OpenRA.FileFormats; using OpenRA.GameRules; using OpenRA.Graphics; +using OpenRA.Orders; using OpenRA.Traits; namespace OpenRA.Widgets { - public enum WorldTooltipType { None, Unexplored, Actor } + public enum WorldTooltipType { None, Unexplored, Actor, FrozenActor } public class ViewportControllerWidget : Widget { @@ -28,6 +29,7 @@ namespace OpenRA.Widgets public WorldTooltipType TooltipType { get; private set; } public IToolTip ActorTooltip { get; private set; } + public FrozenActor FrozenActorTooltip { get; private set; } public int EdgeScrollThreshold = 15; public int EdgeCornerScrollThreshold = 35; @@ -101,13 +103,28 @@ namespace OpenRA.Widgets return; } - var actor = world.FindUnitsAtMouse(Viewport.LastMousePos).FirstOrDefault(); - if (actor == null) - return; + var underCursor = world.FindUnitsAtMouse(Viewport.LastMousePos) + .Where(a => a.HasTrait()) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); - ActorTooltip = actor.TraitsImplementing().FirstOrDefault(); - if (ActorTooltip != null) + if (underCursor != null) + { + ActorTooltip = underCursor.TraitsImplementing().First(); TooltipType = WorldTooltipType.Actor; + return; + } + + var frozen = world.FindFrozenActorsAtMouse(Viewport.LastMousePos) + .Where(a => a.TooltipName != null) + .OrderByDescending(a => a.Info.SelectionPriority()) + .FirstOrDefault(); + + if (frozen != null) + { + FrozenActorTooltip = frozen; + TooltipType = WorldTooltipType.FrozenActor; + } } public static string GetScrollCursor(Widget w, ScrollDirection edge, int2 pos) diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 965c6d8d33..94f635ba1d 100755 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -27,6 +27,20 @@ namespace OpenRA return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a)); } + public static readonly IEnumerable NoFrozenActors = new FrozenActor[0].AsEnumerable(); + public static IEnumerable FindFrozenActorsAtMouse(this World world, int2 mouseLocation) + { + if (world.RenderPlayer == null) + return NoFrozenActors; + + var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return NoFrozenActors; + + var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2(); + return frozenLayer.FrozenActorsAt(loc); + } + public static IEnumerable FindActorsInBox(this World world, CPos tl, CPos br) { return world.FindActorsInBox(tl.TopLeft, br.BottomRight); diff --git a/OpenRA.Mods.RA/Activities/Attack.cs b/OpenRA.Mods.RA/Activities/Attack.cs index c583115ed8..0d8f045cd6 100755 --- a/OpenRA.Mods.RA/Activities/Attack.cs +++ b/OpenRA.Mods.RA/Activities/Attack.cs @@ -33,7 +33,7 @@ namespace OpenRA.Mods.RA.Activities public Attack(Target target, WRange range, bool allowMovement) { Target = target; - if (target.IsActor) + if (target.Type == TargetType.Actor) targetable = target.Actor.TraitOrDefault(); Range = range; @@ -53,10 +53,11 @@ namespace OpenRA.Mods.RA.Activities if (IsCanceled) return NextActivity; - if (!Target.IsValid) + var type = Target.Type; + if (type != TargetType.Actor && type != TargetType.Terrain) return NextActivity; - if (!self.Owner.HasFogVisibility() && Target.Actor != null && Target.Actor.HasTrait() && !self.Owner.Shroud.IsTargetable(Target.Actor)) + if (type == TargetType.Actor && !self.Owner.HasFogVisibility() && Target.Actor.HasTrait() && !self.Owner.Shroud.IsTargetable(Target.Actor)) return NextActivity; if (targetable != null && !targetable.TargetableBy(Target.Actor, self)) diff --git a/OpenRA.Mods.RA/Activities/CaptureActor.cs b/OpenRA.Mods.RA/Activities/CaptureActor.cs index 91af78a7aa..ee9379d780 100644 --- a/OpenRA.Mods.RA/Activities/CaptureActor.cs +++ b/OpenRA.Mods.RA/Activities/CaptureActor.cs @@ -24,7 +24,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (!target.IsValid) + if (target.Type != TargetType.Actor) return NextActivity; var capturable = target.Actor.Trait(); diff --git a/OpenRA.Mods.RA/Activities/Demolish.cs b/OpenRA.Mods.RA/Activities/Demolish.cs index 2d9a9e0b26..5161cb7509 100644 --- a/OpenRA.Mods.RA/Activities/Demolish.cs +++ b/OpenRA.Mods.RA/Activities/Demolish.cs @@ -27,13 +27,13 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () => { // Can't demolish an already dead actor - if (!target.IsValid) + if (target.Type != TargetType.Actor) return; // Invulnerable actors can't be demolished diff --git a/OpenRA.Mods.RA/Activities/DonateSupplies.cs b/OpenRA.Mods.RA/Activities/DonateSupplies.cs index 5d82168c76..4382f60895 100644 --- a/OpenRA.Mods.RA/Activities/DonateSupplies.cs +++ b/OpenRA.Mods.RA/Activities/DonateSupplies.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid || !target.IsActor) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var targetActor = target.Actor; diff --git a/OpenRA.Mods.RA/Activities/Enter.cs b/OpenRA.Mods.RA/Activities/Enter.cs index c4afe104b5..cddb4b1cf2 100755 --- a/OpenRA.Mods.RA/Activities/Enter.cs +++ b/OpenRA.Mods.RA/Activities/Enter.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; if (!Util.AdjacentCells(target).Any(c => c == self.Location)) diff --git a/OpenRA.Mods.RA/Activities/Heal.cs b/OpenRA.Mods.RA/Activities/Heal.cs index 199f89d041..ea000e910e 100755 --- a/OpenRA.Mods.RA/Activities/Heal.cs +++ b/OpenRA.Mods.RA/Activities/Heal.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Activities protected override Activity InnerTick(Actor self, AttackBase attack) { - if (Target.IsActor && Target.Actor.GetDamageState() == DamageState.Undamaged) + if (Target.Type == TargetType.Actor && Target.Actor.GetDamageState() == DamageState.Undamaged) return NextActivity; return base.InnerTick(self, attack); diff --git a/OpenRA.Mods.RA/Activities/Infiltrate.cs b/OpenRA.Mods.RA/Activities/Infiltrate.cs index 60f1338442..9baad5547f 100644 --- a/OpenRA.Mods.RA/Activities/Infiltrate.cs +++ b/OpenRA.Mods.RA/Activities/Infiltrate.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid || target.Actor.Owner == self.Owner) + if (IsCanceled || target.Type != TargetType.Actor || target.Actor.Owner == self.Owner) return NextActivity; foreach (var t in target.Actor.TraitsImplementing()) diff --git a/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs b/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs index 83d8dbad8d..a2ba106cca 100644 --- a/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs +++ b/OpenRA.Mods.RA/Activities/LegacyCaptureActor.cs @@ -9,9 +9,9 @@ #endregion using System.Linq; -using OpenRA.Traits; -using OpenRA.Mods.RA.Move; using OpenRA.Mods.RA.Buildings; +using OpenRA.Mods.RA.Move; +using OpenRA.Traits; namespace OpenRA.Mods.RA.Activities { @@ -23,31 +23,30 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled) - return NextActivity; - if (!target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; - var b = target.Actor.TraitOrDefault(); + var actor = target.Actor; + var b = actor.TraitOrDefault(); if (b != null && b.Locked) return NextActivity; var capturesInfo = self.Info.Traits.Get(); - var capturableInfo = target.Actor.Info.Traits.Get(); + var capturableInfo = actor.Info.Traits.Get(); - var health = target.Actor.Trait(); + var health = actor.Trait(); self.World.AddFrameEndTask(w => { var lowEnoughHealth = health.HP <= capturableInfo.CaptureThreshold * health.MaxHP; - if (!capturesInfo.Sabotage || lowEnoughHealth || target.Actor.Owner.NonCombatant) + if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant) { - var oldOwner = target.Actor.Owner; + var oldOwner = actor.Owner; - target.Actor.ChangeOwner(self.Owner); + actor.ChangeOwner(self.Owner); - foreach (var t in target.Actor.TraitsImplementing()) - t.OnCapture(target.Actor, self, oldOwner, self.Owner); + foreach (var t in actor.TraitsImplementing()) + t.OnCapture(actor, self, oldOwner, self.Owner); if (b != null && b.Locked) b.Unlock(); @@ -55,7 +54,7 @@ namespace OpenRA.Mods.RA.Activities else { var damage = (int)(health.MaxHP * capturesInfo.SabotageHPRemoval); - target.Actor.InflictDamage(self, damage, null); + actor.InflictDamage(self, damage, null); } self.Destroy(); diff --git a/OpenRA.Mods.RA/Activities/RepairBridge.cs b/OpenRA.Mods.RA/Activities/RepairBridge.cs index 90377a47e1..178eea6ce3 100644 --- a/OpenRA.Mods.RA/Activities/RepairBridge.cs +++ b/OpenRA.Mods.RA/Activities/RepairBridge.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var hut = target.Actor.Trait(); diff --git a/OpenRA.Mods.RA/Activities/RepairBuilding.cs b/OpenRA.Mods.RA/Activities/RepairBuilding.cs index 8e5e4f4bc6..c5c13fafbe 100644 --- a/OpenRA.Mods.RA/Activities/RepairBuilding.cs +++ b/OpenRA.Mods.RA/Activities/RepairBuilding.cs @@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities public override Activity Tick(Actor self) { - if (IsCanceled || !target.IsValid) + if (IsCanceled || target.Type != TargetType.Actor) return NextActivity; var health = target.Actor.Trait(); diff --git a/OpenRA.Mods.RA/ActorExts.cs b/OpenRA.Mods.RA/ActorExts.cs index 9dedf3412b..9cca5a9595 100644 --- a/OpenRA.Mods.RA/ActorExts.cs +++ b/OpenRA.Mods.RA/ActorExts.cs @@ -9,6 +9,7 @@ #endregion using System; +using System.Drawing; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -44,6 +45,44 @@ namespace OpenRA.Mods.RA return stance == Stance.Enemy; } + + public static Target ResolveFrozenActorOrder(this Actor self, Order order, Color targetLine) + { + // Not targeting a frozen actor + if (order.ExtraData == 0) + return Target.FromOrder(order); + + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return Target.Invalid; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return Target.Invalid; + + // Flashes the frozen proxy + self.SetTargetLine(frozen, targetLine, true); + + // Target is still alive - resolve the real order + if (frozen.Actor != null && frozen.Actor.IsInWorld) + return Target.FromActor(frozen.Actor); + + if (!order.Queued) + self.CancelActivity(); + + var move = self.TraitOrDefault(); + if (move != null) + { + // Move within sight range of the frozen actor + var sight = self.TraitOrDefault(); + var range = sight != null ? sight.Range : 2; + + self.QueueActivity(move.MoveWithinRange(Target.FromPos(frozen.CenterPosition), WRange.FromCells(range))); + } + + return Target.Invalid; + } } } diff --git a/OpenRA.Mods.RA/Air/Aircraft.cs b/OpenRA.Mods.RA/Air/Aircraft.cs index b9b407b1c4..90e4285b56 100755 --- a/OpenRA.Mods.RA/Air/Aircraft.cs +++ b/OpenRA.Mods.RA/Air/Aircraft.cs @@ -166,7 +166,7 @@ namespace OpenRA.Mods.RA.Air { get { - yield return new EnterOrderTargeter("Enter", 5, false, true, + yield return new EnterAlliedActorTargeter("Enter", 5, target => AircraftCanEnter(target), target => !Reservable.IsReserved(target)); yield return new AircraftMoveOrderTargeter(); @@ -217,15 +217,13 @@ namespace OpenRA.Mods.RA.Air public string OrderID { get { return "Move"; } } public int OrderPriority { get { return 4; } } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - cursor = self.World.Map.IsInMap(location) ? "move" : "move-blocked"; + cursor = self.World.Map.IsInMap(target.CenterPosition.ToCPos()) ? "move" : "move-blocked"; return true; } diff --git a/OpenRA.Mods.RA/Attack/AttackBase.cs b/OpenRA.Mods.RA/Attack/AttackBase.cs index 9321698c67..d9ad4fbcc1 100644 --- a/OpenRA.Mods.RA/Attack/AttackBase.cs +++ b/OpenRA.Mods.RA/Attack/AttackBase.cs @@ -54,8 +54,8 @@ namespace OpenRA.Mods.RA if (self.IsDisabled()) return false; - if (target.IsActor && target.Actor.HasTrait() && - !target.Actor.Trait().TargetableBy(target.Actor,self)) + if (target.Type == TargetType.Actor && target.Actor.HasTrait() && + !target.Actor.Trait().TargetableBy(target.Actor, self)) return false; return true; @@ -76,6 +76,7 @@ namespace OpenRA.Mods.RA x.Second(); delayedActions[i] = x; } + delayedActions.RemoveAll(a => a.First <= 0); } @@ -104,8 +105,7 @@ namespace OpenRA.Mods.RA if (Armaments.Count() == 0) yield break; - var negativeDamage = (Armaments.First().Weapon.Warheads[0].Damage < 0); - + var negativeDamage = Armaments.First().Weapon.Warheads[0].Damage < 0; yield return new AttackOrderTargeter("Attack", 6, negativeDamage); } } @@ -114,33 +114,42 @@ namespace OpenRA.Mods.RA { if (order is AttackOrderTargeter) { - if (target.IsActor) + switch (target.Type) + { + case TargetType.Actor: return new Order("Attack", self, queued) { TargetActor = target.Actor }; - else + case TargetType.FrozenActor: + return new Order("Attack", self, queued) { ExtraData = target.FrozenActor.ID }; + case TargetType.Terrain: return new Order("Attack", self, queued) { TargetLocation = target.CenterPosition.ToCPos() }; + } } + return null; } public virtual void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "Attack" || order.OrderString == "AttackHold") + if (order.OrderString == "Attack") { - var target = Target.FromOrder(order); + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (!target.IsValid) + return; + self.SetTargetLine(target, Color.Red); - AttackTarget(target, order.Queued, order.OrderString == "Attack"); + AttackTarget(target, order.Queued, true); } } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "Attack" || order.OrderString == "AttackHold") ? "Attack" : null; + return order.OrderString == "Attack" ? "Attack" : null; } public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove); public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World)); } - public WRange GetMaximumRange() { return new WRange((int)(1024*Armaments.Max(a => a.Weapon.Range))); } + public WRange GetMaximumRange() { return new WRange((int)(1024 * Armaments.Max(a => a.Weapon.Range))); } public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World)); } @@ -169,16 +178,16 @@ namespace OpenRA.Mods.RA public string OrderID { get; private set; } public int OrderPriority { get; private set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + bool CanTargetActor(Actor self, Target target, TargetModifiers modifiers, ref string cursor) { IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); cursor = self.Info.Traits.Get().Cursor; - if (self == target) + if (target.Type == TargetType.Actor && target.Actor == self) return false; - if (!self.Trait().HasAnyValidWeapons(Target.FromActor(target))) + if (!self.Trait().HasAnyValidWeapons(target)) return false; if (modifiers.HasModifier(TargetModifiers.ForceAttack)) @@ -189,10 +198,11 @@ namespace OpenRA.Mods.RA var targetableRelationship = negativeDamage ? Stance.Ally : Stance.Enemy; - return self.Owner.Stances[target.Owner] == targetableRelationship; + var owner = target.Type == TargetType.FrozenActor ? target.FrozenActor.Owner : target.Actor.Owner; + return self.Owner.Stances[owner] == targetableRelationship; } - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) + bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) { if (!self.World.Map.IsInMap(location)) return false; @@ -214,6 +224,20 @@ namespace OpenRA.Mods.RA return false; } + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) + { + switch (target.Type) + { + case TargetType.Actor: + case TargetType.FrozenActor: + return CanTargetActor(self, target, modifiers, ref cursor); + case TargetType.Terrain: + return CanTargetLocation(self, target.CenterPosition.ToCPos(), othersAtTarget, modifiers, ref cursor); + default: + return false; + } + } + public bool IsQueued { get; protected set; } } } diff --git a/OpenRA.Mods.RA/Attack/AttackLeap.cs b/OpenRA.Mods.RA/Attack/AttackLeap.cs index 75683b1673..9bfbed10cf 100644 --- a/OpenRA.Mods.RA/Attack/AttackLeap.cs +++ b/OpenRA.Mods.RA/Attack/AttackLeap.cs @@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA public override void DoAttack(Actor self, Target target) { - if (!CanAttack(self, target) || !target.IsActor) + if (target.Type != TargetType.Actor || !CanAttack(self, target)) return; var a = ChooseArmamentForTarget(target); diff --git a/OpenRA.Mods.RA/Attack/AttackTurreted.cs b/OpenRA.Mods.RA/Attack/AttackTurreted.cs index 06c944ce44..4708273e88 100644 --- a/OpenRA.Mods.RA/Attack/AttackTurreted.cs +++ b/OpenRA.Mods.RA/Attack/AttackTurreted.cs @@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA base.ResolveOrder(self, order); if (order.OrderString == "Stop") - target = Target.None; + target = Target.Invalid; } public virtual void BuildingComplete(Actor self) { buildComplete = true; } diff --git a/OpenRA.Mods.RA/AutoTarget.cs b/OpenRA.Mods.RA/AutoTarget.cs index 19f06ada6a..096565c330 100644 --- a/OpenRA.Mods.RA/AutoTarget.cs +++ b/OpenRA.Mods.RA/AutoTarget.cs @@ -43,7 +43,8 @@ namespace OpenRA.Mods.RA public UnitStance stance; [Sync] public int stanceNumber { get { return (int)stance; } } public UnitStance predictedStance; /* NOT SYNCED: do not refer to this anywhere other than UI code */ - [Sync] public int AggressorID; + [Sync] public Actor Aggressor; + [Sync] public Actor TargetedActor; public AutoTarget(Actor self, AutoTargetInfo info) { @@ -75,7 +76,7 @@ namespace OpenRA.Mods.RA if (e.Damage < 0) return; // don't retaliate against healers - AggressorID = (int)e.Attacker.ActorID; + Aggressor = e.Attacker; attack.AttackTarget(Target.FromActor(e.Attacker), false, Info.AllowMovement && stance != UnitStance.Defend); } @@ -87,6 +88,8 @@ namespace OpenRA.Mods.RA var target = ScanForTarget(self, null); if (target != null) { + TargetedActor = target; + var t = Target.FromActor(target); self.SetTargetLine(t, Color.Red, false); attack.AttackTarget(t, false, Info.AllowMovement && stance != UnitStance.Defend); @@ -113,7 +116,10 @@ namespace OpenRA.Mods.RA { var targetActor = ScanForTarget(self, null); if (targetActor != null) + { + TargetedActor = targetActor; attack.AttackTarget(Target.FromActor(targetActor), false, Info.AllowMovement && stance != UnitStance.Defend); + } } Actor ChooseTarget(Actor self, WRange range) diff --git a/OpenRA.Mods.RA/C4Demolition.cs b/OpenRA.Mods.RA/C4Demolition.cs index f200ef8bad..59d876674f 100644 --- a/OpenRA.Mods.RA/C4Demolition.cs +++ b/OpenRA.Mods.RA/C4Demolition.cs @@ -26,11 +26,11 @@ namespace OpenRA.Mods.RA class C4Demolition : IIssueOrder, IResolveOrder, IOrderVoice { - readonly C4DemolitionInfo Info; + readonly C4DemolitionInfo info; public C4Demolition(C4DemolitionInfo info) { - Info = info; + this.info = info; } public IEnumerable Orders @@ -38,28 +38,36 @@ namespace OpenRA.Mods.RA get { yield return new TargetTypeOrderTargeter("C4", "C4", 6, "c4", true, false); } } - public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) + public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "C4") - return new Order("C4", self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "C4") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "C4") - { - self.SetTargetLine(Target.FromOrder(order), Color.Red); + if (order.OrderString != "C4") + return; + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new Demolish(order.TargetActor, Info.C4Delay))); - } + + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new Enter(target.Actor, new Demolish(target.Actor, info.C4Delay))); } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "C4") ? "Attack" : null; + return order.OrderString == "C4" ? "Attack" : null; } } } diff --git a/OpenRA.Mods.RA/Capturable.cs b/OpenRA.Mods.RA/Capturable.cs index ffe33564e2..0c3d2c5745 100644 --- a/OpenRA.Mods.RA/Capturable.cs +++ b/OpenRA.Mods.RA/Capturable.cs @@ -28,6 +28,28 @@ namespace OpenRA.Mods.RA [Desc("Seconds it takes to change the owner.", "You might want to add a CapturableBar: trait, too.")] public readonly int CaptureCompleteTime = 15; + public bool CanBeTargetedBy(Actor captor, Player owner) + { + var c = captor.TraitOrDefault(); + if (c == null) + return false; + + var playerRelationship = owner.Stances[captor.Owner]; + if (playerRelationship == Stance.Ally && !AllowAllies) + return false; + + if (playerRelationship == Stance.Enemy && !AllowEnemies) + return false; + + if (playerRelationship == Stance.Neutral && !AllowNeutral) + return false; + + if (!c.Info.CaptureTypes.Contains(Type)) + return false; + + return true; + } + public object Create(ActorInitializer init) { return new Capturable(init.self, this); } } @@ -45,31 +67,6 @@ namespace OpenRA.Mods.RA Info = info; } - public bool CanBeTargetedBy(Actor captor) - { - var c = captor.TraitOrDefault(); - if (c == null) - return false; - - var playerRelationship = self.Owner.Stances[captor.Owner]; - if (playerRelationship == Stance.Ally && !Info.AllowAllies) - return false; - - if (playerRelationship == Stance.Enemy && !Info.AllowEnemies) - return false; - - if (playerRelationship == Stance.Neutral && !Info.AllowNeutral) - return false; - - if (!c.Info.CaptureTypes.Contains(Info.Type)) - return false; - - if (CaptureInProgress) - return false; - - return true; - } - public void BeginCapture(Actor captor) { var building = self.TraitOrDefault(); diff --git a/OpenRA.Mods.RA/Captures.cs b/OpenRA.Mods.RA/Captures.cs index 093bfec04a..72c6cdc8c2 100644 --- a/OpenRA.Mods.RA/Captures.cs +++ b/OpenRA.Mods.RA/Captures.cs @@ -12,11 +12,11 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.FileFormats; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Orders; using OpenRA.Traits; -using OpenRA.FileFormats; namespace OpenRA.Mods.RA { @@ -34,11 +34,9 @@ namespace OpenRA.Mods.RA class Captures : IIssueOrder, IResolveOrder, IOrderVoice { public readonly CapturesInfo Info; - readonly Actor self; public Captures(Actor self, CapturesInfo info) { - this.self = self; Info = info; } @@ -46,69 +44,89 @@ namespace OpenRA.Mods.RA { get { - yield return new CaptureOrderTargeter(CanCapture); + yield return new CaptureOrderTargeter(); } } public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "CaptureActor") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "CaptureActor") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + } + + bool IsValidOrder(Actor self, Order order) + { + // Not targeting an actor + if (order.ExtraData == 0 && order.TargetActor == null) + return false; + + if (order.ExtraData != 0) + { + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return false; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return false; + + var ci = frozen.Info.Traits.GetOrDefault(); + return ci != null && ci.CanBeTargetedBy(self, frozen.Owner); + } + + var c = order.TargetActor.TraitOrDefault(); + return c != null && !c.CaptureInProgress && c.Info.CanBeTargetedBy(self, order.TargetActor.Owner); } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "CaptureActor" && CanCapture(order.TargetActor)) ? "Attack" : null; + return order.OrderString == "CaptureActor" && IsValidOrder(self, order) + ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "CaptureActor") - { - if (!CanCapture(order.TargetActor)) - return; + if (order.OrderString != "CaptureActor" || !IsValidOrder(self, order)) + return; - self.SetTargetLine(Target.FromOrder(order), Color.Red); + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new CaptureActor(Target.FromOrder(order))); - } - } - bool CanCapture(Actor target) - { - var c = target.TraitOrDefault(); - return c != null && c.CanBeTargetedBy(self); + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new CaptureActor(target)); } } class CaptureOrderTargeter : UnitOrderTargeter { - readonly Func useCaptureCursor; - - public CaptureOrderTargeter(Func useCaptureCursor) - : base("CaptureActor", 6, "enter", true, true) - { - this.useCaptureCursor = useCaptureCursor; - } + public CaptureOrderTargeter() : base("CaptureActor", 6, "enter", true, true) { } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; + var c = target.TraitOrDefault(); - var canTargetActor = useCaptureCursor(target); + var canTargetActor = c != null && !c.CaptureInProgress && c.Info.CanBeTargetedBy(self, target.Owner); cursor = canTargetActor ? "ability" : "move-blocked"; + return canTargetActor; + } - if (canTargetActor) - { - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return true; - } + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + var c = target.Info.Traits.GetOrDefault(); - return false; + var canTargetActor = c != null && c.CanBeTargetedBy(self, target.Owner); + cursor = canTargetActor ? "ability" : "move-blocked"; + return canTargetActor; } } } diff --git a/OpenRA.Mods.RA/Combat.cs b/OpenRA.Mods.RA/Combat.cs index 37b751f09f..c50613a2a0 100755 --- a/OpenRA.Mods.RA/Combat.cs +++ b/OpenRA.Mods.RA/Combat.cs @@ -118,7 +118,7 @@ namespace OpenRA.Mods.RA foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindActorsInBox(t, t)) unit.InflictDamage(firedBy, - (int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead); + (int)(warhead.Damage * warhead.EffectivenessAgainst(unit.Info)), warhead); } break; } } @@ -173,7 +173,7 @@ namespace OpenRA.Mods.RA var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - health.Radius); var falloff = (float)GetDamageFalloff(distance / warhead.Spread); var rawDamage = (float)(warhead.Damage * modifier * falloff); - var multiplier = (float)warhead.EffectivenessAgainst(target); + var multiplier = (float)warhead.EffectivenessAgainst(target.Info); return (float)(rawDamage * multiplier); } diff --git a/OpenRA.Mods.RA/DemoTruck.cs b/OpenRA.Mods.RA/DemoTruck.cs index 179fabab55..3ae88887dd 100644 --- a/OpenRA.Mods.RA/DemoTruck.cs +++ b/OpenRA.Mods.RA/DemoTruck.cs @@ -17,8 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - // Exception when overriding Chronoshift event; removed for now, will look into it. - class DemoTruckInfo : TraitInfo, Requires {} + class DemoTruckInfo : TraitInfo, Requires { } class DemoTruck : IIssueOrder, IResolveOrder, IOrderVoice { @@ -41,10 +40,13 @@ namespace OpenRA.Mods.RA public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "DemoAttack" || order.OrderID == "DemoDeploy") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "DemoAttack" && order.OrderID != "DemoDeploy") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public string VoicePhraseForOrder(Actor self, Order order) @@ -56,13 +58,18 @@ namespace OpenRA.Mods.RA { if (order.OrderString == "DemoAttack") { - self.SetTargetLine(Target.FromOrder(order), Color.Red); - self.World.AddFrameEndTask(w => - { - self.QueueActivity(new MoveAdjacentTo(Target.FromOrder(order))); - self.QueueActivity(new CallFunc(() => Explode(self))); - }); + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) + self.CancelActivity(); + + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new MoveAdjacentTo(target)); + self.QueueActivity(new CallFunc(() => Explode(self))); } + if (order.OrderString == "DemoDeploy") Explode(self); } diff --git a/OpenRA.Mods.RA/Effects/FrozenActorProxy.cs b/OpenRA.Mods.RA/Effects/FrozenActorProxy.cs deleted file mode 100644 index 96fe2d82e0..0000000000 --- a/OpenRA.Mods.RA/Effects/FrozenActorProxy.cs +++ /dev/null @@ -1,53 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation. For more information, - * see COPYING. - */ -#endregion - -using System.Collections.Generic; -using System.Linq; -using OpenRA.Effects; -using OpenRA.Graphics; -using OpenRA.Traits; - -namespace OpenRA.Mods.RA.Effects -{ - public class FrozenActorProxy : IEffect - { - readonly Actor self; - readonly IEnumerable footprint; - IRenderable[] renderables; - - public FrozenActorProxy(Actor self, IEnumerable footprint) - { - this.self = self; - this.footprint = footprint; - } - - public void Tick(World world) { } - public void SetRenderables(IEnumerable r) - { - renderables = r.Select(rr => rr).ToArray(); - } - - public IEnumerable Render(WorldRenderer wr) - { - if (renderables == null) - return SpriteRenderable.None; - - if (footprint.Any(c => !wr.world.FogObscures(c))) - { - if (self.Destroyed) - self.World.AddFrameEndTask(w => w.Remove(this)); - - return SpriteRenderable.None; - } - - return renderables; - } - } -} diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index 785c5029a6..86b7bc2154 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -28,28 +28,58 @@ namespace OpenRA.Mods.RA public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "EngineerRepair") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "EngineerRepair") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + } + + bool IsValidOrder(Actor self, Order order) + { + // Not targeting a frozen actor + if (order.ExtraData == 0 && order.TargetActor == null) + return false; + + if (order.ExtraData != 0) + { + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return false; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return false; + + return frozen.DamageState > DamageState.Undamaged; + } + + return order.TargetActor.GetDamageState() > DamageState.Undamaged; } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "EngineerRepair" && - order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null; + return order.OrderString == "EngineerRepair" && IsValidOrder(self, order) + ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "EngineerRepair" && - order.TargetActor.GetDamageState() > DamageState.Undamaged) - { - self.SetTargetLine(Target.FromOrder(order), Color.Yellow); + if (order.OrderString != "EngineerRepair" || !IsValidOrder(self, order)) + return; + var target = self.ResolveFrozenActorOrder(order, Color.Yellow); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new RepairBuilding(order.TargetActor))); - } + + self.SetTargetLine(target, Color.Yellow); + self.QueueActivity(new Enter(target.Actor, new RepairBuilding(target.Actor))); } class EngineerRepairOrderTargeter : UnitOrderTargeter @@ -59,22 +89,31 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - if (!target.HasTrait()) return false; if (self.Owner.Stances[target.Owner] != Stance.Ally) return false; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - if (target.GetDamageState() == DamageState.Undamaged) cursor = "goldwrench-blocked"; return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + if (!target.Info.Traits.Contains()) + return false; + + if (self.Owner.Stances[target.Owner] != Stance.Ally) + return false; + + if (target.DamageState == DamageState.Undamaged) + cursor = "goldwrench-blocked"; + + return true; + } } } diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index ec1860261d..692d3bfe81 100644 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -239,7 +239,8 @@ namespace OpenRA.Mods.RA { get { - yield return new EnterOrderTargeter("Deliver", 5, false, true, _ => true, proc => !IsEmpty && proc.Trait().AllowDocking); + yield return new EnterAlliedActorTargeter("Deliver", 5, _ => true, + proc => !IsEmpty && proc.Trait().AllowDocking); yield return new HarvestOrderTargeter(); } } @@ -419,16 +420,15 @@ namespace OpenRA.Mods.RA public int OrderPriority { get { return 10; } } public bool IsQueued { get; protected set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { if (modifiers.HasModifier(TargetModifiers.ForceMove)) return false; + var location = target.CenterPosition.ToCPos(); // Don't leak info about resources under the shroud if (!self.Owner.Shroud.IsExplored(location)) return false; diff --git a/OpenRA.Mods.RA/Infiltrates.cs b/OpenRA.Mods.RA/Infiltrates.cs index 195b355fde..00d6a457ab 100644 --- a/OpenRA.Mods.RA/Infiltrates.cs +++ b/OpenRA.Mods.RA/Infiltrates.cs @@ -41,67 +41,96 @@ namespace OpenRA.Mods.RA Info = info; } - public IEnumerable Orders - { - get - { - yield return new InfiltratorOrderTargeter(CanInfiltrate); - } - } + public IEnumerable Orders { get { yield return new InfiltratorOrderTargeter(Info.Types); } } public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "Infiltrate") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "Infiltrate") + return null; + + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; - return null; + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } + bool IsValidOrder(Actor self, Order order) + { + // Not targeting an actor + if (order.ExtraData == 0 && order.TargetActor == null) + return false; + + if (order.ExtraData != 0) + { + // Targeted an actor under the fog + var frozenLayer = self.Owner.PlayerActor.TraitOrDefault(); + if (frozenLayer == null) + return false; + + var frozen = frozenLayer.FromID(order.ExtraData); + if (frozen == null) + return false; + + var ii = frozen.Info.Traits.GetOrDefault(); + return ii != null && Info.Types.Contains(ii.Type); + } + + var i = order.TargetActor.Info.Traits.GetOrDefault(); + return i != null && Info.Types.Contains(i.Type); + } + public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "Infiltrate" && CanInfiltrate(order.TargetActor)) ? "Attack" : null; + return order.OrderString == "Infiltrate" && IsValidOrder(self, order) + ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "Infiltrate") - { - if (!CanInfiltrate(order.TargetActor)) - return; + if (order.OrderString != "Infiltrate" || !IsValidOrder(self, order)) + return; - self.SetTargetLine(Target.FromOrder(order), Color.Red); - + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new Infiltrate(order.TargetActor))); - } - } - - bool CanInfiltrate(Actor target) - { - var infiltratable = target.Info.Traits.GetOrDefault(); - return infiltratable != null && Info.Types.Contains(infiltratable.Type); + + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new Enter(target.Actor, new Infiltrate(target.Actor))); } class InfiltratorOrderTargeter : UnitOrderTargeter { - readonly Func useEnterCursor; - - public InfiltratorOrderTargeter(Func useEnterCursor) + string[] infiltrationTypes; + + public InfiltratorOrderTargeter(string[] infiltrationTypes) : base("Infiltrate", 7, "enter", true, false) { ForceAttack = false; - this.useEnterCursor = useEnterCursor; + this.infiltrationTypes = infiltrationTypes; } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) + var info = target.Info.Traits.GetOrDefault(); + if (info == null) return false; - if (!target.HasTrait()) + if (!infiltrationTypes.Contains(info.Type)) + cursor = "enter-blocked"; + + return true; + } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + var info = target.Info.Traits.GetOrDefault(); + if (info == null) return false; - if (!useEnterCursor(target)) + if (!infiltrationTypes.Contains(info.Type)) cursor = "enter-blocked"; return true; diff --git a/OpenRA.Mods.RA/LegacyCapturable.cs b/OpenRA.Mods.RA/LegacyCapturable.cs index 639dbddbb4..3375d1d024 100644 --- a/OpenRA.Mods.RA/LegacyCapturable.cs +++ b/OpenRA.Mods.RA/LegacyCapturable.cs @@ -15,7 +15,7 @@ using OpenRA.FileFormats; namespace OpenRA.Mods.RA { [Desc("This actor can be captured by a unit with LegacyCaptures: trait.")] - class LegacyCapturableInfo : ITraitInfo + class LegacyCapturableInfo : TraitInfo { [Desc("Type of actor (the LegacyCaptures: trait defines what Types it can capture).")] public readonly string Type = "building"; @@ -25,40 +25,28 @@ namespace OpenRA.Mods.RA [Desc("Health percentage the target must be at (or below) before it can be captured.")] public readonly float CaptureThreshold = 0.5f; - public object Create(ActorInitializer init) { return new LegacyCapturable(init.self, this); } - } - - class LegacyCapturable - { - [Sync] Actor self; - public LegacyCapturableInfo Info; - - public LegacyCapturable(Actor self, LegacyCapturableInfo info) - { - this.self = self; - Info = info; - } - - public bool CanBeTargetedBy(Actor captor) + public bool CanBeTargetedBy(Actor captor, Player owner) { var c = captor.TraitOrDefault(); if (c == null) return false; - var playerRelationship = self.Owner.Stances[captor.Owner]; - if (playerRelationship == Stance.Ally && !Info.AllowAllies) + var playerRelationship = owner.Stances[captor.Owner]; + if (playerRelationship == Stance.Ally && !AllowAllies) return false; - if (playerRelationship == Stance.Enemy && !Info.AllowEnemies) + if (playerRelationship == Stance.Enemy && !AllowEnemies) return false; - if (playerRelationship == Stance.Neutral && !Info.AllowNeutral) + if (playerRelationship == Stance.Neutral && !AllowNeutral) return false; - if (!c.Info.CaptureTypes.Contains(Info.Type)) + if (!c.Info.CaptureTypes.Contains(Type)) return false; return true; } } + + class LegacyCapturable { } } diff --git a/OpenRA.Mods.RA/LegacyCaptures.cs b/OpenRA.Mods.RA/LegacyCaptures.cs index 32a0602bc4..8193145f8a 100644 --- a/OpenRA.Mods.RA/LegacyCaptures.cs +++ b/OpenRA.Mods.RA/LegacyCaptures.cs @@ -12,11 +12,11 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using OpenRA.FileFormats; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Orders; using OpenRA.Traits; -using OpenRA.FileFormats; namespace OpenRA.Mods.RA { @@ -36,11 +36,9 @@ namespace OpenRA.Mods.RA class LegacyCaptures : IIssueOrder, IResolveOrder, IOrderVoice { public readonly LegacyCapturesInfo Info; - readonly Actor self; public LegacyCaptures(Actor self, LegacyCapturesInfo info) { - this.self = self; Info = info; } @@ -48,70 +46,85 @@ namespace OpenRA.Mods.RA { get { - yield return new LegacyCaptureOrderTargeter(CanCapture); + yield return new LegacyCaptureOrderTargeter(Info.Sabotage); } } public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "LegacyCaptureActor") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "LegacyCaptureActor") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "LegacyCaptureActor") ? "Attack" : null; + return order.OrderString == "LegacyCaptureActor" ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "LegacyCaptureActor") - { - self.SetTargetLine(Target.FromOrder(order), Color.Red); + if (order.OrderString != "LegacyCaptureActor") + return; + var target = self.ResolveFrozenActorOrder(order, Color.Red); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new LegacyCaptureActor(Target.FromOrder(order)))); - } - } - bool CanCapture(Actor target) - { - var c = target.TraitOrDefault(); - return c != null && c.CanBeTargetedBy(self); + self.SetTargetLine(target, Color.Red); + self.QueueActivity(new Enter(target.Actor, new LegacyCaptureActor(target))); } class LegacyCaptureOrderTargeter : UnitOrderTargeter { - readonly Func useCaptureCursor; + readonly bool sabotage; - public LegacyCaptureOrderTargeter(Func useCaptureCursor) + public LegacyCaptureOrderTargeter(bool sabotage) : base("LegacyCaptureActor", 6, "enter", true, true) { - this.useCaptureCursor = useCaptureCursor; + this.sabotage = sabotage; } public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) return false; - - var canTargetActor = useCaptureCursor(target); - - if (canTargetActor) + var c = target.Info.Traits.GetOrDefault(); + if (c == null || !c.CanBeTargetedBy(self, target.Owner)) { - var c = target.Trait(); - var health = target.Trait(); - var lowEnoughHealth = health.HP <= c.Info.CaptureThreshold * health.MaxHP; - - cursor = lowEnoughHealth ? "enter" : "capture"; - - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return true; + cursor = "enter-blocked"; + return false; } - cursor = "enter-blocked"; - return false; + var health = target.Trait(); + var lowEnoughHealth = health.HP <= c.CaptureThreshold * health.MaxHP; + + cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant + ? "capture" : "enter"; + return true; + } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + var c = target.Info.Traits.GetOrDefault(); + if (c == null || !c.CanBeTargetedBy(self, target.Owner)) + { + cursor = "enter-blocked"; + return false; + } + + var health = target.Info.Traits.GetOrDefault(); + var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP; + + cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant + ? "capture" : "enter"; + + return true; } } } diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index 2030368a42..a8f56b9913 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -156,21 +156,21 @@ namespace OpenRA.Mods.RA public string OrderID { get { return "BeginMinefield"; } } public int OrderPriority { get { return 5; } } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { + var location = target.CenterPosition.ToCPos(); if (!self.World.Map.IsInMap(location)) return false; cursor = "ability"; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return (actorsAtLocation.Count == 0 && modifiers.HasModifier(TargetModifiers.ForceAttack)); + return !othersAtTarget.Any() && modifiers.HasModifier(TargetModifiers.ForceAttack); } + public bool IsQueued { get; protected set; } } } diff --git a/OpenRA.Mods.RA/Missions/Allies04Script.cs b/OpenRA.Mods.RA/Missions/Allies04Script.cs index e66a5335fd..173d461f2a 100644 --- a/OpenRA.Mods.RA/Missions/Allies04Script.cs +++ b/OpenRA.Mods.RA/Missions/Allies04Script.cs @@ -457,7 +457,7 @@ namespace OpenRA.Mods.RA.Missions } } - class Allies04HijackableInfo : ITraitInfo + class Allies04HijackableInfo : ITraitInfo, Requires { public object Create(ActorInitializer init) { return new Allies04Hijackable(init.self); } } diff --git a/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs b/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs index 8544ae2ffa..78a7ffa5f9 100644 --- a/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs +++ b/OpenRA.Mods.RA/Modifiers/FrozenUnderFog.cs @@ -9,7 +9,9 @@ #endregion using System.Collections.Generic; +using System.Drawing; using System.Linq; +using OpenRA.FileFormats; using OpenRA.Graphics; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Effects; @@ -24,50 +26,97 @@ namespace OpenRA.Mods.RA public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); } } - public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITickRender + public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITick, ITickRender, ISync { - FrozenActorProxy proxy; + [Sync] public int VisibilityHash; + + bool initialized, startsRevealed; IEnumerable footprint; - bool visible, cacheFirstFrame; + Lazy tooltip; + Lazy health; + + Dictionary visible; + Dictionary frozen; public FrozenUnderFog(ActorInitializer init, FrozenUnderFogInfo info) { - footprint = FootprintUtils.Tiles(init.self); - proxy = new FrozenActorProxy(init.self, footprint); - init.world.AddFrameEndTask(w => w.Add(proxy)); - // Spawned actors (e.g. building husks) shouldn't be revealed - cacheFirstFrame = info.StartsRevealed && !init.Contains(); + startsRevealed = info.StartsRevealed && !init.Contains(); + footprint = FootprintUtils.Tiles(init.self); + tooltip = Lazy.New(() => init.self.TraitsImplementing().FirstOrDefault()); + tooltip = Lazy.New(() => init.self.TraitsImplementing().FirstOrDefault()); + health = Lazy.New(() => init.self.TraitOrDefault()); + + frozen = new Dictionary(); + visible = init.world.Players.ToDictionary(p => p, p => false); } public bool IsVisible(Actor self, Player byPlayer) { - return byPlayer == null || footprint.Any(c => byPlayer.Shroud.IsVisible(c)); + return byPlayer == null || visible[byPlayer]; } - public void TickRender(WorldRenderer wr, Actor self) + public void Tick(Actor self) { if (self.Destroyed) return; - visible = IsVisible(self, self.World.RenderPlayer); - - if (cacheFirstFrame) + VisibilityHash = 0; + foreach (var p in self.World.Players) { - visible = true; - cacheFirstFrame = false; + visible[p] = footprint.Any(c => p.Shroud.IsVisible(c)); + if (visible[p]) + VisibilityHash += p.ClientIndex; } - if (visible) + if (!initialized) { - var comparer = new RenderableComparer(wr); - proxy.SetRenderables(self.Render(wr).OrderBy(r => r, comparer)); + foreach (var p in self.World.Players) + { + visible[p] |= startsRevealed; + frozen[p] = new FrozenActor(self, footprint); + p.PlayerActor.Trait().Add(frozen[p]); + } + + initialized = true; } + + foreach (var player in self.World.Players) + { + if (!visible[player]) + continue; + + frozen[player].Owner = self.Owner; + + if (health.Value != null) + { + frozen[player].HP = health.Value.HP; + frozen[player].DamageState = health.Value.DamageState; + } + + if (tooltip.Value != null) + { + frozen[player].TooltipName = tooltip.Value.Name(); + frozen[player].TooltipOwner = tooltip.Value.Owner(); + } + } + } + + public void TickRender(WorldRenderer wr, Actor self) + { + if (self.Destroyed || !initialized || !visible.Any(v => v.Value)) + return; + + // Force a copy of the underlying data + var renderables = self.Render(wr).Select(rr => rr).ToArray(); + foreach (var player in self.World.Players) + if (visible[player]) + frozen[player].Renderables = renderables; } public IEnumerable ModifyRender(Actor self, WorldRenderer wr, IEnumerable r) { - return visible ? r : SpriteRenderable.None; + return IsVisible(self, self.World.RenderPlayer) ? r : SpriteRenderable.None; } } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/Move/Mobile.cs b/OpenRA.Mods.RA/Move/Mobile.cs index 0da1ec50b1..1b9217f3d0 100755 --- a/OpenRA.Mods.RA/Move/Mobile.cs +++ b/OpenRA.Mods.RA/Move/Mobile.cs @@ -494,13 +494,12 @@ namespace OpenRA.Mods.RA.Move public int OrderPriority { get { return 4; } } public bool IsQueued { get; protected set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (!target.IsValid) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { + var location = target.CenterPosition.ToCPos(); IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); cursor = "move"; diff --git a/OpenRA.Mods.RA/Move/Move.cs b/OpenRA.Mods.RA/Move/Move.cs index e3d8e79e0e..503b0bd2c8 100755 --- a/OpenRA.Mods.RA/Move/Move.cs +++ b/OpenRA.Mods.RA/Move/Move.cs @@ -258,7 +258,7 @@ namespace OpenRA.Mods.RA.Move return Enumerable.Reverse(path).Select(c => Target.FromCell(c)); if (destination != null) return new Target[] { Target.FromCell(destination.Value) }; - return Target.NoTargets; + return Target.None; } abstract class MovePart : Activity diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 3dc9d57449..6f78829aae 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -282,7 +282,6 @@ - @@ -462,10 +461,10 @@ - + diff --git a/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs b/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs index 88491e5e83..bd28e219af 100755 --- a/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/DeployOrderTargeter.cs @@ -35,16 +35,15 @@ namespace OpenRA.Mods.RA.Orders public string OrderID { get; private set; } public int OrderPriority { get; private set; } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { + if (target.Type != TargetType.Actor) + return false; + IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); cursor = useDeployCursor() ? "deploy" : "deploy-blocked"; - return self == target; - } - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { - return false; + return self == target.Actor; } public bool IsQueued { get; protected set; } diff --git a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs b/OpenRA.Mods.RA/Orders/EnterAlliedActorTargeter.cs similarity index 66% rename from OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs rename to OpenRA.Mods.RA/Orders/EnterAlliedActorTargeter.cs index e679830493..6d0b77f49e 100755 --- a/OpenRA.Mods.RA/Orders/EnterBuildingOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/EnterAlliedActorTargeter.cs @@ -9,18 +9,19 @@ #endregion using System; +using System.Collections.Generic; using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders { - public class EnterOrderTargeter : UnitOrderTargeter + public class EnterAlliedActorTargeter : UnitOrderTargeter { readonly Func canTarget; readonly Func useEnterCursor; - public EnterOrderTargeter(string order, int priority, bool targetEnemy, bool targetAlly, + public EnterAlliedActorTargeter(string order, int priority, Func canTarget, Func useEnterCursor) - : base (order, priority, "enter", targetEnemy, targetAlly) + : base (order, priority, "enter", false, true) { this.canTarget = canTarget; this.useEnterCursor = useEnterCursor; @@ -28,18 +29,17 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - - if (!target.HasTrait()) - return false; - - if (!canTarget(target)) + if (!target.HasTrait() || !canTarget(target)) return false; cursor = useEnterCursor(target) ? "enter" : "enter-blocked"; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // Allied actors are never frozen + return false; + } } } diff --git a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs index 2f19c71a2b..3ed8ae752e 100755 --- a/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs +++ b/OpenRA.Mods.RA/Orders/UnitOrderTargeter.cs @@ -15,12 +15,12 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA.Orders { - public class UnitOrderTargeter : IOrderTargeter + public abstract class UnitOrderTargeter : IOrderTargeter { readonly string cursor; readonly bool targetEnemyUnits, targetAllyUnits; - public UnitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits ) + public UnitOrderTargeter(string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits) { this.OrderID = order; this.OrderPriority = priority; @@ -33,27 +33,33 @@ namespace OpenRA.Mods.RA.Orders public int OrderPriority { get; private set; } public bool? ForceAttack = null; - public virtual bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public abstract bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor); + public abstract bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor); + + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - if( self == null ) throw new ArgumentNullException( "self" ); - if( target == null ) throw new ArgumentNullException( "target" ); + var type = target.Type; + if (type != TargetType.Actor && type != TargetType.FrozenActor) + return false; cursor = this.cursor; IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack) return false; + if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack) + return false; - var playerRelationship = self.Owner.Stances[target.Owner]; + var owner = type == TargetType.FrozenActor ? target.FrozenActor.Owner : target.Actor.Owner; + var playerRelationship = self.Owner.Stances[owner]; - if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits) return false; - if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits) return false; + if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits) + return false; - return true; - } + if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits) + return false; - public virtual bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { - return false; + return type == TargetType.FrozenActor ? + CanTargetFrozenActor(self, target.FrozenActor, modifiers, ref cursor) : + CanTargetActor(self, target.Actor, modifiers, ref cursor); } public virtual bool IsQueued { get; protected set; } @@ -71,15 +77,12 @@ namespace OpenRA.Mods.RA.Orders public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; + return target.TraitsImplementing().Any(t => t.TargetTypes.Contains(targetType)); + } - if (!target.TraitsImplementing().Any(t => t.TargetTypes.Contains(targetType))) - return false; - - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - - return true; + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + return target.Info.Traits.WithInterface().Any(t => t.GetTargetTypes().Contains(targetType)); } } } diff --git a/OpenRA.Mods.RA/Passenger.cs b/OpenRA.Mods.RA/Passenger.cs index 7a4c9fc6b8..b9e06883fe 100644 --- a/OpenRA.Mods.RA/Passenger.cs +++ b/OpenRA.Mods.RA/Passenger.cs @@ -35,8 +35,8 @@ namespace OpenRA.Mods.RA { get { - yield return new EnterOrderTargeter( "EnterTransport", 6, false, true, - target => IsCorrectCargoType( target ), target => CanEnter( target ) ); + yield return new EnterAlliedActorTargeter("EnterTransport", 6, + target => IsCorrectCargoType(target), target => CanEnter(target)); } } diff --git a/OpenRA.Mods.RA/RallyPoint.cs b/OpenRA.Mods.RA/RallyPoint.cs index 1db32f3e2b..6d29981e99 100755 --- a/OpenRA.Mods.RA/RallyPoint.cs +++ b/OpenRA.Mods.RA/RallyPoint.cs @@ -58,13 +58,12 @@ namespace OpenRA.Mods.RA public string OrderID { get { return "SetRallyPoint"; } } public int OrderPriority { get { return 0; } } - public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + public bool CanTarget(Actor self, Target target, List othersAtTarget, TargetModifiers modifiers, ref string cursor) { - return false; - } + if (target.Type != TargetType.Terrain) + return false; - public bool CanTargetLocation(Actor self, CPos location, List actorsAtLocation, TargetModifiers modifiers, ref string cursor) - { + var location = target.CenterPosition.ToCPos(); if (self.World.Map.IsInMap(location)) { cursor = "ability"; diff --git a/OpenRA.Mods.RA/Repairable.cs b/OpenRA.Mods.RA/Repairable.cs index 35b1e8bc9c..1fb9c2b5a9 100644 --- a/OpenRA.Mods.RA/Repairable.cs +++ b/OpenRA.Mods.RA/Repairable.cs @@ -38,7 +38,8 @@ namespace OpenRA.Mods.RA public IEnumerable Orders { - get { yield return new EnterOrderTargeter( "Repair", 5, false, true, target => CanRepairAt( target ), _ => CanRepair() ); } + get { yield return new EnterAlliedActorTargeter("Repair", 5, + target => CanRepairAt(target), _ => CanRepair()); } } public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) diff --git a/OpenRA.Mods.RA/RepairableNear.cs b/OpenRA.Mods.RA/RepairableNear.cs index 132d001278..e8e209f71a 100644 --- a/OpenRA.Mods.RA/RepairableNear.cs +++ b/OpenRA.Mods.RA/RepairableNear.cs @@ -38,8 +38,8 @@ namespace OpenRA.Mods.RA { get { - yield return new EnterOrderTargeter( "RepairNear", 5, false, true, - target => CanRepairAt( target ), _ => ShouldRepair() ); + yield return new EnterAlliedActorTargeter("RepairNear", 5, + target => CanRepairAt(target), _ => ShouldRepair()); } } diff --git a/OpenRA.Mods.RA/RepairsBridges.cs b/OpenRA.Mods.RA/RepairsBridges.cs index 2e8ab2cf14..b95ba0f8ad 100644 --- a/OpenRA.Mods.RA/RepairsBridges.cs +++ b/OpenRA.Mods.RA/RepairsBridges.cs @@ -71,9 +71,6 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; - var bridge = target.TraitOrDefault(); if (bridge == null) return false; @@ -83,14 +80,18 @@ namespace OpenRA.Mods.RA if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead) return false; - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - // Can't repair an undamaged bridge if (damage == DamageState.Undamaged) cursor = "goldwrench-blocked"; return true; } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + // TODO: Bridges don't yet support FrozenUnderFog. + return false; + } } } } diff --git a/OpenRA.Mods.RA/SupplyTruck.cs b/OpenRA.Mods.RA/SupplyTruck.cs index b7b779caef..1ff07ba7b2 100644 --- a/OpenRA.Mods.RA/SupplyTruck.cs +++ b/OpenRA.Mods.RA/SupplyTruck.cs @@ -10,10 +10,10 @@ using System.Collections.Generic; using System.Drawing; +using OpenRA.FileFormats; using OpenRA.Mods.RA.Activities; using OpenRA.Mods.RA.Buildings; using OpenRA.Mods.RA.Orders; -using OpenRA.FileFormats; using OpenRA.Traits; namespace OpenRA.Mods.RA @@ -28,11 +28,11 @@ namespace OpenRA.Mods.RA class SupplyTruck : IIssueOrder, IResolveOrder, IOrderVoice { - SupplyTruckInfo Info; + SupplyTruckInfo info; public SupplyTruck(SupplyTruckInfo info) { - Info = info; + this.info = info; } public IEnumerable Orders @@ -42,10 +42,13 @@ namespace OpenRA.Mods.RA public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if (order.OrderID == "DeliverSupplies") - return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + if (order.OrderID != "DeliverSupplies") + return null; - return null; + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; } public string VoicePhraseForOrder(Actor self, Order order) @@ -55,12 +58,18 @@ namespace OpenRA.Mods.RA public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "DeliverSupplies") - { - self.SetTargetLine(Target.FromOrder(order), Color.Yellow); + if (order.OrderString != "DeliverSupplies") + return; + + var target = self.ResolveFrozenActorOrder(order, Color.Yellow); + if (target.Type != TargetType.Actor) + return; + + if (!order.Queued) self.CancelActivity(); - self.QueueActivity(new Enter(order.TargetActor, new DonateSupplies(order.TargetActor, Info.Payload))); - } + + self.SetTargetLine(target, Color.Yellow); + self.QueueActivity(new Enter(target.Actor, new DonateSupplies(target.Actor, info.Payload))); } class SupplyTruckOrderTargeter : UnitOrderTargeter @@ -72,17 +81,12 @@ namespace OpenRA.Mods.RA public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) { - if (!base.CanTargetActor(self, target, modifiers, ref cursor)) - return false; + return target.HasTrait(); + } - if (target.AppearsHostileTo(self)) - return false; - - if (!target.HasTrait()) - return false; - - IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue); - return true; + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + return target.Info.Traits.Contains(); } } } diff --git a/OpenRA.Mods.RA/TargetableBuilding.cs b/OpenRA.Mods.RA/TargetableBuilding.cs index 8bc331338c..6666053472 100755 --- a/OpenRA.Mods.RA/TargetableBuilding.cs +++ b/OpenRA.Mods.RA/TargetableBuilding.cs @@ -15,10 +15,11 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class TargetableBuildingInfo : ITraitInfo, Requires + public class TargetableBuildingInfo : ITraitInfo, ITargetableInfo, Requires { public readonly string[] TargetTypes = { }; + public string[] GetTargetTypes() { return TargetTypes; } public object Create(ActorInitializer init) { return new TargetableBuilding(init.self, this); } } diff --git a/OpenRA.Mods.RA/TargetableUnit.cs b/OpenRA.Mods.RA/TargetableUnit.cs index 282e97c8eb..195e8226c7 100755 --- a/OpenRA.Mods.RA/TargetableUnit.cs +++ b/OpenRA.Mods.RA/TargetableUnit.cs @@ -14,10 +14,11 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class TargetableUnitInfo : ITraitInfo + public class TargetableUnitInfo : ITraitInfo, ITargetableInfo { public readonly string[] TargetTypes = { }; + public string[] GetTargetTypes() { return TargetTypes; } public virtual object Create(ActorInitializer init) { return new TargetableUnit(init.self, this); } } diff --git a/OpenRA.Mods.RA/Turreted.cs b/OpenRA.Mods.RA/Turreted.cs index 68dc809d55..bbd303ae23 100755 --- a/OpenRA.Mods.RA/Turreted.cs +++ b/OpenRA.Mods.RA/Turreted.cs @@ -77,7 +77,7 @@ namespace OpenRA.Mods.RA public virtual void ResolveOrder(Actor self, Order order) { - if (info.AlignWhenIdle && order.OrderString != "Attack" && order.OrderString != "AttackHold") + if (info.AlignWhenIdle && order.OrderString != "Attack") desiredFacing = null; } diff --git a/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs index e34eb34ead..4e946b1286 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/WorldTooltipLogic.cs @@ -41,16 +41,30 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (viewport == null || viewport.TooltipType == WorldTooltipType.None) return; - labelText = viewport.TooltipType == WorldTooltipType.Unexplored ? "Unexplored Terrain" : - viewport.ActorTooltip.Name(); + Player o = null; + switch (viewport.TooltipType) + { + case WorldTooltipType.Unexplored: + labelText = "Unexplored Terrain"; + break; + case WorldTooltipType.Actor: + labelText = viewport.ActorTooltip.Name(); + o = viewport.ActorTooltip.Owner(); + break; + case WorldTooltipType.FrozenActor: + labelText = viewport.FrozenActorTooltip.TooltipName; + o = viewport.FrozenActorTooltip.TooltipOwner; + break; + } + var textWidth = font.Measure(labelText).X; if (textWidth != cachedWidth) { label.Bounds.Width = textWidth; widget.Bounds.Width = 2*label.Bounds.X + textWidth; } - var o = viewport.ActorTooltip != null ? viewport.ActorTooltip.Owner() : null; - showOwner = viewport.TooltipType == WorldTooltipType.Actor && o != null && !o.NonCombatant; + + showOwner = o != null && !o.NonCombatant; if (showOwner) { diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index 99c425c102..01ba2734af 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -203,6 +203,7 @@ Player: BaseAttackNotifier: Shroud: PlayerStatistics: + FrozenActorLayer: World: LoadWidgetAtGameStart: diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index 2d6a543416..23da9915c6 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -269,7 +269,7 @@ Player: RemapIndex: 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240 BaseAttackNotifier: Shroud: - Shroud: false + FrozenActorLayer: HarvesterAttackNotifier: PlayerStatistics: diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 6529debd7f..6be1a8d901 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -524,6 +524,7 @@ Player: RemapIndex: 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95 GpsWatcher: Shroud: + FrozenActorLayer: BaseAttackNotifier: PlayerStatistics: