Merge pull request #6528 from atlimit8/DragIntoTransports

Modifies IMove & Enter and Fixes #4640 - Units teleport into transports
This commit is contained in:
Matthias Mailänder
2014-10-03 15:37:29 +02:00
29 changed files with 531 additions and 94 deletions

View File

@@ -101,6 +101,7 @@ namespace OpenRA.Traits
public interface INotifyCapture { void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner); }
public interface INotifyHarvest { void Harvested(Actor self, ResourceType resource); }
public interface INotifyInfiltrated { void Infiltrated(Actor self, Actor infiltrator); }
public interface IDisableMove { bool MoveDisabled(Actor self); }
public interface IUpgradable
{
@@ -206,9 +207,12 @@ namespace OpenRA.Traits
Activity MoveWithinRange(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 MoveToTarget(Actor self, Target target);
Activity MoveIntoTarget(Actor self, Target target);
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
CPos NearestMoveableCell(CPos target);
bool IsMoving { get; set; }
bool CanEnterTargetNow(Actor self, Target target);
}
public interface INotifyBlockingMove { void OnNotifyBlockingMove(Actor self, Actor blocking); }

View File

@@ -47,10 +47,10 @@ namespace OpenRA.Mods.Cnc
return this;
case State.Turn:
state = State.DragIn;
return Util.SequenceActivities(new Turn(112), this);
return Util.SequenceActivities(new Turn(self, 112), this);
case State.DragIn:
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:
ru.PlayCustomAnimation(self, "dock", () => {
ru.PlayCustomAnimRepeating(self, "dock-loop");
@@ -73,7 +73,7 @@ namespace OpenRA.Mods.Cnc
state = State.Wait;
return this;
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");

View File

@@ -62,7 +62,7 @@ namespace OpenRA.Mods.Cnc
cargoFacing.Facing = facing.Facing;
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));
foreach (var cr in c.Render(wr))

View File

@@ -64,7 +64,7 @@ namespace OpenRA.Mods.RA.Activities
var desiredFacing = Util.GetFacing(Target.CenterPosition - self.CenterPosition, 0);
if (facing.Facing != desiredFacing)
return Util.SequenceActivities(new Turn(desiredFacing), this);
return Util.SequenceActivities(new Turn(self, desiredFacing), this);
attack.DoAttack(self, Target);

View File

@@ -8,41 +8,267 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Activities
{
public class Enter : Activity
{
readonly Target target;
readonly Activity inner;
public enum ReserveStatus { None, TooFar, Pending, Ready }
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.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)
{
if (IsCanceled || !target.IsValidFor(self))
return NextActivity;
if (IsCanceled)
return CanceledTick(self);
if (target.Type != TargetType.Actor)
return NextActivity;
// Check target validity if not exiting or done
if (nextState != State.Done && (target.Type != TargetType.Actor || !target.IsValidFor(self)))
AbortOrExit(self);
if (!Util.AdjacentCells(self.World, target).Any(c => c == self.Location))
return Util.SequenceActivities(new MoveAdjacentTo(self, target), this);
// If no current activity, tick next activity
if (inner == null && FindAndTransitionToNextState(self) == State.Done)
return CanceledTick(self);
// Move to the middle of the target, ignoring impassable tiles
var move = self.Trait<IMove>();
return Util.SequenceActivities(
move.VisualMove(self, self.CenterPosition, target.CenterPosition),
inner,
move.VisualMove(self, target.CenterPosition, self.CenterPosition),
NextActivity
);
// Run inner activity/InsideTick
inner = inner == this ? InsideTick(self) : Util.RunActivity(self, inner);
return this;
}
}
}

View File

@@ -8,39 +8,42 @@
*/
#endregion
using System;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Activities
{
class EnterTransport : Activity
class EnterTransport : Enter
{
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.maxTries = maxTries;
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)
return NextActivity;
if (transport == null || !transport.IsInWorld)
return NextActivity;
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;
var status = base.Reserve(self);
if (status != ReserveStatus.Ready)
return status;
if (passenger.Reserve(self, cargo))
return ReserveStatus.Ready;
return ReserveStatus.Pending;
}
protected override void OnInside(Actor self)
{
self.World.AddFrameEndTask(w =>
{
if (self.IsDead() || transport.IsDead() || !cargo.CanLoad(transport, self))
@@ -50,7 +53,19 @@ namespace OpenRA.Mods.RA.Activities
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
}
}
}

View File

