Rework paradrop logic to be more robust.

This commit is contained in:
Paul Chote
2019-05-26 22:19:46 +00:00
committed by reaperrr
parent 829b8cd2e1
commit e2b27328bd
5 changed files with 94 additions and 91 deletions

View File

@@ -18,72 +18,45 @@ namespace OpenRA.Mods.Common.Activities
public class Parachute : Activity public class Parachute : Activity
{ {
readonly IPositionable pos; readonly IPositionable pos;
readonly ParachutableInfo para;
readonly WVec fallVector; readonly WVec fallVector;
readonly Actor ignore; readonly Actor ignore;
WPos dropPosition; int groundLevel;
WPos currentPosition;
bool triggered = false;
public Parachute(Actor self, WPos dropPosition, Actor ignoreActor = null) public Parachute(Actor self, Actor ignoreActor = null)
{ {
pos = self.TraitOrDefault<IPositionable>(); pos = self.TraitOrDefault<IPositionable>();
ignore = ignoreActor; ignore = ignoreActor;
// Parachutable trait is a prerequisite for running this activity fallVector = new WVec(0, 0, self.Info.TraitInfo<ParachutableInfo>().FallRate);
para = self.Info.TraitInfo<ParachutableInfo>();
fallVector = new WVec(0, 0, para.FallRate);
this.dropPosition = dropPosition;
IsInterruptible = false; IsInterruptible = false;
} }
Activity FirstTick(Actor self) protected override void OnFirstRun(Actor self)
{ {
triggered = true; groundLevel = self.World.Map.CenterOfCell(self.Location).Z;
foreach (var np in self.TraitsImplementing<INotifyParachute>()) foreach (var np in self.TraitsImplementing<INotifyParachute>())
np.OnParachute(self); np.OnParachute(self);
// Place the actor and retrieve its visual position (CenterPosition)
pos.SetPosition(self, dropPosition);
currentPosition = self.CenterPosition;
return this;
}
Activity LastTick(Actor self)
{
var dat = self.World.Map.DistanceAboveTerrain(currentPosition);
pos.SetPosition(self, currentPosition - new WVec(WDist.Zero, WDist.Zero, dat));
foreach (var np in self.TraitsImplementing<INotifyParachute>())
np.OnLanded(self, ignore);
return NextActivity;
} }
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
// If this is the first tick var nextPosition = self.CenterPosition - fallVector;
if (!triggered) if (nextPosition.Z < groundLevel)
return FirstTick(self); return NextActivity;
currentPosition -= fallVector; pos.SetVisualPosition(self, nextPosition);
// If the unit has landed, this will be the last tick
if (self.World.Map.DistanceAboveTerrain(currentPosition).Length <= 0)
return LastTick(self);
pos.SetVisualPosition(self, currentPosition);
return this; return this;
} }
// Only the last queued activity (given order) is kept protected override void OnLastRun(Actor self)
public override void Queue(Actor self, Activity activity)
{ {
NextActivity = activity; var centerPosition = self.CenterPosition;
pos.SetPosition(self, centerPosition - new WVec(0, 0, groundLevel - centerPosition.Z));
foreach (var np in self.TraitsImplementing<INotifyParachute>())
np.OnLanded(self, ignore);
} }
} }
} }

View File

