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
{
readonly IPositionable pos;
readonly ParachutableInfo para;
readonly WVec fallVector;
readonly Actor ignore;
WPos dropPosition;
WPos currentPosition;
bool triggered = false;
int groundLevel;
public Parachute(Actor self, WPos dropPosition, Actor ignoreActor = null)
public Parachute(Actor self, Actor ignoreActor = null)
{
pos = self.TraitOrDefault<IPositionable>();
ignore = ignoreActor;
// Parachutable trait is a prerequisite for running this activity
para = self.Info.TraitInfo<ParachutableInfo>();
fallVector = new WVec(0, 0, para.FallRate);
this.dropPosition = dropPosition;
fallVector = new WVec(0, 0, self.Info.TraitInfo<ParachutableInfo>().FallRate);
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>())
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)
{
// If this is the first tick
if (!triggered)
return FirstTick(self);
var nextPosition = self.CenterPosition - fallVector;
if (nextPosition.Z < groundLevel)
return NextActivity;
currentPosition -= fallVector;
// 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);
pos.SetVisualPosition(self, nextPosition);
return this;
}
// Only the last queued activity (given order) is kept
public override void Queue(Actor self, Activity activity)
protected override void OnLastRun(Actor self)
{
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(),
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 (!Info.AllowUnsuitableCell)
{
pilot.Dispose();
return;
}
pilotSubCell = SubCell.Any;
}
if (inAir)
{
self.World.AddFrameEndTask(w =>
{
pilotPositionable.SetPosition(pilot, pilotCell, pilotSubCell);
w.Add(pilot);
pilot.QueueActivity(new Parachute(pilot, cp));
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));
self.World.AddFrameEndTask(w =>
{
w.Add(pilot);
pilotPositionable.SetPosition(pilot, pilotCell, pilotSubCell);
var pilotMobile = pilot.TraitOrDefault<Mobile>();
if (pilotMobile != null)
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.")]
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.")]
public readonly string ChuteSound = null;
@@ -33,7 +36,6 @@ namespace OpenRA.Mods.Common.Traits
readonly ParaDropInfo info;
readonly Actor self;
readonly Cargo cargo;
readonly HashSet<CPos> droppedAt = new HashSet<CPos>();
public event Action<Actor> OnRemovedFromWorld = self => { };
public event Action<Actor> OnEnteredDropRange = self => { };
@@ -45,6 +47,9 @@ namespace OpenRA.Mods.Common.Traits
[Sync]
Target target;
[Sync]
int dropDelay;
bool checkForSuitableCell;
public ParaDrop(Actor self, ParaDropInfo info)
@@ -56,13 +61,18 @@ namespace OpenRA.Mods.Common.Traits
public void SetLZ(CPos lz, bool checkLandingCell)
{
droppedAt.Clear();
target = Target.FromCell(self.World, lz);
checkForSuitableCell = checkLandingCell;
}
void ITick.Tick(Actor self)
{
if (dropDelay > 0)
{
dropDelay--;
return;
}
var wasInDropRange = inDropRange;
inDropRange = target.IsInRange(self.CenterPosition, info.DropRange);
@@ -73,30 +83,38 @@ namespace OpenRA.Mods.Common.Traits
OnExitedDropRange(self);
// 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;
if (droppedAt.Contains(self.Location) || (checkForSuitableCell && !IsSuitableCell(cargo.Peek(self), self.Location)))
return;
if (!self.World.Map.Contains(self.Location))
return;
// unload a dude here
droppedAt.Add(self.Location);
var a = cargo.Unload(self);
self.World.AddFrameEndTask(w =>
var dropActor = cargo.Peek(self);
var dropPositionable = dropActor.Trait<IPositionable>();
var dropCell = self.Location;
var dropSubCell = dropPositionable.GetAvailableSubCell(dropCell);
if (dropSubCell == SubCell.Invalid)
{
w.Add(a);
a.QueueActivity(new Parachute(a, self.CenterPosition));
});
Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
if (checkForSuitableCell)
return;
dropSubCell = SubCell.Any;
}
static bool IsSuitableCell(Actor actorToDrop, CPos p)
// Unload here
if (cargo.Unload(self) != dropActor)
throw new InvalidOperationException("Peeked cargo was not unloaded!");
self.World.AddFrameEndTask(w =>
{
return actorToDrop.Trait<IPositionable>().CanEnterCell(p);
dropPositionable.SetPosition(dropActor, dropCell, dropSubCell);
w.Add(dropActor);
var dropPosition = dropActor.CenterPosition + new WVec(0, 0, self.CenterPosition.Z - dropActor.CenterPosition.Z);
dropPositionable.SetVisualPosition(dropActor, dropPosition);
dropActor.QueueActivity(new Parachute(dropActor));
});
Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
dropDelay = info.DropInterval;
}
void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)

View File

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