@@ -153,7 +153,7 @@ namespace OpenRA.Mods.RA.Activities
var facing = self.Trait<IFacing>().Facing;
var desired = Util.QuantizeFacing(facing, harvInfo.HarvestFacings) * (256 / harvInfo.HarvestFacings);
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>();

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA
return this;
case State.Turn:
state = State.Dock;
return Util.SequenceActivities(new Turn(angle), this);
return Util.SequenceActivities(new Turn(self, angle), this);
case State.Dock:
ru.PlayCustomAnimation(self, "dock", () => {
ru.PlayCustomAnimRepeating(self, "dock-loop");

View File

@@ -8,22 +8,30 @@
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Activities
{
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;
}
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>();
if( desiredFacing == facing.Facing )

View File

@@ -54,6 +54,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
cargo.Unloading = false;
if (IsCanceled || cargo.IsEmpty(self))
return NextActivity;
@@ -93,6 +94,7 @@ namespace OpenRA.Mods.RA.Activities
if (!unloadAll || cargo.IsEmpty(self))
return NextActivity;
cargo.Unloading = true;
return this;
}
}

View File

@@ -135,7 +135,7 @@ namespace OpenRA.Mods.RA.Air
if (afld == null)
return;
var res = afld.Trait<Reservable>();
var res = afld.TraitOrDefault<Reservable>();
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 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
{
get

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.RA.Air
.ClosestTo(self);
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
return Util.SequenceActivities(new HeliFly(self, Target.FromActor(nearestHpad)));
}
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA.Air
return Util.SequenceActivities(
new HeliFly(self, Target.FromPos(dest.CenterPosition + offset)),
new Turn(initialFacing),
new Turn(self, initialFacing),
new HeliLand(false),
new ResupplyAircraft());
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
@@ -70,7 +71,7 @@ namespace OpenRA.Mods.RA.Air
if (Info.LandWhenIdle)
{
if (Info.TurnToLand)
self.QueueActivity(new Turn(Info.InitialFacing));
self.QueueActivity(new Turn(self, Info.InitialFacing));
self.QueueActivity(new HeliLand(true));
}
@@ -96,7 +97,7 @@ namespace OpenRA.Mods.RA.Air
self.CancelActivity();
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 ResupplyAircraft());
self.QueueActivity(new TakeOff());
@@ -116,7 +117,7 @@ namespace OpenRA.Mods.RA.Air
if (Info.LandWhenIdle)
{
if (Info.TurnToLand)
self.QueueActivity(new Turn(Info.InitialFacing));
self.QueueActivity(new Turn(self, Info.InitialFacing));
self.QueueActivity(new HeliLand(true));
}
@@ -153,6 +154,12 @@ namespace OpenRA.Mods.RA.Air
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)
{
// TODO: Ignore repulsion when moving

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using System.Drawing;
using OpenRA.Mods.RA.Activities;
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 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); }
}
}

View File

@@ -76,7 +76,7 @@ namespace OpenRA.Mods.RA
self.CancelActivity();
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)));
}

View File

@@ -75,7 +75,7 @@ namespace OpenRA.Mods.RA
self.CancelActivity();
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

View File

@@ -29,13 +29,16 @@ namespace OpenRA.Mods.RA
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;
readonly Actor self;
public bool Unloading { get; internal set; }
int totalWeight = 0;
int reservedWeight = 0;
List<Actor> cargo = new List<Actor>();
HashSet<Actor> reserves = new HashSet<Actor>();
public IEnumerable<Actor> Passengers { get { return cargo; } }
CPos currentCell;
@@ -45,6 +48,7 @@ namespace OpenRA.Mods.RA
{
self = init.self;
Info = info;
Unloading = false;
if (init.Contains<RuntimeCargoInit>())
{
@@ -97,6 +101,7 @@ namespace OpenRA.Mods.RA
if (!CanUnload())
return;
Unloading = true;
self.CancelActivity();
self.QueueActivity(new UnloadCargo(self, true));
}
@@ -115,7 +120,27 @@ namespace OpenRA.Mods.RA
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)
@@ -130,7 +155,8 @@ namespace OpenRA.Mods.RA
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 Actor Peek(Actor self) { return cargo[0]; }
@@ -177,7 +203,13 @@ namespace OpenRA.Mods.RA
public void Load(Actor self, Actor 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>())
npe.PassengerEntered(self, a);

View File

@@ -78,7 +78,7 @@ namespace OpenRA.Mods.RA
self.CancelActivity();
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

View File

@@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA
var finalPos = init.world.Map.CenterOfCell(TopLeft);
var distance = (finalPos - CenterPosition).Length;
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); }

View File

@@ -96,7 +96,7 @@ namespace OpenRA.Mods.RA.Infiltration
self.CancelActivity();
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)));
}
}
}

View File

