Merge pull request #4912 from pchote/exits

Clean up mobile world insertion.
This commit is contained in:
Matthias Mailänder
2014-03-20 11:58:46 +01:00
10 changed files with 73 additions and 63 deletions

View File

@@ -161,6 +161,8 @@ namespace OpenRA.Traits
Activity MoveTo(CPos cell, Actor ignoredActor); Activity MoveTo(CPos cell, Actor ignoredActor);
Activity MoveWithinRange(Target target, WRange range); Activity MoveWithinRange(Target target, WRange range);
Activity MoveFollow(Actor self, Target target, WRange range); Activity MoveFollow(Actor self, Target target, WRange range);
Activity MoveIntoWorld(Actor self, CPos cell);
Activity VisualMove(Actor self, WPos fromPos, WPos toPos);
CPos NearestMoveableCell(CPos target); CPos NearestMoveableCell(CPos target);
bool IsMoving { get; set; } bool IsMoving { get; set; }
} }

View File

@@ -37,18 +37,11 @@ namespace OpenRA.Mods.RA.Activities
return Util.SequenceActivities(new MoveAdjacentTo(self, target), this); return Util.SequenceActivities(new MoveAdjacentTo(self, target), this);
// Move to the middle of the target, ignoring impassable tiles // Move to the middle of the target, ignoring impassable tiles
var mobile = self.Trait<Mobile>(); var move = self.Trait<IMove>();
var to = target.CenterPosition;
var from = self.CenterPosition;
var speed = mobile.MovementSpeedForCell(self, self.Location);
var length = speed > 0 ? (to - from).Length / speed : 0;
return Util.SequenceActivities( return Util.SequenceActivities(
new Turn(Util.GetFacing(to - from, mobile.Facing)), move.VisualMove(self, self.CenterPosition, target.CenterPosition),
new Drag(from, to, length),
inner, inner,
new Turn(Util.GetFacing(from - to, mobile.Facing)), move.VisualMove(self, target.CenterPosition, self.CenterPosition),
new Drag(to, from, length),
NextActivity NextActivity
); );
} }

View File

@@ -33,20 +33,21 @@ namespace OpenRA.Mods.RA.Activities
public CPos? ChooseExitCell(Actor passenger) public CPos? ChooseExitCell(Actor passenger)
{ {
var mobile = passenger.Trait<Mobile>(); var pos = passenger.Trait<IPositionable>();
return cargo.CurrentAdjacentCells return cargo.CurrentAdjacentCells
.Shuffle(self.World.SharedRandom) .Shuffle(self.World.SharedRandom)
.Cast<CPos?>() .Cast<CPos?>()
.FirstOrDefault(c => mobile.CanEnterCell(c.Value)); .FirstOrDefault(c => pos.CanEnterCell(c.Value));
} }
IEnumerable<CPos> BlockedExitCells(Actor passenger) IEnumerable<CPos> BlockedExitCells(Actor passenger)
{ {
var mobile = passenger.Trait<Mobile>(); var pos = passenger.Trait<IPositionable>();
// Find the cells that are blocked by transient actors
return cargo.CurrentAdjacentCells return cargo.CurrentAdjacentCells
.Where(c => mobile.MovementSpeedForCell(passenger, c) != int.MaxValue && !mobile.CanEnterCell(c)); .Where(c => pos.CanEnterCell(c, null, true) != pos.CanEnterCell(c, null, false));
} }
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
@@ -58,6 +59,7 @@ namespace OpenRA.Mods.RA.Activities
cloak.Uncloak(); cloak.Uncloak();
var actor = cargo.Peek(self); var actor = cargo.Peek(self);
var spawn = self.CenterPosition;
var exitCell = ChooseExitCell(actor); var exitCell = ChooseExitCell(actor);
if (exitCell == null) if (exitCell == null)
@@ -71,33 +73,18 @@ namespace OpenRA.Mods.RA.Activities
} }
cargo.Unload(self); cargo.Unload(self);
self.World.AddFrameEndTask(w => self.World.AddFrameEndTask(w =>
{ {
if (actor.Destroyed) if (actor.Destroyed)
return; return;
var mobile = actor.Trait<Mobile>(); var move = actor.Trait<IMove>();
var pos = actor.Trait<IPositionable>();
var exitSubcell = mobile.GetDesiredSubcell(exitCell.Value, null);
mobile.fromSubCell = exitSubcell; // these settings make sure that the below Set* calls
mobile.toSubCell = exitSubcell; // and the above GetDesiredSubcell call pick a good free subcell for later units being unloaded
var exit = exitCell.Value.CenterPosition + MobileInfo.SubCellOffsets[exitSubcell];
var current = self.Location.CenterPosition + MobileInfo.SubCellOffsets[exitSubcell];
mobile.Facing = Util.GetFacing(exit - current, mobile.Facing);
mobile.SetPosition(actor, exitCell.Value);
mobile.SetVisualPosition(actor, current);
var speed = mobile.MovementSpeedForCell(actor, exitCell.Value);
var length = speed > 0 ? (exit - current).Length / speed : 0;
w.Add(actor); w.Add(actor);
actor.CancelActivity(); actor.CancelActivity();
actor.QueueActivity(new Drag(current, exit, length)); pos.SetVisualPosition(actor, spawn);
actor.QueueActivity(mobile.MoveTo(exitCell.Value, 0)); actor.QueueActivity(move.MoveIntoWorld(actor, exitCell.Value));
actor.SetTargetLine(Target.FromCell(exitCell.Value), Color.Green, false); actor.SetTargetLine(Target.FromCell(exitCell.Value), Color.Green, false);
}); });

