Merge pull request #3648 from pchote/frozen-fog-rework
Rework frozen actors to support tooltips and orders
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<HealthInfo>();
|
||||
if (health == null) return 0f;
|
||||
var armor = self.Info.Traits.GetOrDefault<ArmorInfo>();
|
||||
if (armor == null || armor.Type == null) return 1;
|
||||
var health = ai.Traits.GetOrDefault<HealthInfo>();
|
||||
if (health == null)
|
||||
return 0f;
|
||||
|
||||
var armor = ai.Traits.GetOrDefault<ArmorInfo>();
|
||||
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<ITargetableInfo>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -234,6 +234,7 @@
|
||||
<Compile Include="Graphics\BeamRenderable.cs" />
|
||||
<Compile Include="Graphics\ContrailRenderable.cs" />
|
||||
<Compile Include="Widgets\ViewportControllerWidget.cs" />
|
||||
<Compile Include="Traits\Player\FrozenActorLayer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">
|
||||
|
||||
@@ -21,11 +21,23 @@ namespace OpenRA.Orders
|
||||
{
|
||||
var underCursor = world.FindUnitsAtMouse(mi.Location)
|
||||
.Where(a => a.HasTrait<ITargetable>())
|
||||
.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<ITargetableInfo>())
|
||||
.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<ITargetable>())
|
||||
.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<ITargetableInfo>())
|
||||
.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<SelectableInfo>();
|
||||
var selectableInfo = a.Traits.GetOrDefault<SelectableInfo>();
|
||||
return selectableInfo != null ? selectableInfo.Priority : int.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<DrawLineToTarget>();
|
||||
@@ -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<DrawLineToTarget>();
|
||||
if (line != null)
|
||||
line.SetTarget(self, Target.FromPos(target.CenterPosition), color, display);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
173
OpenRA.Game/Traits/Player/FrozenActorLayer.cs
Executable file
173
OpenRA.Game/Traits/Player/FrozenActorLayer.cs
Executable file
@@ -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<CPos> 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<CPos> 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<IRenderable> 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<uint, FrozenActor> frozen;
|
||||
List<FrozenActor>[,] bins;
|
||||
|
||||
public FrozenActorLayer(World world, FrozenActorLayerInfo info)
|
||||
{
|
||||
this.info = info;
|
||||
frozen = new Dictionary<uint, FrozenActor>();
|
||||
bins = new List<FrozenActor>[
|
||||
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<FrozenActor>();
|
||||
}
|
||||
|
||||
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<uint>();
|
||||
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<IRenderable> Render(Actor self, WorldRenderer wr)
|
||||
{
|
||||
return frozen.Values
|
||||
.Where(f => f.Visible)
|
||||
.SelectMany(ff => ff.Render(wr));
|
||||
}
|
||||
|
||||
public IEnumerable<FrozenActor> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ITargetable>();
|
||||
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<ITargetable>();
|
||||
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
|
||||
|
||||
@@ -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<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor);
|
||||
bool CanTarget(Actor self, Target target, List<Actor> 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; }
|
||||
|
||||
@@ -134,13 +134,7 @@ namespace OpenRA.Traits
|
||||
|
||||
public static IEnumerable<CPos> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IToolTip>())
|
||||
.OrderByDescending(a => a.Info.SelectionPriority())
|
||||
.FirstOrDefault();
|
||||
|
||||
ActorTooltip = actor.TraitsImplementing<IToolTip>().FirstOrDefault();
|
||||
if (ActorTooltip != null)
|
||||
if (underCursor != null)
|
||||
{
|
||||
ActorTooltip = underCursor.TraitsImplementing<IToolTip>().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)
|
||||
|
||||
@@ -27,6 +27,20 @@ namespace OpenRA
|
||||
return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a));
|
||||
}
|
||||
|
||||
public static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0].AsEnumerable();
|
||||
public static IEnumerable<FrozenActor> FindFrozenActorsAtMouse(this World world, int2 mouseLocation)
|
||||
{
|
||||
if (world.RenderPlayer == null)
|
||||
return NoFrozenActors;
|
||||
|
||||
var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault<FrozenActorLayer>();
|
||||
if (frozenLayer == null)
|
||||
return NoFrozenActors;
|
||||
|
||||
var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2();
|
||||
return frozenLayer.FrozenActorsAt(loc);
|
||||
}
|
||||
|
||||
public static IEnumerable<Actor> FindActorsInBox(this World world, CPos tl, CPos br)
|
||||
{
|
||||
return world.FindActorsInBox(tl.TopLeft, br.BottomRight);
|
||||
|
||||
Reference in New Issue
Block a user