@@ -63,32 +63,46 @@ namespace OpenRA.Mods.Common.Traits
var pilot = self.World.CreateActor(false, Info.PilotActor.ToLowerInvariant(), var pilot = self.World.CreateActor(false, Info.PilotActor.ToLowerInvariant(),
new TypeDictionary { new OwnerInit(self.Owner), new LocationInit(self.Location) }); new TypeDictionary { new OwnerInit(self.Owner), new LocationInit(self.Location) });
if (Info.AllowUnsuitableCell || IsSuitableCell(self, pilot)) var pilotPositionable = pilot.TraitOrDefault<IPositionable>();
var pilotCell = self.Location;
var pilotSubCell = pilotPositionable.GetAvailableSubCell(pilotCell);
if (pilotSubCell == SubCell.Invalid)
{ {
if (inAir) if (!Info.AllowUnsuitableCell)
{ {
self.World.AddFrameEndTask(w => pilot.Dispose();
{ return;
w.Add(pilot);
pilot.QueueActivity(new Parachute(pilot, cp));
});
Game.Sound.Play(SoundType.World, Info.ChuteSound, cp);
} }
else
pilotSubCell = SubCell.Any;
}
if (inAir)
{
self.World.AddFrameEndTask(w =>
{ {
self.World.AddFrameEndTask(w => w.Add(pilot)); pilotPositionable.SetPosition(pilot, pilotCell, pilotSubCell);
w.Add(pilot);
var dropPosition = pilot.CenterPosition + new WVec(0, 0, self.CenterPosition.Z - pilot.CenterPosition.Z);
pilotPositionable.SetVisualPosition(pilot, dropPosition);
pilot.QueueActivity(new Parachute(pilot));
});
Game.Sound.Play(SoundType.World, Info.ChuteSound, cp);
}
else
{
self.World.AddFrameEndTask(w =>
{
w.Add(pilot);
pilotPositionable.SetPosition(pilot, pilotCell, pilotSubCell);
var pilotMobile = pilot.TraitOrDefault<Mobile>(); var pilotMobile = pilot.TraitOrDefault<Mobile>();
if (pilotMobile != null) if (pilotMobile != null)
pilotMobile.Nudge(pilot, pilot, true); pilotMobile.Nudge(pilot, pilot, true);
} });
} }
else
pilot.Dispose();
}
static bool IsSuitableCell(Actor self, Actor actorToDrop)
{
return actorToDrop.Trait<IPositionable>().CanEnterCell(self.Location, self, true);
} }
} }
} }

View File

@@ -22,6 +22,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Distance around the drop-point to unload troops.")] [Desc("Distance around the drop-point to unload troops.")]
public readonly WDist DropRange = WDist.FromCells(4); public readonly WDist DropRange = WDist.FromCells(4);
[Desc("Wait at least this many ticks between each drop.")]
public readonly int DropInterval = 5;
[Desc("Sound to play when dropping.")] [Desc("Sound to play when dropping.")]
public readonly string ChuteSound = null; public readonly string ChuteSound = null;
@@ -33,7 +36,6 @@ namespace OpenRA.Mods.Common.Traits
readonly ParaDropInfo info; readonly ParaDropInfo info;
readonly Actor self; readonly Actor self;
readonly Cargo cargo; readonly Cargo cargo;
readonly HashSet<CPos> droppedAt = new HashSet<CPos>();
public event Action<Actor> OnRemovedFromWorld = self => { }; public event Action<Actor> OnRemovedFromWorld = self => { };
public event Action<Actor> OnEnteredDropRange = self => { }; public event Action<Actor> OnEnteredDropRange = self => { };
@@ -45,6 +47,9 @@ namespace OpenRA.Mods.Common.Traits
[Sync] [Sync]
Target target; Target target;
[Sync]
int dropDelay;
bool checkForSuitableCell; bool checkForSuitableCell;
public ParaDrop(Actor self, ParaDropInfo info) public ParaDrop(Actor self, ParaDropInfo info)
@@ -56,13 +61,18 @@ namespace OpenRA.Mods.Common.Traits
public void SetLZ(CPos lz, bool checkLandingCell) public void SetLZ(CPos lz, bool checkLandingCell)
{ {
droppedAt.Clear();
target = Target.FromCell(self.World, lz); target = Target.FromCell(self.World, lz);
checkForSuitableCell = checkLandingCell; checkForSuitableCell = checkLandingCell;
} }
void ITick.Tick(Actor self) void ITick.Tick(Actor self)
{ {
if (dropDelay > 0)
{
dropDelay--;
return;
}
var wasInDropRange = inDropRange; var wasInDropRange = inDropRange;
inDropRange = target.IsInRange(self.CenterPosition, info.DropRange); inDropRange = target.IsInRange(self.CenterPosition, info.DropRange);
@@ -73,30 +83,38 @@ namespace OpenRA.Mods.Common.Traits
OnExitedDropRange(self); OnExitedDropRange(self);
// Are we able to drop the next trooper? // Are we able to drop the next trooper?
if (!inDropRange || cargo.IsEmpty(self)) if (!inDropRange || cargo.IsEmpty(self) || !self.World.Map.Contains(self.Location))
return; return;
if (droppedAt.Contains(self.Location) || (checkForSuitableCell && !IsSuitableCell(cargo.Peek(self), self.Location))) var dropActor = cargo.Peek(self);
return; var dropPositionable = dropActor.Trait<IPositionable>();
var dropCell = self.Location;
var dropSubCell = dropPositionable.GetAvailableSubCell(dropCell);
if (dropSubCell == SubCell.Invalid)
{
if (checkForSuitableCell)
return;
if (!self.World.Map.Contains(self.Location)) dropSubCell = SubCell.Any;
return; }
// unload a dude here // Unload here
droppedAt.Add(self.Location); if (cargo.Unload(self) != dropActor)
throw new InvalidOperationException("Peeked cargo was not unloaded!");
var a = cargo.Unload(self);
self.World.AddFrameEndTask(w => self.World.AddFrameEndTask(w =>
{ {
w.Add(a); dropPositionable.SetPosition(dropActor, dropCell, dropSubCell);
a.QueueActivity(new Parachute(a, self.CenterPosition)); w.Add(dropActor);
});
Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
}
static bool IsSuitableCell(Actor actorToDrop, CPos p) var dropPosition = dropActor.CenterPosition + new WVec(0, 0, self.CenterPosition.Z - dropActor.CenterPosition.Z);
{ dropPositionable.SetVisualPosition(dropActor, dropPosition);
return actorToDrop.Trait<IPositionable>().CanEnterCell(p);
dropActor.QueueActivity(new Parachute(dropActor));
});
Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
dropDelay = info.DropInterval;
} }
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self) void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)

