Let order generators account for selection changes

This commit is contained in:
abcdefg30
2020-05-22 11:21:57 +02:00
committed by Paul Chote
parent b5e3f25418
commit 07d58337f1
12 changed files with 63 additions and 24 deletions

View File

@@ -18,13 +18,14 @@ namespace OpenRA.Orders
public class GenericSelectTarget : UnitOrderGenerator
{
public readonly string OrderName;
protected readonly IEnumerable<Actor> Subjects;
protected readonly string Cursor;
protected readonly MouseButton ExpectedButton;
protected IEnumerable<Actor> subjects;
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
{
Subjects = subjects;
this.subjects = subjects;
OrderName = order;
Cursor = cursor;
ExpectedButton = button;
@@ -53,7 +54,7 @@ namespace OpenRA.Orders
world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, Subjects.ToArray());
yield return new Order(OrderName, null, Target.FromCell(world, cell), queued, null, subjects.ToArray());
}
}
@@ -68,6 +69,11 @@ namespace OpenRA.Orders
return true;
}
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
subjects = selected;
}
public override bool ClearSelectionOnLeftClick { get { return false; } }
}
}

View File

@@ -24,5 +24,6 @@ namespace OpenRA
string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
void Deactivate();
bool HandleKeyPress(KeyInput e);
void SelectionChanged(World world, IEnumerable<Actor> selected);
}
}

View File

@@ -124,6 +124,8 @@ namespace OpenRA.Orders
return false;
}
public virtual void SelectionChanged(World world, IEnumerable<Actor> selected) { }
/// <summary>
/// Returns the most appropriate order for a given actor and target.
/// First priority is given to orders that interact with the given actors.

View File

@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Orders;
@@ -211,9 +212,10 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
protected override void Tick(World world)
protected override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
minelayers.RemoveAll(minelayer => !minelayer.IsInWorld || minelayer.IsDead);
minelayers.Clear();
minelayers.AddRange(selected.Where(s => s.Info.HasTraitInfo<MinelayerInfo>()));
if (!minelayers.Any())
world.CancelInputMode();
}

View File

@@ -10,6 +10,7 @@
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Graphics;
@@ -209,9 +210,9 @@ namespace OpenRA.Mods.Cnc.Traits
}
}
protected override void Tick(World world)
protected override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
if (!self.IsInWorld || self.IsDead)
if (!selected.Contains(self))
world.CancelInputMode();
}

View File

@@ -25,7 +25,6 @@ namespace OpenRA.Mods.Common.Orders
yield return new Order("PlaceBeacon", world.LocalPlayer.PlayerActor, Target.FromCell(world, cell), false) { SuppressVisualFeedback = true };
}
protected override void Tick(World world) { }
protected override IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
protected override IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr, World world) { yield break; }
protected override IEnumerable<IRenderable> RenderAnnotations(WorldRenderer wr, World world) { yield break; }

View File

@@ -28,29 +28,31 @@ namespace OpenRA.Mods.Common.Orders
yield break;
var target = FriendlyGuardableUnits(world, mi).FirstOrDefault();
if (target == null || Subjects.All(s => s.IsDead))
if (target == null)
yield break;
world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift);
yield return new Order(OrderName, null, Target.FromActor(target), queued, null, Subjects.Where(s => s != target).ToArray());
yield return new Order(OrderName, null, Target.FromActor(target), queued, null, subjects.Where(s => s != target).ToArray());
}
public override void Tick(World world)
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
if (Subjects.All(s => s.IsDead || !s.Info.HasTraitInfo<GuardInfo>()))
// Guarding doesn't work without AutoTarget, so require at least one unit in the selection to have it
subjects = selected.Where(s => s.Info.HasTraitInfo<GuardInfo>());
if (!subjects.Any(s => s.Info.HasTraitInfo<AutoTargetInfo>()))
world.CancelInputMode();
}
public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)
{
if (!Subjects.Any())
if (!subjects.Any())
return null;
var multiple = Subjects.Count() > 1;
var multiple = subjects.Count() > 1;
var canGuard = FriendlyGuardableUnits(world, mi)
.Any(a => multiple || a != Subjects.First());
.Any(a => multiple || a != subjects.First());
return canGuard ? Cursor : "move-blocked";
}

View File

@@ -32,12 +32,14 @@ namespace OpenRA.Mods.Common.Orders
string IOrderGenerator.GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { return GetCursor(world, cell, worldPixel, mi); }
void IOrderGenerator.Deactivate() { }
bool IOrderGenerator.HandleKeyPress(KeyInput e) { return false; }
void IOrderGenerator.SelectionChanged(World world, IEnumerable<Actor> selected) { SelectionChanged(world, selected); }
protected abstract void Tick(World world);
protected virtual void Tick(World world) { }
protected abstract IEnumerable<IRenderable> Render(WorldRenderer wr, World world);
protected abstract IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr, World world);
protected abstract IEnumerable<IRenderable> RenderAnnotations(WorldRenderer wr, World world);
protected abstract string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
protected abstract IEnumerable<Order> OrderInner(World world, CPos cell, int2 worldPixel, MouseInput mi);
protected virtual void SelectionChanged(World world, IEnumerable<Actor> selected) { }
}
}

View File