View File

@@ -159,5 +159,16 @@ namespace OpenRA.Mods.RA.Air
public Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange) { return new HeliFly(self, target, minRange, maxRange); } public Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange) { return new HeliFly(self, target, minRange, maxRange); }
public Activity MoveFollow(Actor self, Target target, WRange range) { return new Follow(self, target, range); } public Activity MoveFollow(Actor self, Target target, WRange range) { return new Follow(self, target, range); }
public CPos NearestMoveableCell(CPos cell) { return cell; } public CPos NearestMoveableCell(CPos cell) { return cell; }
public Activity MoveIntoWorld(Actor self, CPos cell)
{
return new HeliFly(self, Target.FromCell(cell));
}
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos)
{
// TODO: Ignore repulsion when moving
return Util.SequenceActivities(new CallFunc(() => SetVisualPosition(self, fromPos)), new HeliFly(self, Target.FromPos(toPos)));
}
} }
} }

View File

@@ -100,5 +100,8 @@ namespace OpenRA.Mods.RA.Air
public Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange) { return Util.SequenceActivities(new Fly(self, target, minRange, maxRange), new FlyCircle()); } public Activity MoveWithinRange(Target target, WRange minRange, WRange maxRange) { return Util.SequenceActivities(new Fly(self, target, minRange, maxRange), new FlyCircle()); }
public Activity MoveFollow(Actor self, Target target, WRange range) { return new FlyFollow(self, target, range); } public Activity MoveFollow(Actor self, Target target, WRange range) { return new FlyFollow(self, target, range); }
public CPos NearestMoveableCell(CPos cell) { return cell; } public CPos NearestMoveableCell(CPos cell) { return cell; }
public Activity MoveIntoWorld(Actor self, CPos cell) { return new Fly(self, Target.FromCell(cell)); }
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos) { return Util.SequenceActivities(new CallFunc(() => SetVisualPosition(self, fromPos)), new Fly(self, Target.FromPos(toPos))); }
} }
} }

View File