View File

@@ -135,7 +135,7 @@ namespace OpenRA.Mods.Common.Traits
{ {
var newUnit = self.World.CreateActor(producee.Name, td); var newUnit = self.World.CreateActor(producee.Name, td);
newUnit.QueueActivity(new Parachute(newUnit, newUnit.CenterPosition, self)); newUnit.QueueActivity(new Parachute(newUnit, self));
var move = newUnit.TraitOrDefault<IMove>(); var move = newUnit.TraitOrDefault<IMove>();
if (move != null) if (move != null)
{ {

View File

@@ -175,14 +175,6 @@ namespace OpenRA.Mods.Common.Traits
} }
}; };
foreach (var p in info.DropItems)
{
var unit = self.World.CreateActor(false, p.ToLowerInvariant(),
new TypeDictionary { new OwnerInit(self.Owner) });
units.Add(unit);
}
self.World.AddFrameEndTask(w => self.World.AddFrameEndTask(w =>
{ {
PlayLaunchSounds(); PlayLaunchSounds();
@@ -216,11 +208,17 @@ namespace OpenRA.Mods.Common.Traits
drop.OnRemovedFromWorld += onRemovedFromWorld; drop.OnRemovedFromWorld += onRemovedFromWorld;
var cargo = a.Trait<Cargo>(); var cargo = a.Trait<Cargo>();
var passengers = units.Skip(added).Take(passengersPerPlane); foreach (var p in info.DropItems.Skip(added).Take(passengersPerPlane))
added += passengersPerPlane; {
var unit = self.World.CreateActor(false, p.ToLowerInvariant(), new TypeDictionary
{
new OwnerInit(self.Owner)
});
foreach (var p in passengers) cargo.Load(a, unit);
cargo.Load(a, p); units.Add(unit);
added++;
}
a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset))); a.QueueActivity(new Fly(a, Target.FromPos(target + spawnOffset)));
a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset))); a.QueueActivity(new Fly(a, Target.FromPos(finishEdge + spawnOffset)));