183 lines
6.0 KiB
C#
183 lines
6.0 KiB
C#
#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 OpenRA.Activities;
|
|
using OpenRA.Mods.Common.Activities;
|
|
using OpenRA.Primitives;
|
|
using OpenRA.Traits;
|
|
|
|
namespace OpenRA.Mods.Common.Traits
|
|
{
|
|
public sealed class DockType { DockType() { } }
|
|
|
|
[Desc("A generic dock that services DockClients.")]
|
|
public class DockHostInfo : ConditionalTraitInfo, IDockHostInfo
|
|
{
|
|
[Desc("Docking type.")]
|
|
public readonly BitSet<DockType> Type;
|
|
|
|
[Desc("How many clients can this dock be reserved for?")]
|
|
public readonly int MaxQueueLength = 3;
|
|
|
|
[Desc("How long should the client wait before starting the docking sequence.")]
|
|
public readonly int DockWait = 10;
|
|
|
|
[Desc("Actual client facing when docking.")]
|
|
public readonly WAngle DockAngle = WAngle.Zero;
|
|
|
|
[Desc("Docking cell relative to the centre of the actor.")]
|
|
public readonly WVec DockOffset = WVec.Zero;
|
|
|
|
[Desc("Does client need to be dragged in?")]
|
|
public readonly bool IsDragRequired = false;
|
|
|
|
[Desc("Vector by which the client will be dragged when docking.")]
|
|
public readonly WVec DragOffset = WVec.Zero;
|
|
|
|
[Desc("In how many steps to perform the dragging?")]
|
|
public readonly int DragLength = 0;
|
|
|
|
public override object Create(ActorInitializer init) { return new DockHost(init.Self, this); }
|
|
}
|
|
|
|
public class DockHost : ConditionalTrait<DockHostInfo>,
|
|
IDockHost, IDockHostDrag, ITick, INotifySold, INotifyCapture, INotifyOwnerChanged, ISync, INotifyKilled, INotifyActorDisposing
|
|
{
|
|
readonly Actor self;
|
|
|
|
public BitSet<DockType> GetDockType => Info.Type;
|
|
public bool IsEnabledAndInWorld => !preventDock && !IsTraitDisabled && !self.IsDead && self.IsInWorld;
|
|
public int ReservationCount => ReservedDockClients.Count;
|
|
public bool CanBeReserved => ReservationCount < Info.MaxQueueLength;
|
|
protected readonly List<DockClientManager> ReservedDockClients = new();
|
|
|
|
public WPos DockPosition => self.CenterPosition + Info.DockOffset;
|
|
public int DockWait => Info.DockWait;
|
|
public WAngle DockAngle => Info.DockAngle;
|
|
|
|
bool IDockHostDrag.IsDragRequired => Info.IsDragRequired;
|
|
WVec IDockHostDrag.DragOffset => Info.DragOffset;
|
|
int IDockHostDrag.DragLength => Info.DragLength;
|
|
|
|
[Sync]
|
|
bool preventDock = false;
|
|
|
|
[Sync]
|
|
protected Actor dockedClientActor = null;
|
|
protected DockClientManager dockedClient = null;
|
|
|
|
public DockHost(Actor self, DockHostInfo info)
|
|
: base(info)
|
|
{
|
|
this.self = self;
|
|
}
|
|
|
|
public virtual bool IsDockingPossible(Actor clientActor, IDockClient client, bool ignoreReservations = false)
|
|
{
|
|
return !IsTraitDisabled && (ignoreReservations || CanBeReserved || ReservedDockClients.Contains(client.DockClientManager));
|
|
}
|
|
|
|
public virtual bool Reserve(Actor self, DockClientManager client)
|
|
{
|
|
if (CanBeReserved && !ReservedDockClients.Contains(client))
|
|
{
|
|
ReservedDockClients.Add(client);
|
|
client.ReserveHost(self, this);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual void UnreserveAll()
|
|
{
|
|
while (ReservedDockClients.Count > 0)
|
|
Unreserve(ReservedDockClients[0]);
|
|
}
|
|
|
|
public virtual void Unreserve(DockClientManager client)
|
|
{
|
|
if (ReservedDockClients.Remove(client))
|
|
client.UnreserveHost();
|
|
}
|
|
|
|
public virtual void OnDockStarted(Actor self, Actor clientActor, DockClientManager client)
|
|
{
|
|
dockedClientActor = clientActor;
|
|
dockedClient = client;
|
|
}
|
|
|
|
public virtual void OnDockCompleted(Actor self, Actor clientActor, DockClientManager client)
|
|
{
|
|
dockedClientActor = null;
|
|
dockedClient = null;
|
|
}
|
|
|
|
void ITick.Tick(Actor self)
|
|
{
|
|
Tick(self);
|
|
}
|
|
|
|
protected virtual void Tick(Actor self)
|
|
{
|
|
// Client was killed during docking.
|
|
if (dockedClientActor != null && (dockedClientActor.IsDead || !dockedClientActor.IsInWorld))
|
|
OnDockCompleted(self, dockedClientActor, dockedClient);
|
|
}
|
|
|
|
public virtual bool QueueMoveActivity(Activity moveToDockActivity, Actor self, Actor clientActor, DockClientManager client, MoveCooldownHelper moveCooldownHelper)
|
|
{
|
|
var move = clientActor.Trait<IMove>();
|
|
|
|
// Make sure the actor is at dock, at correct facing, and aircraft are landed.
|
|
// Mobile cannot freely move in WPos, so when we calculate close enough we convert to CPos.
|
|
if ((move is Mobile ? clientActor.Location != clientActor.World.Map.CellContaining(DockPosition) : clientActor.CenterPosition != DockPosition)
|
|
|| move is not IFacing facing || facing.Facing != DockAngle)
|
|
{
|
|
moveCooldownHelper.NotifyMoveQueued();
|
|
moveToDockActivity.QueueChild(move.MoveOntoTarget(clientActor, Target.FromActor(self), DockPosition - self.CenterPosition, DockAngle));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public virtual void QueueDockActivity(Activity moveToDockActivity, Actor self, Actor clientActor, DockClientManager client)
|
|
{
|
|
moveToDockActivity.QueueChild(new GenericDockSequence(clientActor, client, self, this));
|
|
}
|
|
|
|
protected override void TraitDisabled(Actor self) { UnreserveAll(); }
|
|
|
|
void INotifyOwnerChanged.OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { UnreserveAll(); }
|
|
|
|
void INotifyCapture.OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner, BitSet<CaptureType> captureTypes)
|
|
{
|
|
// Steal any docked unit too.
|
|
if (dockedClientActor != null && !dockedClientActor.IsDead && dockedClientActor.IsInWorld)
|
|
{
|
|
dockedClientActor.ChangeOwner(newOwner);
|
|
|
|
// On capture OnOwnerChanged event is called first, so we need to re-reserve.
|
|
dockedClient.ReserveHost(self, this);
|
|
}
|
|
}
|
|
|
|
void INotifySold.Selling(Actor self) { preventDock = true; }
|
|
|
|
void INotifySold.Sold(Actor self) { UnreserveAll(); }
|
|
|
|
void INotifyKilled.Killed(Actor self, AttackInfo e) { UnreserveAll(); }
|
|
|
|
void INotifyActorDisposing.Disposing(Actor self) { preventDock = true; UnreserveAll(); }
|
|
}
|
|
}
|