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 class GenericSelectTarget : UnitOrderGenerator
{ {
public readonly string OrderName; public readonly string OrderName;
protected readonly IEnumerable<Actor> Subjects;
protected readonly string Cursor; protected readonly string Cursor;
protected readonly MouseButton ExpectedButton; protected readonly MouseButton ExpectedButton;
protected IEnumerable<Actor> subjects;
public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button) public GenericSelectTarget(IEnumerable<Actor> subjects, string order, string cursor, MouseButton button)
{ {
Subjects = subjects; this.subjects = subjects;
OrderName = order; OrderName = order;
Cursor = cursor; Cursor = cursor;
ExpectedButton = button; ExpectedButton = button;
@@ -53,7 +54,7 @@ namespace OpenRA.Orders
world.CancelInputMode(); world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift); 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; return true;
} }
public override void SelectionChanged(World world, IEnumerable<Actor> selected)
{
subjects = selected;
}
public override bool ClearSelectionOnLeftClick { get { return false; } } 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); string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
void Deactivate(); void Deactivate();
bool HandleKeyPress(KeyInput e); bool HandleKeyPress(KeyInput e);
void SelectionChanged(World world, IEnumerable<Actor> selected);
} }
} }

View File

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

View File

@@ -12,6 +12,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Activities; using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Orders; 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()) if (!minelayers.Any())
world.CancelInputMode(); world.CancelInputMode();
} }

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Cnc.Activities; using OpenRA.Mods.Cnc.Activities;
using OpenRA.Mods.Common.Graphics; 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(); 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 }; 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> Render(WorldRenderer wr, World world) { yield break; }
protected override IEnumerable<IRenderable> RenderAboveShroud(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; } protected override IEnumerable<IRenderable> RenderAnnotations(WorldRenderer wr, World world) { yield break; }

View File

@@ -28,29 +28,31 @@ namespace OpenRA.Mods.Common.Orders
yield break; yield break;
var target = FriendlyGuardableUnits(world, mi).FirstOrDefault(); var target = FriendlyGuardableUnits(world, mi).FirstOrDefault();
if (target == null || Subjects.All(s => s.IsDead)) if (target == null)
yield break; yield break;
world.CancelInputMode(); world.CancelInputMode();
var queued = mi.Modifiers.HasModifier(Modifiers.Shift); 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(); world.CancelInputMode();
} }
public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) public override string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi)
{ {
if (!Subjects.Any()) if (!subjects.Any())
return null; return null;
var multiple = Subjects.Count() > 1; var multiple = subjects.Count() > 1;
var canGuard = FriendlyGuardableUnits(world, mi) var canGuard = FriendlyGuardableUnits(world, mi)
.Any(a => multiple || a != Subjects.First()); .Any(a => multiple || a != subjects.First());
return canGuard ? Cursor : "move-blocked"; 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); } string IOrderGenerator.GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi) { return GetCursor(world, cell, worldPixel, mi); }
void IOrderGenerator.Deactivate() { } void IOrderGenerator.Deactivate() { }
bool IOrderGenerator.HandleKeyPress(KeyInput e) { return false; } 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> Render(WorldRenderer wr, World world);
protected abstract IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr, World world); protected abstract IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr, World world);
protected abstract IEnumerable<IRenderable> RenderAnnotations(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 string GetCursor(World world, CPos cell, int2 worldPixel, MouseInput mi);
protected abstract IEnumerable<Order> OrderInner(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(); v.Preview.Tick();
} }
void IOrderGenerator.SelectionChanged(World world, IEnumerable<Actor> selected) { }
bool AcceptsPlug(CPos cell, PlugInfo plug) bool AcceptsPlug(CPos cell, PlugInfo plug)
{ {
var host = buildingInfluence.GetBuildingAt(cell); var host = buildingInfluence.GetBuildingAt(cell);

View File

@@ -84,7 +84,8 @@ namespace OpenRA.Mods.Common.Traits
public class AttackMoveOrderGenerator : UnitOrderGenerator public class AttackMoveOrderGenerator : UnitOrderGenerator
{ {
readonly TraitPair<AttackMove>[] subjects; TraitPair<AttackMove>[] subjects;
readonly MouseButton expectedButton; readonly MouseButton expectedButton;
public AttackMoveOrderGenerator(IEnumerable<Actor> subjects, MouseButton button) 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(); world.CancelInputMode();
} }

View File

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

View File

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