@@ -222,6 +222,8 @@ namespace OpenRA.Mods.Common.Orders
v.Preview.Tick();
}
void IOrderGenerator.SelectionChanged(World world, IEnumerable<Actor> selected) { }
bool AcceptsPlug(CPos cell, PlugInfo plug)
{
var host = buildingInfluence.GetBuildingAt(cell);

View File

@@ -84,7 +84,8 @@ namespace OpenRA.Mods.Common.Traits
public class AttackMoveOrderGenerator : UnitOrderGenerator
{
readonly TraitPair<AttackMove>[] subjects;
TraitPair<AttackMove>[] subjects;
readonly MouseButton expectedButton;
public AttackMoveOrderGenerator(IEnumerable<Actor> subjects, MouseButton button)
@@ -120,9 +121,14 @@ namespace OpenRA.Mods.Common.Traits
}
}
public override void Tick(World world)
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
if (subjects.All(s => s.Actor.IsDead))
subjects = selected.SelectMany(a => a.TraitsImplementing<AttackMove>()
.Select(am => new TraitPair<AttackMove>(a, am)))
.ToArray();
// AttackMove doesn't work without AutoTarget, so require at least one unit in the selection to have it
if (!subjects.Any(s => s.Actor.Info.HasTraitInfo<AutoTargetInfo>()))
world.CancelInputMode();
}

View File

@@ -110,6 +110,8 @@ namespace OpenRA.Mods.Common.Traits
world.CancelInputMode();
}
void IOrderGenerator.SelectionChanged(World world, IEnumerable<Actor> selected) { }
bool IsOutsideDragZone
{
get { return dragStarted && dragDirection.Length > MinDragThreshold; }

View File

@@ -28,6 +28,7 @@ namespace OpenRA.Mods.Common.Traits
public IEnumerable<Actor> Actors { get { return actors; } }
readonly HashSet<Actor> actors = new HashSet<Actor>();
World world;
IEnumerable<Actor> rolloverActors;
INotifySelection[] worldNotifySelection;
@@ -37,6 +38,7 @@ namespace OpenRA.Mods.Common.Traits
void INotifyCreated.Created(Actor self)
{
worldNotifySelection = self.TraitsImplementing<INotifySelection>().ToArray();
world = self.World;
}
void UpdateHash()
@@ -55,6 +57,7 @@ namespace OpenRA.Mods.Common.Traits
foreach (var sel in a.TraitsImplementing<INotifySelected>())
sel.Selected(a);
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => world.OrderGenerator.SelectionChanged(world, actors));
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
}
@@ -64,6 +67,7 @@ namespace OpenRA.Mods.Common.Traits
if (actors.Remove(a))
{
UpdateHash();
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => world.OrderGenerator.SelectionChanged(world, actors));
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
}
@@ -76,7 +80,7 @@ namespace OpenRA.Mods.Common.Traits
// Remove the actor from the original owners selection
// Call UpdateHash directly for everyone else so watchers can account for the owner change if needed
if (oldOwner == a.World.LocalPlayer)
if (oldOwner == world.LocalPlayer)
Remove(a);
else
UpdateHash();
@@ -91,7 +95,8 @@ namespace OpenRA.Mods.Common.Traits
{
if (isClick)
{
var adjNewSelection = newSelection.Take(1); // TODO: select BEST, not FIRST
// TODO: select BEST, not FIRST
var adjNewSelection = newSelection.Take(1);
if (isCombine)
actors.SymmetricExceptWith(adjNewSelection);
else
@@ -117,6 +122,7 @@ namespace OpenRA.Mods.Common.Traits
foreach (var sel in a.TraitsImplementing<INotifySelected>())
sel.Selected(a);
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => world.OrderGenerator.SelectionChanged(world, actors));
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
@@ -143,6 +149,9 @@ namespace OpenRA.Mods.Common.Traits
{
actors.Clear();
UpdateHash();
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => world.OrderGenerator.SelectionChanged(world, actors));
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
}
public void SetRollover(IEnumerable<Actor> rollover)
@@ -157,18 +166,23 @@ namespace OpenRA.Mods.Common.Traits
void ITick.Tick(Actor self)
{
var removed = actors.RemoveWhere(a => !a.IsInWorld || (!a.Owner.IsAlliedWith(self.World.RenderPlayer) && self.World.FogObscures(a)));
var removed = actors.RemoveWhere(a => !a.IsInWorld || (!a.Owner.IsAlliedWith(world.RenderPlayer) && world.FogObscures(a)));
if (removed > 0)
{
UpdateHash();
Sync.RunUnsynced(Game.Settings.Debug.SyncCheckUnsyncedCode, world, () => world.OrderGenerator.SelectionChanged(world, actors));
foreach (var ns in worldNotifySelection)
ns.SelectionChanged();
}
foreach (var cg in controlGroups.Values)
{
// note: NOT `!a.IsInWorld`, since that would remove things that are in transports.
cg.RemoveAll(a => a.Disposed || a.Owner != self.World.LocalPlayer);
cg.RemoveAll(a => a.Disposed || a.Owner != world.LocalPlayer);
}
}
Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());
readonly Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());
public void DoControlGroup(World world, WorldRenderer worldRenderer, int group, Modifiers mods, int multiTapCount)
{