Add TransformsIntoDockClient

This commit is contained in:
Gustas
2023-01-24 19:33:42 +02:00
committed by Matthias Mailänder
parent deacc7ad65
commit 8e7fa26709
5 changed files with 175 additions and 6 deletions

View File

@@ -116,7 +116,7 @@ namespace OpenRA.Mods.Common.Activities
// of the refinery entrance.
if (LastSearchFailed)
{
var lastproc = harv.DockClientManager.LastReservedHost;
var lastproc = harv.DockClientManager?.LastReservedHost;
if (lastproc != null)
{
var deliveryLoc = self.World.Map.CellContaining(lastproc.DockPosition);
@@ -171,7 +171,7 @@ namespace OpenRA.Mods.Common.Activities
else
{
searchRadius = harvInfo.SearchFromProcRadius;
var dock = harv.DockClientManager.LastReservedHost;
var dock = harv.DockClientManager?.LastReservedHost;
if (dock != null)
{
dockPos = dock.DockPosition;
@@ -247,7 +247,7 @@ namespace OpenRA.Mods.Common.Activities
else
{
var manager = harv.DockClientManager;
if (manager.ReservedHostActor != null)
if (manager?.ReservedHostActor != null)
yield return new TargetLineNode(Target.FromActor(manager.ReservedHostActor), manager.DockLineColor);
}
}

View File

@@ -0,0 +1,165 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Add to a building to expose a move cursor that triggers Transforms and issues a dock order to the transformed actor.")]
public class TransformsIntoDockClientInfo : ConditionalTraitInfo, Requires<TransformsInfo>, IDockClientManagerInfo
{
[CursorReference]
[Desc("Cursor to display when able to dock at target actor.")]
public readonly string EnterCursor = "enter";
[CursorReference]
[Desc("Cursor to display when unable to dock at target actor.")]
public readonly string EnterBlockedCursor = "enter-blocked";
[VoiceReference]
[Desc("Voice.")]
public readonly string Voice = "Action";
[Desc("Color to use for the target line of docking orders.")]
public readonly Color DockLineColor = Color.Green;
[Desc("Require the force-move modifier to display the dock cursor.")]
public readonly bool RequiresForceMove = false;
public override object Create(ActorInitializer init) { return new TransformsIntoDockClient(init.Self, this); }
}
public class TransformsIntoDockClient : ConditionalTrait<TransformsIntoDockClientInfo>, IResolveOrder, IOrderVoice, IIssueOrder
{
readonly Actor self;
protected IDockClient[] dockClients;
readonly Transforms[] transforms;
public TransformsIntoDockClient(Actor self, TransformsIntoDockClientInfo info)
: base(info)
{
this.self = self;
transforms = self.TraitsImplementing<Transforms>().ToArray();
}
protected override void Created(Actor self)
{
base.Created(self);
dockClients = self.TraitsImplementing<IDockClient>().ToArray();
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
yield return new EnterAlliedActorTargeter<DockHostInfo>(
"ForceDock",
6,
Info.EnterCursor,
Info.EnterBlockedCursor,
ForceDockingPossible,
target => CanDockAt(target, true));
yield return new EnterAlliedActorTargeter<DockHostInfo>(
"Dock",
5,
Info.EnterCursor,
Info.EnterBlockedCursor,
DockingPossible,
target => CanDockAt(target, false));
}
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Dock" || order.OrderString == "ForceDock")
{
// Deliver orders are only valid for own/allied actors,
// which are guaranteed to never be frozen.
// TODO: support frozen actors.
if (order.Target.Type != TargetType.Actor)
return;
if (IsTraitDisabled)
return;
var currentTransform = self.CurrentActivity as Transform;
var transform = transforms.FirstOrDefault(t => !t.IsTraitDisabled && !t.IsTraitPaused);
if (transform == null && currentTransform == null)
return;
// Manually manage the inner activity queue.
var activity = currentTransform ?? transform.GetTransformActivity();
if (!order.Queued)
activity.NextActivity?.Cancel(self);
activity.Queue(new IssueOrderAfterTransform(order.OrderString, order.Target, Info.DockLineColor));
if (currentTransform == null)
self.QueueActivity(order.Queued, activity);
self.ShowTargetLines();
}
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString == "Dock" && CanDockAt(order.Target.Actor, false))
return Info.Voice;
else if (order.OrderString == "ForceDock" && CanDockAt(order.Target.Actor, true))
return Info.Voice;
return null;
}
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, in Target target, bool queued)
{
if (order.OrderID == "Dock" || order.OrderID == "ForceDock")
return new Order(order.OrderID, self, target, queued);
return null;
}
/// <summary>Clone of <see cref="DockClientManager.DockingPossible(Actor)"/>.</summary>
public bool DockingPossible(Actor target, TargetModifiers modifiers)
{
var forceEnter = modifiers.HasModifier(TargetModifiers.ForceMove);
if (Info.RequiresForceMove && !forceEnter)
return false;
return !IsTraitDisabled && target.TraitsImplementing<DockHost>().Any(host => dockClients.Any(client => client.IsDockingPossible(host.GetDockType)));
}
/// <summary>Clone of <see cref="DockClientManager.DockingPossible(Actor, TargetModifiers)"/>.</summary>
public bool ForceDockingPossible(Actor target, TargetModifiers modifiers)
{
var forceEnter = modifiers.HasModifier(TargetModifiers.ForceMove);
if (Info.RequiresForceMove && !forceEnter)
return false;
return !IsTraitDisabled && target.TraitsImplementing<DockHost>().Any(host => dockClients.Any(client => client.IsDockingPossible(host.GetDockType, forceEnter)));
}
/// <summary>Clone of <see cref="DockClientManager.CanDockAt(Actor, bool, bool)"/>.</summary>
public bool CanDockAt(Actor target, bool forceEnter = false)
{
if (!(self.CurrentActivity is Transform || transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused)))
return false;
return !IsTraitDisabled && target.TraitsImplementing<DockHost>().Any(host => dockClients.Any(client => client.CanDockAt(target, host, forceEnter, true)));
}
}
}