@@ -548,5 +548,31 @@ namespace OpenRA.Mods.RA.Move
if (self.IsIdle && self.AppearsFriendlyTo(blocking)) if (self.IsIdle && self.AppearsFriendlyTo(blocking))
Nudge(self, blocking, true); Nudge(self, blocking, true);
} }
public Activity MoveIntoWorld(Actor self, CPos cell)
{
var pos = self.CenterPosition;
// Reserve the exit cell
SetPosition(self, cell);
SetVisualPosition(self, pos);
// Animate transition
var to = cell.CenterPosition;
var speed = MovementSpeedForCell(self, cell);
var length = speed > 0 ? (to - pos).Length / speed : 0;
var facing = Util.GetFacing(to - pos, Facing);
return Util.SequenceActivities(new Turn(facing), new Drag(pos, to, length));
}
public Activity VisualMove(Actor self, WPos fromPos, WPos toPos)
{
var speed = MovementSpeedForCell(self, self.Location);
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));
}
} }
} }

View File

@@ -34,6 +34,7 @@ namespace OpenRA.Mods.RA
[Desc("Cell offset where the exiting actor enters the ActorMap")] [Desc("Cell offset where the exiting actor enters the ActorMap")]
public readonly CVec ExitCell = CVec.Zero; public readonly CVec ExitCell = CVec.Zero;
public readonly int Facing = -1; public readonly int Facing = -1;
public readonly bool MoveIntoWorld = true;
} }
public class Exit { } public class Exit { }
@@ -58,28 +59,15 @@ namespace OpenRA.Mods.RA
var newUnit = self.World.CreateActor(producee.Name, new TypeDictionary var newUnit = self.World.CreateActor(producee.Name, new TypeDictionary
{ {
new OwnerInit(self.Owner), new OwnerInit(self.Owner),
new LocationInit(exit), new CenterPositionInit(spawn),
new FacingInit(initialFacing) new FacingInit(initialFacing)
}); });
// TODO: Move this into an *Init var move = newUnit.Trait<IMove>();
// TODO: We should be adjusting the actual position for aircraft, not just visuals. if (exitinfo.MoveIntoWorld)
var teleportable = newUnit.Trait<IPositionable>(); newUnit.QueueActivity(move.MoveIntoWorld(newUnit, exit));
teleportable.SetVisualPosition(newUnit, spawn);
// TODO: Generalize this for non-mobile (e.g. aircraft) too
// Remember to update the Enter activity too
var mobile = newUnit.TraitOrDefault<Mobile>();
if (mobile != null)
{
// Animate the spawn -> exit transition
var speed = mobile.MovementSpeedForCell(newUnit, exit);
var length = speed > 0 ? (to - spawn).Length / speed : 0;
newUnit.QueueActivity(new Drag(spawn, to, length));
}
var target = MoveToRallyPoint(self, newUnit, exit); var target = MoveToRallyPoint(self, newUnit, exit);
newUnit.SetTargetLine(Target.FromCell(target), Color.Green, false); newUnit.SetTargetLine(Target.FromCell(target), Color.Green, false);
foreach (var t in self.TraitsImplementing<INotifyProduction>()) foreach (var t in self.TraitsImplementing<INotifyProduction>())
t.UnitProduced(self, newUnit, exit); t.UnitProduced(self, newUnit, exit);

View File

