DockClientManager: enter cursor per DockHost's DockType

This commit is contained in:
michaeldgg2
2025-04-30 19:27:49 +02:00
committed by Gustas Kažukauskas
parent 56c615d890
commit 5537a194fa
2 changed files with 91 additions and 38 deletions

View File

@@ -14,6 +14,7 @@ using System.Linq;
using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Activities;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Traits; using OpenRA.Traits;
using static OpenRA.Mods.Common.Traits.DockActorTargeter;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
@@ -66,12 +67,18 @@ namespace OpenRA.Mods.Common.Traits
get get
{ {
yield return new DockActorTargeter( yield return new DockActorTargeter(
6, priority: 6,
Info.EnterCursor, context =>
Info.EnterBlockedCursor, {
() => Info.RequiresForceMove, if (Info.RequiresForceMove && !context.ForceEnter)
CanQueueDockAt, return CanTargetResult.Blocked(Info.EnterCursor);
CanDockAt);
if (!CanQueueDockAt(context.Target.Actor, context.ForceEnter, context.IsQueued))
return CanTargetResult.Blocked(Info.EnterCursor);
var cursor = context.IsQueued || CanDockAt(context.Target.Actor, context.ForceEnter) ? Info.EnterCursor : Info.EnterBlockedCursor;
return CanTargetResult.Allowed(cursor);
});
} }
} }

View File

@@ -16,6 +16,7 @@ using OpenRA.Mods.Common.Activities;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Support; using OpenRA.Support;
using OpenRA.Traits; using OpenRA.Traits;
using static OpenRA.Mods.Common.Traits.DockActorTargeter;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
@@ -33,9 +34,14 @@ namespace OpenRA.Mods.Common.Traits
public readonly BooleanExpression RequireForceMoveCondition = null; public readonly BooleanExpression RequireForceMoveCondition = null;
[CursorReference] [CursorReference]
[Desc("Cursor to display when able to dock at target actor.")] [Desc($"Default cursor to display when able to dock at target actor. Can be overriden using {nameof(EnterCursorOverrides)}.")]
public readonly string EnterCursor = "enter"; public readonly string EnterCursor = "enter";
[CursorReference(dictionaryReference: LintDictionaryReference.Values)]
[Desc($"Cursor to display when able to dock at target actor. Overrides the default cursor specified in {nameof(EnterCursor)}",
"A dictionary of [DockType]: [cursor name].")]
public readonly Dictionary<string, string> EnterCursorOverrides = [];
[CursorReference] [CursorReference]
[Desc("Cursor to display when unable to dock at target actor.")] [Desc("Cursor to display when unable to dock at target actor.")]
public readonly string EnterBlockedCursor = "enter-blocked"; public readonly string EnterBlockedCursor = "enter-blocked";
@@ -155,13 +161,38 @@ namespace OpenRA.Mods.Common.Traits
{ {
get get
{ {
yield return new DockActorTargeter( yield return new DockActorTargeter(6,
6, context =>
Info.EnterCursor, {
Info.EnterBlockedCursor, if (requireForceMove && !context.ForceEnter)
() => requireForceMove, return CanTargetResult.Blocked(Info.EnterCursor);
CanQueueDockAt,
(target, forceEnter) => CanDockAt(target, forceEnter, true)); if (IsTraitDisabled)
return CanTargetResult.Blocked(Info.EnterBlockedCursor);
var availableDockHosts = GetDockableHosts(context.Target.Actor, context.ForceEnter, context.IsQueued).ToList();
if (availableDockHosts.Count == 0)
return CanTargetResult.Blocked(Info.EnterCursor);
var canDock = availableDockHosts.Any(
host => dockClients.Any(client => client.CanDockAt(context.Target.Actor, host, context.ForceEnter, true)));
var cursor = context.IsQueued || canDock
? GetCursorOverride(availableDockHosts) ?? Info.EnterCursor
: Info.EnterBlockedCursor;
return CanTargetResult.Allowed(cursor);
string GetCursorOverride(IEnumerable<IDockHost> dockHosts)
{
foreach (var dockHost in dockHosts)
foreach (var dockType in dockHost.GetDockType)
if (Info.EnterCursorOverrides.TryGetValue(dockType, out var cursor))
return cursor;
return null;
}
});
} }
} }
@@ -261,6 +292,12 @@ namespace OpenRA.Mods.Common.Traits
.Any(host => dockClients.Any(client => client.CanQueueDockAt(target, host, forceEnter, isQueued))); .Any(host => dockClients.Any(client => client.CanQueueDockAt(target, host, forceEnter, isQueued)));
} }
IEnumerable<IDockHost> GetDockableHosts(Actor target, bool forceEnter, bool isQueued)
{
return target.TraitsImplementing<IDockHost>()
.Where(host => dockClients.Any(client => client.CanQueueDockAt(target, host, forceEnter, isQueued)));
}
/// <summary>Find the closest viable <see cref="IDockHost"/>.</summary> /// <summary>Find the closest viable <see cref="IDockHost"/>.</summary>
/// <remarks>If <paramref name="type"/> is not set, scans all clients. Does not check if <see cref="DockClientManager"/> is enabled.</remarks> /// <remarks>If <paramref name="type"/> is not set, scans all clients. Does not check if <see cref="DockClientManager"/> is enabled.</remarks>
public TraitPair<IDockHost>? ClosestDock(IDockHost ignore, BitSet<DockType> type = default, bool forceEnter = false, bool ignoreOccupancy = false) public TraitPair<IDockHost>? ClosestDock(IDockHost ignore, BitSet<DockType> type = default, bool forceEnter = false, bool ignoreOccupancy = false)
@@ -298,22 +335,13 @@ namespace OpenRA.Mods.Common.Traits
public class DockActorTargeter : IOrderTargeter public class DockActorTargeter : IOrderTargeter
{ {
readonly string enterCursor; readonly Func<CanTargetContext, CanTargetResult> canTarget;
readonly string enterBlockedCursor;
readonly Func<bool> requireForceMove;
readonly Func<Actor, bool, bool, bool> canTarget;
readonly Func<Actor, bool, bool> useEnterCursor;
public DockActorTargeter(int priority, string enterCursor, string enterBlockedCursor, public DockActorTargeter(int priority, Func<CanTargetContext, CanTargetResult> canTarget)
Func<bool> requireForceMove, Func<Actor, bool, bool, bool> canTarget, Func<Actor, bool, bool> useEnterCursor)
{ {
OrderID = "Dock"; OrderID = "Dock";
OrderPriority = priority; OrderPriority = priority;
this.enterCursor = enterCursor;
this.enterBlockedCursor = enterBlockedCursor;
this.requireForceMove = requireForceMove;
this.canTarget = canTarget; this.canTarget = canTarget;
this.useEnterCursor = useEnterCursor;
} }
public string OrderID { get; private set; } public string OrderID { get; private set; }
@@ -326,25 +354,43 @@ namespace OpenRA.Mods.Common.Traits
if (target.Type != TargetType.Actor) if (target.Type != TargetType.Actor)
return false; return false;
cursor = enterCursor;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
var forceEnter = modifiers.HasModifier(TargetModifiers.ForceMove); var forceEnter = modifiers.HasModifier(TargetModifiers.ForceMove);
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
OrderID = forceEnter ? "ForceDock" : "Dock"; OrderID = forceEnter ? "ForceDock" : "Dock";
if (requireForceMove() && !forceEnter) var context = new CanTargetContext { Target = target, IsQueued = IsQueued, ForceEnter = forceEnter };
return false; var result = canTarget(context);
cursor = result.Cursor;
if (!canTarget(target.Actor, forceEnter, IsQueued)) return result.CanTarget;
return false;
cursor = IsQueued || useEnterCursor(target.Actor, forceEnter)
? enterCursor
: enterBlockedCursor;
return true;
} }
public virtual bool IsQueued { get; protected set; } public virtual bool IsQueued { get; protected set; }
public readonly record struct CanTargetContext
{
public required Target Target { get; init; }
public bool ForceEnter { get; init; }
public bool IsQueued { get; init; }
}
public readonly record struct CanTargetResult
{
public string Cursor { get; init; }
public bool CanTarget { get; init; }
public static CanTargetResult Blocked(string cursor = null)
{
return new() { CanTarget = false, Cursor = cursor };
}
public static CanTargetResult Allowed(string cursor)
{
return new() { CanTarget = true, Cursor = cursor };
}
}
} }
public static class DockExts public static class DockExts