View File

@@ -14,7 +14,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
public abstract class DockClientBaseInfo : ConditionalTraitInfo, IDockClientInfo, Requires<DockClientManagerInfo> { }
public abstract class DockClientBaseInfo : ConditionalTraitInfo, IDockClientInfo, Requires<IDockClientManagerInfo> { }
public abstract class DockClientBase<InfoType> : ConditionalTrait<InfoType>, IDockClient, INotifyCreated where InfoType : DockClientBaseInfo
{
@@ -27,7 +27,7 @@ namespace OpenRA.Mods.Common.Traits
: base(info)
{
this.self = self;
DockClientManager = self.Trait<DockClientManager>();
DockClientManager = self.TraitOrDefault<DockClientManager>();
}
protected virtual bool CanDock()

View File

@@ -19,7 +19,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Manages DockClients on the actor.")]
public class DockClientManagerInfo : ConditionalTraitInfo
public class DockClientManagerInfo : ConditionalTraitInfo, IDockClientManagerInfo
{
[Desc("How long (in ticks) to wait until (re-)checking for a nearby available DockHost.")]
public readonly int SearchForDockDelay = 125;

View File

@@ -222,6 +222,8 @@ namespace OpenRA.Mods.Common.Traits
public interface IDockClient
{
BitSet<DockType> GetDockType { get; }
/// <summary>When null, the client should act as if it can dock but never do.</summary>
DockClientManager DockClientManager { get; }
void OnDockStarted(Actor self, Actor hostActor, IDockHost host);
bool OnDockTick(Actor self, Actor hostActor, IDockHost dock);
@@ -283,6 +285,8 @@ namespace OpenRA.Mods.Common.Traits
int DragLength { get; }
}
public interface IDockClientManagerInfo : ITraitInfoInterface { }
[RequireExplicitImplementation]
public interface INotifyLoadCargo
{