@@ -14,7 +14,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA.Render namespace OpenRA.Mods.RA.Render
{ {
public class RenderInfantryInfo : RenderSimpleInfo, Requires<MobileInfo> public class RenderInfantryInfo : RenderSimpleInfo, Requires<IMoveInfo>
{ {
public readonly int MinIdleWaitTicks = 30; public readonly int MinIdleWaitTicks = 30;
public readonly int MaxIdleWaitTicks = 110; public readonly int MaxIdleWaitTicks = 110;
@@ -35,7 +35,7 @@ namespace OpenRA.Mods.RA.Render
IdleAnimating IdleAnimating
} }
Mobile mobile; IMove move;
RenderInfantryInfo info; RenderInfantryInfo info;
public bool IsMoving { get; set; } public bool IsMoving { get; set; }
protected bool dirty = false; protected bool dirty = false;
@@ -60,7 +60,7 @@ namespace OpenRA.Mods.RA.Render
this.info = info; this.info = info;
anim.PlayFetchIndex(NormalizeInfantrySequence(self, info.StandAnimations.Random(Game.CosmeticRandom)), () => 0); anim.PlayFetchIndex(NormalizeInfantrySequence(self, info.StandAnimations.Random(Game.CosmeticRandom)), () => 0);
State = AnimationState.Waiting; State = AnimationState.Waiting;
mobile = self.Trait<Mobile>(); move = self.Trait<IMove>();
self.Trait<IBodyOrientation>().SetAutodetectedFacings(anim.CurrentSequence.Facings); self.Trait<IBodyOrientation>().SetAutodetectedFacings(anim.CurrentSequence.Facings);
} }
@@ -83,12 +83,12 @@ namespace OpenRA.Mods.RA.Render
{ {
base.Tick(self); base.Tick(self);
if ((State == AnimationState.Moving || dirty) && !mobile.IsMoving) if ((State == AnimationState.Moving || dirty) && !move.IsMoving)
{ {
State = AnimationState.Waiting; State = AnimationState.Waiting;
anim.PlayFetchIndex(NormalizeInfantrySequence(self, info.StandAnimations.Random(Game.CosmeticRandom)), () => 0); anim.PlayFetchIndex(NormalizeInfantrySequence(self, info.StandAnimations.Random(Game.CosmeticRandom)), () => 0);
} }
else if ((State != AnimationState.Moving || dirty) && mobile.IsMoving) else if ((State != AnimationState.Moving || dirty) && move.IsMoving)
{ {
State = AnimationState.Moving; State = AnimationState.Moving;
anim.PlayRepeating(NormalizeInfantrySequence(self, "run")); anim.PlayRepeating(NormalizeInfantrySequence(self, "run"));

View File

@@ -14,7 +14,7 @@ using OpenRA.Mods.RA.Move;
namespace OpenRA.Mods.RA.Render namespace OpenRA.Mods.RA.Render
{ {
public class RenderLandingCraftInfo : RenderUnitInfo public class RenderLandingCraftInfo : RenderUnitInfo, Requires<IMoveInfo>
{ {
public readonly string[] OpenTerrainTypes = { "Clear" }; public readonly string[] OpenTerrainTypes = { "Clear" };
public readonly string OpenAnim = "open"; public readonly string OpenAnim = "open";
@@ -25,26 +25,24 @@ namespace OpenRA.Mods.RA.Render
public class RenderLandingCraft : RenderUnit public class RenderLandingCraft : RenderUnit
{ {
readonly RenderLandingCraftInfo info;
readonly Actor self; readonly Actor self;
readonly Cargo cargo; readonly Cargo cargo;
readonly RenderLandingCraftInfo info; readonly IMove move;
bool open; bool open;
public RenderLandingCraft(Actor self, RenderLandingCraftInfo info) public RenderLandingCraft(Actor self, RenderLandingCraftInfo info)
: base(self) : base(self)
{ {
this.info = info;
this.self = self; this.self = self;
cargo = self.Trait<Cargo>(); cargo = self.Trait<Cargo>();
this.info = info; move = self.Trait<IMove>();
} }
public bool ShouldBeOpen() public bool ShouldBeOpen()
{ {
var mobile = self.TraitOrDefault<Mobile>(); if (self.CenterPosition.Z > 0 || move.IsMoving)
if (mobile == null)
return false;
if (self.CenterPosition.Z > 0 || mobile.IsMoving)
return false; return false;
return cargo.CurrentAdjacentCells return cargo.CurrentAdjacentCells

View File

@@ -1025,6 +1025,7 @@ HPAD:
Exit@1: Exit@1:
SpawnOffset: 0,-256,0 SpawnOffset: 0,-256,0
ExitCell: 0,0 ExitCell: 0,0
MoveIntoWorld: false
Production: Production:
Produces: Helicopter Produces: Helicopter
Reservable: Reservable:
@@ -1059,6 +1060,7 @@ AFLD:
SpawnOffset: 0,170,0 SpawnOffset: 0,170,0
ExitCell: 1,1 ExitCell: 1,1
Facing: 192 Facing: 192
MoveIntoWorld: false
Production: Production:
Produces: Plane Produces: Plane
Reservable: Reservable: