Merge pull request #6528 from atlimit8/DragIntoTransports
Modifies IMove & Enter and Fixes #4640 - Units teleport into transports
This commit is contained in:
@@ -101,6 +101,7 @@ namespace OpenRA.Traits
|
|||||||
public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); }
|
public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); }
|
||||||
public interface INotifyHarvest { void Harvested(Actor self, ResourceType resource); }
|
public interface INotifyHarvest { void Harvested(Actor self, ResourceType resource); }
|
||||||
public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator); }
|
public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator); }
|
||||||
|
public interface IDisableMove { bool MoveDisabled(Actor self); }
|
||||||
|
|
||||||
public interface IUpgradable
|
public interface IUpgradable
|
||||||
{
|
{
|
||||||
@@ -206,9 +207,12 @@ namespace OpenRA.Traits
|
|||||||
Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange);
|
Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange);
|
||||||
Activity MoveFollow(Actor self, Target target, WRange minRange, WRange maxRange);
|
Activity MoveFollow(Actor self, Target target, WRange minRange, WRange maxRange);
|
||||||
Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any);
|
Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any);
|
||||||
|
Activity MoveToTarget(Actor self, Target target);
|
||||||
|
Activity MoveIntoTarget(Actor self, Target target);
|
||||||
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
|
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
|
||||||
CPos NearestMoveableCell(CPos target);
|
CPos NearestMoveableCell(CPos target);
|
||||||
bool IsMoving { get; set; }
|
bool IsMoving { get; set; }
|
||||||
|
bool CanEnterTargetNow(Actor self, Target target);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface INotifyBlockingMove { void OnNotifyBlockingMove(Actor self, Actor blocking); }
|
public interface INotifyBlockingMove { void OnNotifyBlockingMove(Actor self, Actor blocking); }
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ namespace OpenRA.Mods.Cnc
|
|||||||
return this;
|
return this;
|
||||||
case State.Turn:
|
case State.Turn:
|
||||||
state = State.DragIn;
|
state = State.DragIn;
|
||||||
return Util.SequenceActivities(new Turn(112), this);
|
return Util.SequenceActivities(new Turn(self, 112), this);
|
||||||
case State.DragIn:
|
case State.DragIn:
|
||||||
state = State.Dock;
|
state = State.Dock;
|
||||||
return Util.SequenceActivities(new Drag(startDock, endDock, 12), this);
|
return Util.SequenceActivities(new Drag(self, startDock, endDock, 12), this);
|
||||||
case State.Dock:
|
case State.Dock:
|
||||||
ru.PlayCustomAnimation(self, "dock", () => {
|
ru.PlayCustomAnimation(self, "dock", () => {
|
||||||
ru.PlayCustomAnimRepeating(self, "dock-loop");
|
ru.PlayCustomAnimRepeating(self, "dock-loop");
|
||||||
@@ -73,7 +73,7 @@ namespace OpenRA.Mods.Cnc
|
|||||||
state = State.Wait;
|
state = State.Wait;
|
||||||
return this;
|
return this;
|
||||||
case State.DragOut:
|
case State.DragOut:
|
||||||
return Util.SequenceActivities(new Drag(endDock, startDock, 12), NextActivity);
|
return Util.SequenceActivities(new Drag(self, endDock, startDock, 12), NextActivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Invalid harvester dock state");
|
throw new InvalidOperationException("Invalid harvester dock state");
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Cnc
|
|||||||
cargoFacing.Facing = facing.Facing;
|
cargoFacing.Facing = facing.Facing;
|
||||||
|
|
||||||
var cargoPassenger = c.Trait<Passenger>();
|
var cargoPassenger = c.Trait<Passenger>();
|
||||||
if (cargoInfo.DisplayTypes.Contains(cargoPassenger.info.CargoType))
|
if (cargoInfo.DisplayTypes.Contains(cargoPassenger.Info.CargoType))
|
||||||
{
|
{
|
||||||
var offset = pos - c.CenterPosition + body.LocalToWorld(cargoInfo.LocalOffset[i++ % cargoInfo.LocalOffset.Length].Rotate(bodyOrientation));
|
var offset = pos - c.CenterPosition + body.LocalToWorld(cargoInfo.LocalOffset[i++ % cargoInfo.LocalOffset.Length].Rotate(bodyOrientation));
|
||||||
foreach (var cr in c.Render(wr))
|
foreach (var cr in c.Render(wr))
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
|
|
||||||
var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0);
|
var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0);
|
||||||
if (facing.Facing != desiredFacing)
|
if (facing.Facing != desiredFacing)
|
||||||
return Util.SequenceActivities(new Turn(desiredFacing), this);
|
return Util.SequenceActivities(new Turn(self, desiredFacing), this);
|
||||||
|
|
||||||
attack.DoAttack(self, Target);
|
attack.DoAttack(self, Target);
|
||||||
|
|
||||||
|
|||||||
@@ -8,41 +8,267 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using OpenRA.Mods.RA.Move;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
public class Enter : Activity
|
public class Enter : Activity
|
||||||
{
|
{
|
||||||
readonly Target target;
|
public enum ReserveStatus { None, TooFar, Pending, Ready }
|
||||||
readonly Activity inner;
|
enum State { ApproachingOrEntering, Inside, Exiting, Done }
|
||||||
|
|
||||||
public Enter(Actor target, Activity inner)
|
readonly Activity inside;
|
||||||
|
readonly IMove move;
|
||||||
|
readonly int maxTries = 0;
|
||||||
|
Target target;
|
||||||
|
State nextState = State.ApproachingOrEntering; // Hint/starting point for next state
|
||||||
|
bool isEnteringOrInside = false; // Used to know if exiting should be used
|
||||||
|
WPos savedPos; // Position just before entering
|
||||||
|
Activity inner;
|
||||||
|
bool firstApproach = true;
|
||||||
|
|
||||||
|
protected Enter(Actor self, Actor target, int maxTries = 1)
|
||||||
|
: this(self, target, null)
|
||||||
{
|
{
|
||||||
|
this.maxTries = maxTries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enter(Actor self, Actor target, Activity inside)
|
||||||
|
{
|
||||||
|
this.move = self.Trait<IMove>();
|
||||||
this.target = Target.FromActor(target);
|
this.target = Target.FromActor(target);
|
||||||
this.inner = inner;
|
this.inside = inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanEnter(target) should to be true; othwise, Enter may abort.
|
||||||
|
// Tries counter starts at 1 (reset every tick)
|
||||||
|
protected virtual bool TryGetAlternateTarget(Actor self, int tries, ref Target target) { return false; }
|
||||||
|
protected virtual bool CanReserve(Actor self) { return true; }
|
||||||
|
protected virtual ReserveStatus Reserve(Actor self)
|
||||||
|
{
|
||||||
|
return !CanReserve(self) ? ReserveStatus.None : move.CanEnterTargetNow(self, target) ? ReserveStatus.Ready : ReserveStatus.TooFar;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Unreserve(Actor self, bool abort) { }
|
||||||
|
protected virtual void OnInside(Actor self) { }
|
||||||
|
|
||||||
|
protected bool TryGetAlternateTargetInCircle(Actor self, WRange radius, Action<Target> update, Func<Actor, bool> primaryFilter, Func<Actor, bool>[] preferenceFilters = null)
|
||||||
|
{
|
||||||
|
var radiusSquared = radius.Range * radius.Range;
|
||||||
|
var diff = new WVec(radius, radius, WRange.Zero);
|
||||||
|
var candidates = self.World.ActorMap.ActorsInBox(self.CenterPosition - diff, self.CenterPosition + diff)
|
||||||
|
.Where(primaryFilter).Select(a => new { Actor = a, Ls = (self.CenterPosition - a.CenterPosition).HorizontalLengthSquared })
|
||||||
|
.Where(p => p.Ls <= radiusSquared).OrderBy(p => p.Ls).Select(p => p.Actor);
|
||||||
|
if (preferenceFilters != null)
|
||||||
|
foreach (var filter in preferenceFilters)
|
||||||
|
{
|
||||||
|
var preferredCandidate = candidates.FirstOrDefault(filter);
|
||||||
|
if (preferredCandidate == null)
|
||||||
|
continue;
|
||||||
|
target = Target.FromActor(preferredCandidate);
|
||||||
|
update(target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var candidate = candidates.FirstOrDefault();
|
||||||
|
if (candidate == null)
|
||||||
|
return false;
|
||||||
|
target = Target.FromActor(candidate);
|
||||||
|
update(target);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when inner activity is this and returns inner activity for next tick.
|
||||||
|
protected virtual Activity InsideTick(Actor self) { return Util.RunActivity(self, inside); }
|
||||||
|
|
||||||
|
// Abort entering and/or leave if necessary
|
||||||
|
protected virtual void AbortOrExit(Actor self)
|
||||||
|
{
|
||||||
|
if (nextState == State.Done)
|
||||||
|
return;
|
||||||
|
nextState = isEnteringOrInside ? State.Exiting : State.Done;
|
||||||
|
if (inner == this)
|
||||||
|
inner = null;
|
||||||
|
else if (inner != null)
|
||||||
|
inner.Cancel(self);
|
||||||
|
if (isEnteringOrInside)
|
||||||
|
Unreserve(self, true);
|
||||||
|
isEnteringOrInside = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel inner activity and mark as done unless already leaving or done
|
||||||
|
protected void Done(Actor self)
|
||||||
|
{
|
||||||
|
if (nextState == State.Done)
|
||||||
|
return;
|
||||||
|
nextState = State.Done;
|
||||||
|
if (inner == this)
|
||||||
|
inner = null;
|
||||||
|
else if (inner != null)
|
||||||
|
inner.Cancel(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Cancel(Actor self)
|
||||||
|
{
|
||||||
|
AbortOrExit(self);
|
||||||
|
if (nextState < State.Exiting)
|
||||||
|
base.Cancel(self);
|
||||||
|
else
|
||||||
|
NextActivity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReserveStatus TryReserveElseTryAlternateReserve(Actor self)
|
||||||
|
{
|
||||||
|
for (var tries = 0;;)
|
||||||
|
switch (Reserve(self))
|
||||||
|
{
|
||||||
|
case ReserveStatus.None:
|
||||||
|
if (++tries > maxTries || !TryGetAlternateTarget(self, tries, ref target))
|
||||||
|
return ReserveStatus.None;
|
||||||
|
continue;
|
||||||
|
case ReserveStatus.TooFar:
|
||||||
|
// Always goto to transport on first approach
|
||||||
|
if (firstApproach)
|
||||||
|
{
|
||||||
|
firstApproach = false;
|
||||||
|
return ReserveStatus.TooFar;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++tries > maxTries)
|
||||||
|
return ReserveStatus.TooFar;
|
||||||
|
Target t = target;
|
||||||
|
if (!TryGetAlternateTarget(self, tries, ref t))
|
||||||
|
return ReserveStatus.TooFar;
|
||||||
|
if ((target.CenterPosition - self.CenterPosition).HorizontalLengthSquared <= (t.CenterPosition - self.CenterPosition).HorizontalLengthSquared)
|
||||||
|
return ReserveStatus.TooFar;
|
||||||
|
target = t;
|
||||||
|
continue;
|
||||||
|
case ReserveStatus.Pending:
|
||||||
|
return ReserveStatus.Pending;
|
||||||
|
case ReserveStatus.Ready:
|
||||||
|
return ReserveStatus.Ready;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
State FindAndTransitionToNextState(Actor self)
|
||||||
|
{
|
||||||
|
switch (nextState)
|
||||||
|
{
|
||||||
|
case State.ApproachingOrEntering:
|
||||||
|
|
||||||
|
// Reserve to enter or approach
|
||||||
|
isEnteringOrInside = false;
|
||||||
|
switch (TryReserveElseTryAlternateReserve(self))
|
||||||
|
{
|
||||||
|
case ReserveStatus.None:
|
||||||
|
return State.Done; // No available target -> abort to next activity
|
||||||
|
case ReserveStatus.TooFar:
|
||||||
|
inner = move.MoveToTarget(self, Target.FromPos(target.CenterPosition)); // Approach
|
||||||
|
return State.ApproachingOrEntering;
|
||||||
|
case ReserveStatus.Pending:
|
||||||
|
return State.ApproachingOrEntering; // Retry next tick
|
||||||
|
case ReserveStatus.Ready:
|
||||||
|
break; // Reserved target -> start entering target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entering
|
||||||
|
isEnteringOrInside = true;
|
||||||
|
savedPos = self.CenterPosition; // Save position of self, before entering, for returning on exit
|
||||||
|
inner = move.MoveIntoTarget(self, target); // Enter
|
||||||
|
|
||||||
|
if (inner != null)
|
||||||
|
{
|
||||||
|
nextState = State.Inside; // Should be inside once inner activity is null
|
||||||
|
return State.ApproachingOrEntering;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can enter but there is no activity for it, so go inside without one
|
||||||
|
goto case State.Inside;
|
||||||
|
|
||||||
|
case State.Inside:
|
||||||
|
// Might as well teleport into target if there is no MoveIntoTarget activity
|
||||||
|
if (nextState == State.ApproachingOrEntering)
|
||||||
|
nextState = State.Inside;
|
||||||
|
|
||||||
|
// Otherwise, try to recover from moving target
|
||||||
|
else if (target.CenterPosition != self.CenterPosition)
|
||||||
|
{
|
||||||
|
nextState = State.ApproachingOrEntering;
|
||||||
|
Unreserve(self, false);
|
||||||
|
if (Reserve(self) == ReserveStatus.Ready)
|
||||||
|
{
|
||||||
|
inner = move.MoveIntoTarget(self, target); // Enter
|
||||||
|
if (inner != null)
|
||||||
|
return State.ApproachingOrEntering;
|
||||||
|
|
||||||
|
nextState = State.ApproachingOrEntering;
|
||||||
|
goto case State.ApproachingOrEntering;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextState = State.ApproachingOrEntering;
|
||||||
|
isEnteringOrInside = false;
|
||||||
|
inner = move.MoveIntoWorld(self, self.World.Map.CellContaining(savedPos));
|
||||||
|
|
||||||
|
return State.ApproachingOrEntering;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnInside(self);
|
||||||
|
|
||||||
|
// Return if Abort(Actor) or Done(self) was called from OnInside.
|
||||||
|
if (nextState >= State.Exiting)
|
||||||
|
return State.Inside;
|
||||||
|
|
||||||
|
inner = this; // Start inside activity
|
||||||
|
nextState = State.Exiting; // Exit once inner activity is null (unless Done(self) is called)
|
||||||
|
return State.Inside;
|
||||||
|
|
||||||
|
// TODO: Handle target moved while inside or always call done for movable targets and use a separate exit activity
|
||||||
|
case State.Exiting:
|
||||||
|
inner = move.MoveIntoWorld(self, self.World.Map.CellContaining(savedPos));
|
||||||
|
|
||||||
|
// If not successfully exiting, retry on next tick
|
||||||
|
if (inner == null)
|
||||||
|
return State.Exiting;
|
||||||
|
isEnteringOrInside = false;
|
||||||
|
nextState = State.Done;
|
||||||
|
return State.Exiting;
|
||||||
|
|
||||||
|
case State.Done:
|
||||||
|
return State.Done;
|
||||||
|
}
|
||||||
|
|
||||||
|
return State.Done; // dummy to quiet dumb compiler
|
||||||
|
}
|
||||||
|
|
||||||
|
Activity CanceledTick(Actor self)
|
||||||
|
{
|
||||||
|
if (inner == null)
|
||||||
|
return Util.RunActivity(self, NextActivity);
|
||||||
|
inner.Cancel(self);
|
||||||
|
inner.Queue(NextActivity);
|
||||||
|
return Util.RunActivity(self, inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled || !target.IsValidFor(self))
|
if (IsCanceled)
|
||||||
return NextActivity;
|
return CanceledTick(self);
|
||||||
|
|
||||||
if (target.Type != TargetType.Actor)
|
// Check target validity if not exiting or done
|
||||||
return NextActivity;
|
if (nextState != State.Done && (target.Type != TargetType.Actor || !target.IsValidFor(self)))
|
||||||
|
AbortOrExit(self);
|
||||||
|
|
||||||
if (!Util.AdjacentCells(self.World, target).Any(c => c == self.Location))
|
// If no current activity, tick next activity
|
||||||
return Util.SequenceActivities(new MoveAdjacentTo(self, target), this);
|
if (inner == null && FindAndTransitionToNextState(self) == State.Done)
|
||||||
|
return CanceledTick(self);
|
||||||
|
|
||||||
// Move to the middle of the target, ignoring impassable tiles
|
// Run inner activity/InsideTick
|
||||||
var move = self.Trait<IMove>();
|
inner = inner == this ? InsideTick(self) : Util.RunActivity(self, inner);
|
||||||
return Util.SequenceActivities(
|
return this;
|
||||||
move.VisualMove(self, self.CenterPosition, target.CenterPosition),
|
|
||||||
inner,
|
|
||||||
move.VisualMove(self, target.CenterPosition, self.CenterPosition),
|
|
||||||
NextActivity
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,39 +8,42 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
class EnterTransport : Activity
|
class EnterTransport : Enter
|
||||||
{
|
{
|
||||||
readonly Actor transport;
|
readonly Actor transport;
|
||||||
readonly Cargo cargo;
|
readonly Passenger passenger;
|
||||||
|
readonly int maxTries;
|
||||||
|
Cargo cargo;
|
||||||
|
|
||||||
public EnterTransport(Actor self, Actor transport)
|
public EnterTransport(Actor self, Actor transport, int maxTries = 0)
|
||||||
|
: base(self, transport, maxTries)
|
||||||
{
|
{
|
||||||
this.transport = transport;
|
this.transport = transport;
|
||||||
|
this.maxTries = maxTries;
|
||||||
cargo = transport.Trait<Cargo>();
|
cargo = transport.Trait<Cargo>();
|
||||||
|
passenger = self.Trait<Passenger>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick(Actor self)
|
protected override void Unreserve(Actor self, bool abort) { passenger.Unreserve(self); }
|
||||||
|
protected override bool CanReserve(Actor self) { return cargo.Unloading || cargo.CanLoad(transport, self); }
|
||||||
|
protected override ReserveStatus Reserve(Actor self)
|
||||||
{
|
{
|
||||||
if (IsCanceled)
|
var status = base.Reserve(self);
|
||||||
return NextActivity;
|
if (status != ReserveStatus.Ready)
|
||||||
|
return status;
|
||||||
if (transport == null || !transport.IsInWorld)
|
if (passenger.Reserve(self, cargo))
|
||||||
return NextActivity;
|
return ReserveStatus.Ready;
|
||||||
|
return ReserveStatus.Pending;
|
||||||
if (!cargo.CanLoad(transport, self))
|
}
|
||||||
return NextActivity;
|
|
||||||
|
|
||||||
// TODO: Queue a move order to the transport? need to be
|
|
||||||
// careful about units that can't path to the transport
|
|
||||||
var cells = Util.AdjacentCells(self.World, Target.FromActor(transport));
|
|
||||||
if (!cells.Contains(self.Location))
|
|
||||||
return NextActivity;
|
|
||||||
|
|
||||||
|
protected override void OnInside(Actor self)
|
||||||
|
{
|
||||||
self.World.AddFrameEndTask(w =>
|
self.World.AddFrameEndTask(w =>
|
||||||
{
|
{
|
||||||
if (self.IsDead() || transport.IsDead() || !cargo.CanLoad(transport, self))
|
if (self.IsDead() || transport.IsDead() || !cargo.CanLoad(transport, self))
|
||||||
@@ -50,7 +53,19 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
w.Remove(self);
|
w.Remove(self);
|
||||||
});
|
});
|
||||||
|
|
||||||
return this;
|
Done(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool TryGetAlternateTarget(Actor self, int tries, ref Target target)
|
||||||
|
{
|
||||||
|
if (tries > maxTries)
|
||||||
|
return false;
|
||||||
|
var type = target.Actor.Info.Name;
|
||||||
|
return TryGetAlternateTargetInCircle(
|
||||||
|
self, passenger.Info.AlternateTransportScanRange,
|
||||||
|
t => cargo = t.Actor.Trait<Cargo>(), // update cargo
|
||||||
|
a => { var c = a.TraitOrDefault<Cargo>(); return c != null && (c.Unloading || c.CanLoad(a, self)); },
|
||||||
|
new Func<Actor, bool>[] { a => a.Info.Name == type }); // Prefer transports of the same type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
var facing = self.Trait<IFacing>().Facing;
|
var facing = self.Trait<IFacing>().Facing;
|
||||||
var desired = Util.QuantizeFacing(facing, harvInfo.HarvestFacings) * (256 / harvInfo.HarvestFacings);
|
var desired = Util.QuantizeFacing(facing, harvInfo.HarvestFacings) * (256 / harvInfo.HarvestFacings);
|
||||||
if (desired != facing)
|
if (desired != facing)
|
||||||
return Util.SequenceActivities(new Turn(desired), this);
|
return Util.SequenceActivities(new Turn(self, desired), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
var resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
var resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA
|
|||||||
return this;
|
return this;
|
||||||
case State.Turn:
|
case State.Turn:
|
||||||
state = State.Dock;
|
state = State.Dock;
|
||||||
return Util.SequenceActivities(new Turn(angle), this);
|
return Util.SequenceActivities(new Turn(self, angle), this);
|
||||||
case State.Dock:
|
case State.Dock:
|
||||||
ru.PlayCustomAnimation(self, "dock", () => {
|
ru.PlayCustomAnimation(self, "dock", () => {
|
||||||
ru.PlayCustomAnimRepeating(self, "dock-loop");
|
ru.PlayCustomAnimRepeating(self, "dock-loop");
|
||||||
|
|||||||
@@ -8,22 +8,30 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Activities
|
namespace OpenRA.Mods.RA.Activities
|
||||||
{
|
{
|
||||||
public class Turn : Activity
|
public class Turn : Activity
|
||||||
{
|
{
|
||||||
int desiredFacing;
|
readonly IEnumerable<IDisableMove> moveDisablers;
|
||||||
|
readonly int desiredFacing;
|
||||||
|
|
||||||
public Turn( int desiredFacing )
|
public Turn(Actor self, int desiredFacing)
|
||||||
{
|
{
|
||||||
|
moveDisablers = self.TraitsImplementing<IDisableMove>();
|
||||||
this.desiredFacing = desiredFacing;
|
this.desiredFacing = desiredFacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Activity Tick( Actor self )
|
public override Activity Tick( Actor self )
|
||||||
{
|
{
|
||||||
if (IsCanceled) return NextActivity;
|
if (IsCanceled)
|
||||||
|
return NextActivity;
|
||||||
|
if (moveDisablers.Any(d => d.MoveDisabled(self)))
|
||||||
|
return this;
|
||||||
|
|
||||||
var facing = self.Trait<IFacing>();
|
var facing = self.Trait<IFacing>();
|
||||||
|
|
||||||
if( desiredFacing == facing.Facing )
|
if( desiredFacing == facing.Facing )
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
|
cargo.Unloading = false;
|
||||||
if (IsCanceled || cargo.IsEmpty(self))
|
if (IsCanceled || cargo.IsEmpty(self))
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
@@ -93,6 +94,7 @@ namespace OpenRA.Mods.RA.Activities
|
|||||||
if (!unloadAll || cargo.IsEmpty(self))
|
if (!unloadAll || cargo.IsEmpty(self))
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
|
|
||||||
|
cargo.Unloading = true;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
if (afld == null)
|
if (afld == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var res = afld.Trait<Reservable>();
|
var res = afld.TraitOrDefault<Reservable>();
|
||||||
|
|
||||||
if (res != null)
|
if (res != null)
|
||||||
{
|
{
|
||||||
@@ -204,6 +204,18 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { return SubCell.Invalid; } // Does not use any subcell
|
public SubCell GetAvailableSubCell(CPos a, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, bool checkTransientActors = true) { return SubCell.Invalid; } // Does not use any subcell
|
||||||
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { return true; }
|
public bool CanEnterCell(CPos cell, Actor ignoreActor = null, bool checkTransientActors = true) { return true; }
|
||||||
|
|
||||||
|
public bool CanEnterTargetNow(Actor self, Target target)
|
||||||
|
{
|
||||||
|
if (target.Positions.Any(p => self.World.ActorMap.GetUnitsAt(self.World.Map.CellContaining(p)).Any(a => a != self && a != target.Actor)))
|
||||||
|
return false;
|
||||||
|
var res = target.Actor.TraitOrDefault<Reservable>();
|
||||||
|
if (res == null)
|
||||||
|
return true;
|
||||||
|
UnReserve();
|
||||||
|
Reservation = res.Reserve(target.Actor, self, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public int MovementSpeed
|
public int MovementSpeed
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
.ClosestTo(self);
|
.ClosestTo(self);
|
||||||
|
|
||||||
if (nearestHpad == null)
|
if (nearestHpad == null)
|
||||||
return Util.SequenceActivities(new Turn(initialFacing), new HeliLand(true), NextActivity);
|
return Util.SequenceActivities(new Turn(self, initialFacing), new HeliLand(true), NextActivity);
|
||||||
else
|
else
|
||||||
return Util.SequenceActivities(new HeliFly(self, Target.FromActor(nearestHpad)));
|
return Util.SequenceActivities(new HeliFly(self, Target.FromActor(nearestHpad)));
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
|
|
||||||
return Util.SequenceActivities(
|
return Util.SequenceActivities(
|
||||||
new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)),
|
new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)),
|
||||||
new Turn(initialFacing),
|
new Turn(self, initialFacing),
|
||||||
new HeliLand(false),
|
new HeliLand(false),
|
||||||
new ResupplyAircraft());
|
new ResupplyAircraft());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -70,7 +71,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
if (Info.LandWhenIdle)
|
if (Info.LandWhenIdle)
|
||||||
{
|
{
|
||||||
if (Info.TurnToLand)
|
if (Info.TurnToLand)
|
||||||
self.QueueActivity(new Turn(Info.InitialFacing));
|
self.QueueActivity(new Turn(self, Info.InitialFacing));
|
||||||
|
|
||||||
self.QueueActivity(new HeliLand(true));
|
self.QueueActivity(new HeliLand(true));
|
||||||
}
|
}
|
||||||
@@ -96,7 +97,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new HeliFly(self, Target.FromPos(order.TargetActor.CenterPosition + offset)));
|
self.QueueActivity(new HeliFly(self, Target.FromPos(order.TargetActor.CenterPosition + offset)));
|
||||||
self.QueueActivity(new Turn(Info.InitialFacing));
|
self.QueueActivity(new Turn(self, Info.InitialFacing));
|
||||||
self.QueueActivity(new HeliLand(false));
|
self.QueueActivity(new HeliLand(false));
|
||||||
self.QueueActivity(new ResupplyAircraft());
|
self.QueueActivity(new ResupplyAircraft());
|
||||||
self.QueueActivity(new TakeOff());
|
self.QueueActivity(new TakeOff());
|
||||||
@@ -116,7 +117,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
if (Info.LandWhenIdle)
|
if (Info.LandWhenIdle)
|
||||||
{
|
{
|
||||||
if (Info.TurnToLand)
|
if (Info.TurnToLand)
|
||||||
self.QueueActivity(new Turn(Info.InitialFacing));
|
self.QueueActivity(new Turn(self, Info.InitialFacing));
|
||||||
|
|
||||||
self.QueueActivity(new HeliLand(true));
|
self.QueueActivity(new HeliLand(true));
|
||||||
}
|
}
|
||||||
@@ -153,6 +154,12 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
return new HeliFly(self, Target.FromCell(self.World, cell, subCell));
|
return new HeliFly(self, Target.FromCell(self.World, cell, subCell));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Activity MoveIntoTarget(Actor self, Target target) { return new HeliLand(false); }
|
||||||
|
public Activity MoveToTarget(Actor self, Target target)
|
||||||
|
{
|
||||||
|
return Util.SequenceActivities(new HeliFly(self, target), new Turn(self, Info.InitialFacing));
|
||||||
|
}
|
||||||
|
|
||||||
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos)
|
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos)
|
||||||
{
|
{
|
||||||
// TODO: Ignore repulsion when moving
|
// TODO: Ignore repulsion when moving
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using OpenRA.Mods.RA.Activities;
|
using OpenRA.Mods.RA.Activities;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
@@ -129,5 +130,7 @@ namespace OpenRA.Mods.RA.Air
|
|||||||
|
|
||||||
public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return new Fly(self, Target.FromCell(self.World, cell)); }
|
public Activity MoveIntoWorld(Actor self, CPos cell, SubCell subCell = SubCell.Any) { return new Fly(self, Target.FromCell(self.World, cell)); }
|
||||||
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos) { return Util.SequenceActivities(new CallFunc(() => SetVisualPosition(self, fromPos)), new Fly(self, Target.FromPos(toPos))); }
|
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos) { return Util.SequenceActivities(new CallFunc(() => SetVisualPosition(self, fromPos)), new Fly(self, Target.FromPos(toPos))); }
|
||||||
|
public Activity MoveToTarget(Actor self, Target target) { return new Fly(self, target, WRange.FromCells(3), WRange.FromCells(5)); }
|
||||||
|
public Activity MoveIntoTarget(Actor self, Target target) { return new Land(target); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Red);
|
self.SetTargetLine(target, Color.Red);
|
||||||
self.QueueActivity(new Enter(target.Actor, new Demolish(
|
self.QueueActivity(new Enter(self, target.Actor, new Demolish(
|
||||||
target.Actor, info.C4Delay, info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration)));
|
target.Actor, info.C4Delay, info.Flashes, info.FlashesDelay, info.FlashInterval, info.FlashDuration)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Red);
|
self.SetTargetLine(target, Color.Red);
|
||||||
self.QueueActivity(new Enter(target.Actor, new CaptureActor(target)));
|
self.QueueActivity(new Enter(self, target.Actor, new CaptureActor(target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class CaptureOrderTargeter : UnitOrderTargeter
|
class CaptureOrderTargeter : UnitOrderTargeter
|
||||||
|
|||||||
@@ -29,13 +29,16 @@ namespace OpenRA.Mods.RA
|
|||||||
public object Create(ActorInitializer init) { return new Cargo(init, this); }
|
public object Create(ActorInitializer init) { return new Cargo(init, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Cargo : IPips, IIssueOrder, IResolveOrder, IOrderVoice, INotifyKilled, INotifyCapture, ITick, INotifySold
|
public class Cargo : IPips, IIssueOrder, IResolveOrder, IOrderVoice, INotifyKilled, INotifyCapture, ITick, INotifySold, IDisableMove
|
||||||
{
|
{
|
||||||
readonly Actor self;
|
|
||||||
public readonly CargoInfo Info;
|
public readonly CargoInfo Info;
|
||||||
|
readonly Actor self;
|
||||||
|
|
||||||
|
public bool Unloading { get; internal set; }
|
||||||
int totalWeight = 0;
|
int totalWeight = 0;
|
||||||
|
int reservedWeight = 0;
|
||||||
List<Actor> cargo = new List<Actor>();
|
List<Actor> cargo = new List<Actor>();
|
||||||
|
HashSet<Actor> reserves = new HashSet<Actor>();
|
||||||
public IEnumerable<Actor> Passengers { get { return cargo; } }
|
public IEnumerable<Actor> Passengers { get { return cargo; } }
|
||||||
|
|
||||||
CPos currentCell;
|
CPos currentCell;
|
||||||
@@ -45,6 +48,7 @@ namespace OpenRA.Mods.RA
|
|||||||
{
|
{
|
||||||
self = init.self;
|
self = init.self;
|
||||||
Info = info;
|
Info = info;
|
||||||
|
Unloading = false;
|
||||||
|
|
||||||
if (init.Contains<RuntimeCargoInit>())
|
if (init.Contains<RuntimeCargoInit>())
|
||||||
{
|
{
|
||||||
@@ -97,6 +101,7 @@ namespace OpenRA.Mods.RA
|
|||||||
if (!CanUnload())
|
if (!CanUnload())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Unloading = true;
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new UnloadCargo(self, true));
|
self.QueueActivity(new UnloadCargo(self, true));
|
||||||
}
|
}
|
||||||
@@ -115,7 +120,27 @@ namespace OpenRA.Mods.RA
|
|||||||
|
|
||||||
public bool CanLoad(Actor self, Actor a)
|
public bool CanLoad(Actor self, Actor a)
|
||||||
{
|
{
|
||||||
return HasSpace(GetWeight(a)) && self.CenterPosition.Z == 0;
|
return (reserves.Contains(a) || HasSpace(GetWeight(a))) && self.CenterPosition.Z == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool ReserveSpace(Actor a)
|
||||||
|
{
|
||||||
|
if (reserves.Contains(a))
|
||||||
|
return true;
|
||||||
|
var w = GetWeight(a);
|
||||||
|
if (!HasSpace(w))
|
||||||
|
return false;
|
||||||
|
reserves.Add(a);
|
||||||
|
reservedWeight += w;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void UnreserveSpace(Actor a)
|
||||||
|
{
|
||||||
|
if (!reserves.Contains(a))
|
||||||
|
return;
|
||||||
|
reservedWeight -= GetWeight(a);
|
||||||
|
reserves.Remove(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CursorForOrder(Actor self, Order order)
|
public string CursorForOrder(Actor self, Order order)
|
||||||
@@ -130,7 +155,8 @@ namespace OpenRA.Mods.RA
|
|||||||
return self.HasVoice("Unload") ? "Unload" : "Move";
|
return self.HasVoice("Unload") ? "Unload" : "Move";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasSpace(int weight) { return totalWeight + weight <= Info.MaxWeight; }
|
public bool MoveDisabled(Actor self) { return reserves.Any(); }
|
||||||
|
public bool HasSpace(int weight) { return totalWeight + reservedWeight + weight <= Info.MaxWeight; }
|
||||||
public bool IsEmpty(Actor self) { return cargo.Count == 0; }
|
public bool IsEmpty(Actor self) { return cargo.Count == 0; }
|
||||||
|
|
||||||
public Actor Peek(Actor self) { return cargo[0]; }
|
public Actor Peek(Actor self) { return cargo[0]; }
|
||||||
@@ -177,7 +203,13 @@ namespace OpenRA.Mods.RA
|
|||||||
public void Load(Actor self, Actor a)
|
public void Load(Actor self, Actor a)
|
||||||
{
|
{
|
||||||
cargo.Add(a);
|
cargo.Add(a);
|
||||||
totalWeight += GetWeight(a);
|
var w = GetWeight(a);
|
||||||
|
totalWeight += w;
|
||||||
|
if (reserves.Contains(a))
|
||||||
|
{
|
||||||
|
reservedWeight -= w;
|
||||||
|
reserves.Remove(a);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var npe in self.TraitsImplementing<INotifyPassengerEntered>())
|
foreach (var npe in self.TraitsImplementing<INotifyPassengerEntered>())
|
||||||
npe.PassengerEntered(self, a);
|
npe.PassengerEntered(self, a);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Yellow);
|
self.SetTargetLine(target, Color.Yellow);
|
||||||
self.QueueActivity(new Enter(target.Actor, new RepairBuilding(target.Actor)));
|
self.QueueActivity(new Enter(self, target.Actor, new RepairBuilding(target.Actor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class EngineerRepairOrderTargeter : UnitOrderTargeter
|
class EngineerRepairOrderTargeter : UnitOrderTargeter
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA
|
|||||||
var finalPos = init.world.Map.CenterOfCell(TopLeft);
|
var finalPos = init.world.Map.CenterOfCell(TopLeft);
|
||||||
var distance = (finalPos - CenterPosition).Length;
|
var distance = (finalPos - CenterPosition).Length;
|
||||||
if (speed > 0 && distance > 0)
|
if (speed > 0 && distance > 0)
|
||||||
self.QueueActivity(new Drag(CenterPosition, finalPos, distance / speed));
|
self.QueueActivity(new Drag(init.self, CenterPosition, finalPos, distance / speed));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); }
|
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); }
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace OpenRA.Mods.RA.Infiltration
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Red);
|
self.SetTargetLine(target, Color.Red);
|
||||||
self.QueueActivity(new Enter(target.Actor, new Infiltrate(target.Actor)));
|
self.QueueActivity(new Enter(self, target.Actor, new Infiltrate(target.Actor)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,18 +9,25 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
namespace OpenRA.Mods.RA.Move
|
namespace OpenRA.Mods.RA.Move
|
||||||
{
|
{
|
||||||
public class Drag : Activity
|
public class Drag : Activity
|
||||||
{
|
{
|
||||||
|
readonly IPositionable positionable;
|
||||||
|
readonly IMove movement;
|
||||||
|
readonly IEnumerable<IDisableMove> moveDisablers;
|
||||||
WPos start, end;
|
WPos start, end;
|
||||||
int length;
|
int length;
|
||||||
int ticks = 0;
|
int ticks = 0;
|
||||||
|
|
||||||
public Drag(WPos start, WPos end, int length)
|
public Drag(Actor self, WPos start, WPos end, int length)
|
||||||
{
|
{
|
||||||
|
positionable = self.Trait<IPositionable>();
|
||||||
|
movement = self.TraitOrDefault<IMove>();
|
||||||
|
moveDisablers = self.TraitsImplementing<IDisableMove>();
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
@@ -28,8 +35,8 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
|
|
||||||
public override Activity Tick(Actor self)
|
public override Activity Tick(Actor self)
|
||||||
{
|
{
|
||||||
var positionable = self.Trait<IPositionable>();
|
if (moveDisablers.Any(d => d.MoveDisabled(self)))
|
||||||
var movement = self.TraitOrDefault<IMove>();
|
return this;
|
||||||
|
|
||||||
var pos = length > 1
|
var pos = length > 1
|
||||||
? WPos.Lerp(start, end, ticks, length - 1)
|
? WPos.Lerp(start, end, ticks, length - 1)
|
||||||
|
|||||||
@@ -660,22 +660,42 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
SetPosition(self, cell, subCell);
|
SetPosition(self, cell, subCell);
|
||||||
SetVisualPosition(self, pos);
|
SetVisualPosition(self, pos);
|
||||||
|
|
||||||
// Animate transition
|
return VisualMove(self, pos, self.World.Map.CenterOfSubCell(cell, subCell), cell);
|
||||||
var to = self.World.Map.CenterOfSubCell(cell, subCell);
|
}
|
||||||
var speed = MovementSpeedForCell(self, cell);
|
|
||||||
var length = speed > 0 ? (to - pos).Length / speed : 0;
|
|
||||||
|
|
||||||
var facing = Util.GetFacing(to - pos, Facing);
|
public Activity MoveToTarget(Actor self, Target target)
|
||||||
return Util.SequenceActivities(new Turn(facing), new Drag(pos, to, length));
|
{
|
||||||
|
if (target.Type == TargetType.Invalid)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new MoveAdjacentTo(self, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity MoveIntoTarget(Actor self, Target target)
|
||||||
|
{
|
||||||
|
if (target.Type == TargetType.Invalid)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return VisualMove(self, self.CenterPosition, target.CenterPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanEnterTargetNow(Actor self, Target target)
|
||||||
|
{
|
||||||
|
return self.Location == self.World.Map.CellContaining(target.CenterPosition) || Util.AdjacentCells(self.World, target).Any(c => c == self.Location);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos)
|
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos)
|
||||||
{
|
{
|
||||||
var speed = MovementSpeedForCell(self, self.Location);
|
return VisualMove(self, fromPos, toPos, self.Location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos, CPos cell)
|
||||||
|
{
|
||||||
|
var speed = MovementSpeedForCell(self, cell);
|
||||||
var length = speed > 0 ? (toPos - fromPos).Length / speed : 0;
|
var length = speed > 0 ? (toPos - fromPos).Length / speed : 0;
|
||||||
|
|
||||||
var facing = Util.GetFacing(toPos - fromPos, Facing);
|
var facing = Util.GetFacing(toPos - fromPos, Facing);
|
||||||
return Util.SequenceActivities(new Turn(facing), new Drag(fromPos, toPos, length));
|
return Util.SequenceActivities(new Turn(self, facing), new Drag(self, fromPos, toPos, length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ namespace OpenRA.Mods.RA.Move
|
|||||||
if (firstFacing != mobile.Facing)
|
if (firstFacing != mobile.Facing)
|
||||||
{
|
{
|
||||||
path.Add(nextCell.Value.First);
|
path.Add(nextCell.Value.First);
|
||||||
return Util.SequenceActivities(new Turn(firstFacing), this);
|
return Util.SequenceActivities(new Turn(self, firstFacing), this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -17,34 +18,113 @@ using OpenRA.Traits;
|
|||||||
|
|
||||||
namespace OpenRA.Mods.RA
|
namespace OpenRA.Mods.RA
|
||||||
{
|
{
|
||||||
|
public enum AlternateTransportsMode { None, Force, Default, Always }
|
||||||
|
|
||||||
|
public class EnterTransportTargeter : EnterAlliedActorTargeter<Cargo>
|
||||||
|
{
|
||||||
|
readonly AlternateTransportsMode mode;
|
||||||
|
|
||||||
|
public EnterTransportTargeter(string order, int priority,
|
||||||
|
Func<Actor, bool> canTarget, Func<Actor, bool> useEnterCursor,
|
||||||
|
AlternateTransportsMode mode)
|
||||||
|
: base (order, priority, canTarget, useEnterCursor) { this.mode = mode; }
|
||||||
|
|
||||||
|
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AlternateTransportsMode.None:
|
||||||
|
break;
|
||||||
|
case AlternateTransportsMode.Force:
|
||||||
|
if (modifiers.HasModifier(TargetModifiers.ForceMove))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case AlternateTransportsMode.Default:
|
||||||
|
if (!modifiers.HasModifier(TargetModifiers.ForceMove))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case AlternateTransportsMode.Always:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CanTargetActor(self, target, modifiers, ref cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EnterTransportsTargeter : EnterAlliedActorTargeter<Cargo>
|
||||||
|
{
|
||||||
|
readonly AlternateTransportsMode mode;
|
||||||
|
|
||||||
|
public EnterTransportsTargeter(string order, int priority,
|
||||||
|
Func<Actor, bool> canTarget, Func<Actor, bool> useEnterCursor,
|
||||||
|
AlternateTransportsMode mode)
|
||||||
|
: base (order, priority, canTarget, useEnterCursor) { this.mode = mode; }
|
||||||
|
|
||||||
|
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case AlternateTransportsMode.None:
|
||||||
|
return false;
|
||||||
|
case AlternateTransportsMode.Force:
|
||||||
|
if (!modifiers.HasModifier(TargetModifiers.ForceMove))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case AlternateTransportsMode.Default:
|
||||||
|
if (modifiers.HasModifier(TargetModifiers.ForceMove))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
case AlternateTransportsMode.Always:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return base.CanTargetActor(self, target, modifiers, ref cursor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Desc("This actor can enter Cargo actors.")]
|
[Desc("This actor can enter Cargo actors.")]
|
||||||
public class PassengerInfo : ITraitInfo
|
public class PassengerInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
public readonly string CargoType = null;
|
public readonly string CargoType = null;
|
||||||
public readonly PipType PipType = PipType.Green;
|
public readonly PipType PipType = PipType.Green;
|
||||||
public int Weight = 1;
|
public readonly int Weight = 1;
|
||||||
|
|
||||||
|
[Desc("Use to set when to use alternate transports (Never, Force, Default, Always).",
|
||||||
|
"Force - use force move modifier (Alt) to enable.",
|
||||||
|
"Default - use force move modifier (Alt) to disable.")]
|
||||||
|
public readonly AlternateTransportsMode AlternateTransportsMode = AlternateTransportsMode.Force;
|
||||||
|
|
||||||
|
[Desc("Number of retries using alternate transports.")]
|
||||||
|
public readonly int MaxAlternateTransportAttempts = 1;
|
||||||
|
|
||||||
|
[Desc("Range from self for looking for an alternate transport (default: 5.5 cells).")]
|
||||||
|
public readonly WRange AlternateTransportScanRange = WRange.FromCells(11) / 2;
|
||||||
|
|
||||||
public object Create(ActorInitializer init) { return new Passenger(this); }
|
public object Create(ActorInitializer init) { return new Passenger(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Passenger : IIssueOrder, IResolveOrder, IOrderVoice
|
public class Passenger : IIssueOrder, IResolveOrder, IOrderVoice, INotifyRemovedFromWorld
|
||||||
{
|
{
|
||||||
public readonly PassengerInfo info;
|
public readonly PassengerInfo Info;
|
||||||
public Passenger(PassengerInfo info) { this.info = info; }
|
public Passenger(PassengerInfo info) { Info = info; }
|
||||||
public Actor Transport;
|
public Actor Transport;
|
||||||
|
public Cargo ReservedCargo { get; private set; }
|
||||||
|
|
||||||
public IEnumerable<IOrderTargeter> Orders
|
public IEnumerable<IOrderTargeter> Orders
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
yield return new EnterAlliedActorTargeter<Cargo>("EnterTransport", 6,
|
yield return new EnterTransportTargeter("EnterTransport", 6,
|
||||||
target => IsCorrectCargoType(target), target => CanEnter(target));
|
target => IsCorrectCargoType(target), target => CanEnter(target),
|
||||||
|
Info.AlternateTransportsMode);
|
||||||
|
yield return new EnterTransportsTargeter("EnterTransports", 6,
|
||||||
|
target => IsCorrectCargoType(target), target => CanEnter(target),
|
||||||
|
Info.AlternateTransportsMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
|
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
|
||||||
{
|
{
|
||||||
if (order.OrderID == "EnterTransport")
|
if (order.OrderID == "EnterTransport" || order.OrderID == "EnterTransports")
|
||||||
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
|
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -53,25 +133,29 @@ namespace OpenRA.Mods.RA
|
|||||||
bool IsCorrectCargoType(Actor target)
|
bool IsCorrectCargoType(Actor target)
|
||||||
{
|
{
|
||||||
var ci = target.Info.Traits.Get<CargoInfo>();
|
var ci = target.Info.Traits.Get<CargoInfo>();
|
||||||
return ci.Types.Contains(info.CargoType);
|
return ci.Types.Contains(Info.CargoType);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanEnter(Cargo cargo)
|
||||||
|
{
|
||||||
|
return cargo != null && cargo.HasSpace(Info.Weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanEnter(Actor target)
|
bool CanEnter(Actor target)
|
||||||
{
|
{
|
||||||
var cargo = target.TraitOrDefault<Cargo>();
|
return CanEnter(target.TraitOrDefault<Cargo>());
|
||||||
return cargo != null && cargo.HasSpace(info.Weight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string VoicePhraseForOrder(Actor self, Order order)
|
public string VoicePhraseForOrder(Actor self, Order order)
|
||||||
{
|
{
|
||||||
if (order.OrderString != "EnterTransport" ||
|
if ((order.OrderString != "EnterTransport" && order.OrderString != "EnterTransports") ||
|
||||||
!CanEnter(order.TargetActor)) return null;
|
!CanEnter(order.TargetActor)) return null;
|
||||||
return "Move";
|
return "Move";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResolveOrder(Actor self, Order order)
|
public void ResolveOrder(Actor self, Order order)
|
||||||
{
|
{
|
||||||
if (order.OrderString == "EnterTransport")
|
if (order.OrderString == "EnterTransport" || order.OrderString == "EnterTransports")
|
||||||
{
|
{
|
||||||
if (order.TargetActor == null) return;
|
if (order.TargetActor == null) return;
|
||||||
if (!CanEnter(order.TargetActor)) return;
|
if (!CanEnter(order.TargetActor)) return;
|
||||||
@@ -81,9 +165,26 @@ namespace OpenRA.Mods.RA
|
|||||||
self.SetTargetLine(target, Color.Green);
|
self.SetTargetLine(target, Color.Green);
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new MoveAdjacentTo(self, target));
|
self.QueueActivity(new EnterTransport(self, order.TargetActor, order.OrderString == "EnterTransport" ? 0 : Info.MaxAlternateTransportAttempts));
|
||||||
self.QueueActivity(new EnterTransport(self, order.TargetActor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Reserve(Actor self, Cargo cargo)
|
||||||
|
{
|
||||||
|
Unreserve(self);
|
||||||
|
if (!cargo.ReserveSpace(self))
|
||||||
|
return false;
|
||||||
|
ReservedCargo = cargo;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemovedFromWorld(Actor self) { Unreserve(self); }
|
||||||
|
public void Unreserve(Actor self)
|
||||||
|
{
|
||||||
|
if (ReservedCargo == null)
|
||||||
|
return;
|
||||||
|
ReservedCargo.UnreserveSpace(self);
|
||||||
|
ReservedCargo = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow);
|
self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow);
|
||||||
|
|
||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
self.QueueActivity(new Enter(order.TargetActor, new RepairBridge(order.TargetActor)));
|
self.QueueActivity(new Enter(self, order.TargetActor, new RepairBridge(order.TargetActor)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ namespace OpenRA.Mods.RA.Scripting
|
|||||||
var heli = transport.TraitOrDefault<Helicopter>();
|
var heli = transport.TraitOrDefault<Helicopter>();
|
||||||
if (heli != null)
|
if (heli != null)
|
||||||
{
|
{
|
||||||
transport.QueueActivity(new Turn(heli.Info.InitialFacing));
|
transport.QueueActivity(new Turn(transport, heli.Info.InitialFacing));
|
||||||
transport.QueueActivity(new HeliLand(true));
|
transport.QueueActivity(new HeliLand(true));
|
||||||
transport.QueueActivity(new Wait(15));
|
transport.QueueActivity(new Wait(15));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
self.SetTargetLine(target, Color.Yellow);
|
self.SetTargetLine(target, Color.Yellow);
|
||||||
self.QueueActivity(new Enter(target.Actor, new DonateSupplies(target.Actor, info.Payload)));
|
self.QueueActivity(new Enter(self, target.Actor, new DonateSupplies(target.Actor, info.Payload)));
|
||||||
}
|
}
|
||||||
|
|
||||||
class SupplyTruckOrderTargeter : UnitOrderTargeter
|
class SupplyTruckOrderTargeter : UnitOrderTargeter
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace OpenRA.Mods.RA
|
|||||||
self.CancelActivity();
|
self.CancelActivity();
|
||||||
|
|
||||||
if (self.HasTrait<IFacing>())
|
if (self.HasTrait<IFacing>())
|
||||||
self.QueueActivity(new Turn(info.Facing));
|
self.QueueActivity(new Turn(self, info.Facing));
|
||||||
|
|
||||||
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
|
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
|
||||||
nt.BeforeTransform(self);
|
nt.BeforeTransform(self);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace OpenRA.Mods.TS
|
|||||||
{
|
{
|
||||||
case State.Turn:
|
case State.Turn:
|
||||||
state = State.Dock;
|
state = State.Dock;
|
||||||
return Util.SequenceActivities(new Turn(160), this);
|
return Util.SequenceActivities(new Turn(self, 160), this);
|
||||||
case State.Dock:
|
case State.Dock:
|
||||||
if (proc.IsInWorld && !proc.IsDead())
|
if (proc.IsInWorld && !proc.IsDead())
|
||||||
foreach (var nd in proc.TraitsImplementing<INotifyDocking>())
|
foreach (var nd in proc.TraitsImplementing<INotifyDocking>())
|
||||||
|
|||||||
Reference in New Issue
Block a user