@@ -9,18 +9,25 @@
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Move
{
public class Drag : Activity
{
readonly IPositionable positionable;
readonly IMove movement;
readonly IEnumerable<IDisableMove> moveDisablers;
WPos start, end;
int length;
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.end = end;
this.length = length;
@@ -28,8 +35,8 @@ namespace OpenRA.Mods.RA.Move
public override Activity Tick(Actor self)
{
var positionable = self.Trait<IPositionable>();
var movement = self.TraitOrDefault<IMove>();
if (moveDisablers.Any(d => d.MoveDisabled(self)))
return this;
var pos = length > 1
? WPos.Lerp(start, end, ticks, length - 1)

View File

@@ -660,22 +660,42 @@ namespace OpenRA.Mods.RA.Move
SetPosition(self, cell, subCell);
SetVisualPosition(self, pos);
// Animate transition
var to = self.World.Map.CenterOfSubCell(cell, subCell);
var speed = MovementSpeedForCell(self, cell);
var length = speed > 0 ? (to - pos).Length / speed : 0;
return VisualMove(self, pos, self.World.Map.CenterOfSubCell(cell, subCell), cell);
}
var facing = Util.GetFacing(to - pos, Facing);
return Util.SequenceActivities(new Turn(facing), new Drag(pos, to, length));
public Activity MoveToTarget(Actor self, Target target)
{
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)
{
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 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));
}
}
}

View File

@@ -151,7 +151,7 @@ namespace OpenRA.Mods.RA.Move
if (firstFacing != mobile.Facing)
{
path.Add(nextCell.Value.First);
return Util.SequenceActivities(new Turn(firstFacing), this);
return Util.SequenceActivities(new Turn(self, firstFacing), this);
}
else
{

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
@@ -17,34 +18,113 @@ using OpenRA.Traits;
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.")]
public class PassengerInfo : ITraitInfo
{
public readonly string CargoType = null;
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 class Passenger : IIssueOrder, IResolveOrder, IOrderVoice
public class Passenger : IIssueOrder, IResolveOrder, IOrderVoice, INotifyRemovedFromWorld
{
public readonly PassengerInfo info;
public Passenger(PassengerInfo info) { this.info = info; }
public readonly PassengerInfo Info;
public Passenger(PassengerInfo info) { Info = info; }
public Actor Transport;
public Cargo ReservedCargo { get; private set; }
public IEnumerable<IOrderTargeter> Orders
{
get
{
yield return new EnterAlliedActorTargeter<Cargo>("EnterTransport", 6,
target => IsCorrectCargoType(target), target => CanEnter(target));
yield return new EnterTransportTargeter("EnterTransport", 6,
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)
{
if (order.OrderID == "EnterTransport")
if (order.OrderID == "EnterTransport" || order.OrderID == "EnterTransports")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
return null;
@@ -53,25 +133,29 @@ namespace OpenRA.Mods.RA
bool IsCorrectCargoType(Actor target)
{
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)
{
var cargo = target.TraitOrDefault<Cargo>();
return cargo != null && cargo.HasSpace(info.Weight);
return CanEnter(target.TraitOrDefault<Cargo>());
}
public string VoicePhraseForOrder(Actor self, Order order)
{
if (order.OrderString != "EnterTransport" ||
if ((order.OrderString != "EnterTransport" && order.OrderString != "EnterTransports") ||
!CanEnter(order.TargetActor)) return null;
return "Move";
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "EnterTransport")
if (order.OrderString == "EnterTransport" || order.OrderString == "EnterTransports")
{
if (order.TargetActor == null) return;
if (!CanEnter(order.TargetActor)) return;
@@ -81,9 +165,26 @@ namespace OpenRA.Mods.RA
self.SetTargetLine(target, Color.Green);
self.CancelActivity();
self.QueueActivity(new MoveAdjacentTo(self, target));
self.QueueActivity(new EnterTransport(self, order.TargetActor));
self.QueueActivity(new EnterTransport(self, order.TargetActor, order.OrderString == "EnterTransport" ? 0 : Info.MaxAlternateTransportAttempts));
}
}
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;
}
}
}

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Mods.RA
self.SetTargetLine(Target.FromOrder(self.World, order), Color.Yellow);
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new RepairBridge(order.TargetActor)));
self.QueueActivity(new Enter(self, order.TargetActor, new RepairBridge(order.TargetActor)));
}
}

View File

@@ -177,7 +177,7 @@ namespace OpenRA.Mods.RA.Scripting
var heli = transport.TraitOrDefault<Helicopter>();
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 Wait(15));
}

View File

@@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA
self.CancelActivity();
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

View File

@@ -87,7 +87,7 @@ namespace OpenRA.Mods.RA
self.CancelActivity();
if (self.HasTrait<IFacing>())
self.QueueActivity(new Turn(info.Facing));
self.QueueActivity(new Turn(self, info.Facing));
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
nt.BeforeTransform(self);

View File

@@ -41,7 +41,7 @@ namespace OpenRA.Mods.TS
{
case State.Turn:
state = State.Dock;
return Util.SequenceActivities(new Turn(160), this);
return Util.SequenceActivities(new Turn(self, 160), this);
case State.Dock:
if (proc.IsInWorld && !proc.IsDead())
foreach (var nd in proc.TraitsImplementing<INotifyDocking>())