Airborne transports only land to (un)load.

This commit is contained in:
tovl
2019-06-30 13:41:01 +00:00
committed by reaperrr
parent 76422933f6
commit 5920de1384
15 changed files with 211 additions and 97 deletions

View File

@@ -45,7 +45,7 @@ namespace OpenRA.Mods.Common.Activities
destination = Target.FromCell(self.World, self.Location);
QueueChild(self, new Land(self, destination, deliverRange), true);
QueueChild(self, new Wait(carryall.Info.UnloadingDelay, false), true);
QueueChild(self, new Wait(carryall.Info.BeforeUnloadDelay, false), true);
QueueChild(self, new ReleaseUnit(self));
QueueChild(self, new TakeOff(self));
}

View File

@@ -24,6 +24,7 @@ namespace OpenRA.Mods.Common.Activities
Actor enterActor;
Cargo enterCargo;
Aircraft enterAircraft;
public EnterTransport(Actor self, Target target)
: base(self, target, Color.Green)
@@ -35,6 +36,7 @@ namespace OpenRA.Mods.Common.Activities
{
enterActor = targetActor;
enterCargo = targetActor.TraitOrDefault<Cargo>();
enterAircraft = targetActor.TraitOrDefault<Aircraft>();
// Make sure we can still enter the transport
// (but not before, because this may stop the actor in the middle of nowhere)
@@ -44,6 +46,9 @@ namespace OpenRA.Mods.Common.Activities
return false;
}
if (enterAircraft != null && !enterAircraft.AtLandAltitude)
return false;
return true;
}

View File

@@ -24,13 +24,27 @@ namespace OpenRA.Mods.Common.Activities
readonly Cargo cargo;
readonly INotifyUnload[] notifiers;
readonly bool unloadAll;
readonly Aircraft aircraft;
readonly bool assignTargetOnFirstRun;
readonly WDist unloadRange;
public UnloadCargo(Actor self, bool unloadAll)
Target destination;
public UnloadCargo(Actor self, WDist unloadRange, bool unloadAll = true)
: this(self, Target.Invalid, unloadRange, unloadAll)
{
assignTargetOnFirstRun = true;
}
public UnloadCargo(Actor self, Target destination, WDist unloadRange, bool unloadAll = true)
{
this.self = self;
cargo = self.Trait<Cargo>();
notifiers = self.TraitsImplementing<INotifyUnload>().ToArray();
this.unloadAll = unloadAll;
aircraft = self.TraitOrDefault<Aircraft>();
this.destination = destination;
this.unloadRange = unloadRange;
}
public Pair<CPos, SubCell>? ChooseExitSubCell(Actor passenger)
@@ -53,6 +67,23 @@ namespace OpenRA.Mods.Common.Activities
.Where(c => pos.CanEnterCell(c, null, true) != pos.CanEnterCell(c, null, false));
}
protected override void OnFirstRun(Actor self)
{
if (assignTargetOnFirstRun)
destination = Target.FromCell(self.World, self.Location);
// Move to the target destination
if (aircraft != null)
QueueChild(self, new Land(self, destination, unloadRange));
else
{
var cell = self.World.Map.Clamp(this.self.World.Map.CellContaining(destination.CenterPosition));
QueueChild(self, new Move(self, cell, unloadRange));
}
QueueChild(self, new Wait(cargo.Info.BeforeUnloadDelay));
}
public override Activity Tick(Actor self)
{
if (ChildActivity != null)
@@ -62,50 +93,52 @@ namespace OpenRA.Mods.Common.Activities
return this;
}
cargo.Unloading = false;
if (IsCanceling || cargo.IsEmpty(self))
return NextActivity;
if (!cargo.CanUnload())
if (cargo.CanUnload())
{
foreach (var inu in notifiers)
inu.Unloading(self);
var actor = cargo.Peek(self);
var spawn = self.CenterPosition;
var exitSubCell = ChooseExitSubCell(actor);
if (exitSubCell == null)
{
self.NotifyBlocker(BlockedExitCells(actor));
QueueChild(self, new Wait(10), true);
return this;
}
cargo.Unload(self);
self.World.AddFrameEndTask(w =>
{
if (actor.Disposed)
return;
var move = actor.Trait<IMove>();
var pos = actor.Trait<IPositionable>();
actor.CancelActivity();
pos.SetVisualPosition(actor, spawn);
actor.QueueActivity(move.MoveIntoWorld(actor, exitSubCell.Value.First, exitSubCell.Value.Second));
actor.SetTargetLine(Target.FromCell(w, exitSubCell.Value.First, exitSubCell.Value.Second), Color.Green, false);
w.Add(actor);
});
}
if (!unloadAll || !cargo.CanUnload())
{
Cancel(self, true);
return NextActivity;
if (cargo.Info.AfterUnloadDelay > 0)
QueueChild(self, new Wait(cargo.Info.AfterUnloadDelay, false), true);
if (aircraft != null && !aircraft.Info.LandWhenIdle)
QueueChild(self, new TakeOff(self), true);
}
foreach (var inu in notifiers)
inu.Unloading(self);
var actor = cargo.Peek(self);
var spawn = self.CenterPosition;
var exitSubCell = ChooseExitSubCell(actor);
if (exitSubCell == null)
{
self.NotifyBlocker(BlockedExitCells(actor));
QueueChild(self, new Wait(10), true);
return this;
}
cargo.Unload(self);
self.World.AddFrameEndTask(w =>
{
if (actor.Disposed)
return;
var move = actor.Trait<IMove>();
var pos = actor.Trait<IPositionable>();
actor.CancelActivity();
pos.SetVisualPosition(actor, spawn);
actor.QueueActivity(move.MoveIntoWorld(actor, exitSubCell.Value.First, exitSubCell.Value.Second));
actor.SetTargetLine(Target.FromCell(w, exitSubCell.Value.First, exitSubCell.Value.Second), Color.Green, false);
w.Add(actor);
});
if (!unloadAll || cargo.IsEmpty(self))
return NextActivity;
cargo.Unloading = true;
return this;
}
}

View File

@@ -150,44 +150,14 @@ namespace OpenRA.Mods.Common.Scripting
else
{
var aircraft = transport.TraitOrDefault<Aircraft>();
// Scripted cargo aircraft must turn to default position before unloading.
// TODO: pass facing through UnloadCargo instead.
if (aircraft != null)
{
var destination = entryPath.Last();
// Try to find an alternative landing spot if we can't land at the current destination
if (!aircraft.CanLand(destination) && dropRange > 0)
{
var locomotors = cargo.Passengers
.Select(a => a.Info.TraitInfoOrDefault<MobileInfo>())
.Where(m => m != null)
.Distinct()
.Select(m => m.LocomotorInfo)
.ToList();
foreach (var c in transport.World.Map.FindTilesInCircle(destination, dropRange))
{
if (!aircraft.CanLand(c))
continue;
if (!locomotors.All(m => domainIndex.IsPassable(destination, c, m)))
continue;
destination = c;
break;
}
}
transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, destination), facing: aircraft.Info.InitialFacing));
transport.QueueActivity(new Wait(15));
}
transport.QueueActivity(new Land(transport, Target.FromCell(transport.World, entryPath.Last()), WDist.FromCells(dropRange), aircraft.Info.InitialFacing));
if (cargo != null)
{
transport.QueueActivity(new UnloadCargo(transport, true));
transport.QueueActivity(new WaitFor(() => cargo.IsEmpty(transport)));
}
transport.QueueActivity(new Wait(aircraft != null ? 50 : 25));
transport.QueueActivity(new UnloadCargo(transport, WDist.FromCells(dropRange)));
}
if (exitFunc != null)

View File

@@ -42,9 +42,15 @@ namespace OpenRA.Mods.Common.Scripting
[ScriptActorPropertyActivity]
[Desc("Command transport to unload passengers.")]
public void UnloadPassengers()
public void UnloadPassengers(CPos? cell = null, int unloadRange = 5)
{
Self.QueueActivity(new UnloadCargo(Self, true));
if (cell.HasValue)
{
var destination = Target.FromCell(Self.World, cell.Value);
Self.QueueActivity(new UnloadCargo(Self, destination, WDist.FromCells(unloadRange)));
}
else
Self.QueueActivity(new UnloadCargo(Self, WDist.FromCells(unloadRange)));
}
}
}

View File

@@ -664,7 +664,8 @@ namespace OpenRA.Mods.Common.Traits
protected virtual void OnBecomingIdle(Actor self)
{
var atLandAltitude = self.World.Map.DistanceAboveTerrain(CenterPosition) == LandAltitude;
var altitude = self.World.Map.DistanceAboveTerrain(CenterPosition);
var atLandAltitude = altitude == LandAltitude;
// Work-around to prevent players from accidentally canceling resupply by pressing 'Stop',
// by re-queueing Resupply as long as resupply hasn't finished and aircraft is still on resupplier.
@@ -692,6 +693,8 @@ namespace OpenRA.Mods.Common.Traits
// Will go away soon (in a separate PR) with the arrival of ActionsWhenIdle.
self.QueueActivity(new FlyCircle(self, -1, Info.IdleTurnSpeed > -1 ? Info.IdleTurnSpeed : TurnSpeed));
}
else if (!atLandAltitude && altitude != Info.CruiseAltitude && !Info.LandWhenIdle)
self.QueueActivity(new TakeOff(self));
}
#region Implement IPositionable

View File

@@ -47,9 +47,21 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Voice to play when ordered to unload the passengers.")]
public readonly string UnloadVoice = "Action";
[Desc("Radius to search for a load/unload location if the ordered cell is blocked.")]
public readonly WDist LoadRange = WDist.FromCells(5);
[Desc("Which direction the passenger will face (relative to the transport) when unloading.")]
public readonly int PassengerFacing = 128;
[Desc("Delay (in ticks) before continuing after loading a passenger.")]
public readonly int AfterLoadDelay = 8;
[Desc("Delay (in ticks) before unloading the first passenger.")]
public readonly int BeforeUnloadDelay = 8;
[Desc("Delay (in ticks) before continuing after unloading a passenger.")]
public readonly int AfterUnloadDelay = 25;
[Desc("Cursor to display when able to unload the passengers.")]
public readonly string UnloadCursor = "deploy";
@@ -96,15 +108,16 @@ namespace OpenRA.Mods.Common.Traits
CPos currentCell;
public IEnumerable<CPos> CurrentAdjacentCells { get; private set; }
public bool Unloading { get; internal set; }
public IEnumerable<Actor> Passengers { get { return cargo; } }
public int PassengerCount { get { return cargo.Count; } }
enum State { Free, Locked }
State state = State.Free;
public Cargo(ActorInitializer init, CargoInfo info)
{
self = init.Self;
Info = info;
Unloading = false;
checkTerrainType = info.UnloadTerrainTypes.Count > 0;
if (init.Contains<RuntimeCargoInit>())
@@ -195,11 +208,7 @@ namespace OpenRA.Mods.Common.Traits
if (!order.Queued)
self.CancelActivity();
Unloading = true;
if (aircraft != null)
self.QueueActivity(new Land(self));
self.QueueActivity(new UnloadCargo(self, true));
self.QueueActivity(new UnloadCargo(self, Info.LoadRange));
}
}
@@ -218,13 +227,13 @@ namespace OpenRA.Mods.Common.Traits
return false;
}
return !IsEmpty(self) && (aircraft == null || aircraft.CanLand(self.Location))
return !IsEmpty(self) && (aircraft == null || aircraft.CanLand(self.Location, blockedByMobile: false))
&& CurrentAdjacentCells != null && CurrentAdjacentCells.Any(c => Passengers.Any(p => p.Trait<IPositionable>().CanEnterCell(c, null, immediate)));
}
public bool CanLoad(Actor self, Actor a)
{
return (reserves.Contains(a) || HasSpace(GetWeight(a))) && self.IsAtGroundLevel();
return reserves.Contains(a) || HasSpace(GetWeight(a));
}
internal bool ReserveSpace(Actor a)
@@ -241,6 +250,7 @@ namespace OpenRA.Mods.Common.Traits
reserves.Add(a);
reservedWeight += w;
LockForPickup(self);
return true;
}
@@ -252,11 +262,43 @@ namespace OpenRA.Mods.Common.Traits
reservedWeight -= GetWeight(a);
reserves.Remove(a);
ReleaseLock(self);
if (loadingToken != ConditionManager.InvalidConditionToken)
loadingToken = conditionManager.RevokeCondition(self, loadingToken);
}
// Prepare for transport pickup
bool LockForPickup(Actor self)
{
if (state == State.Locked)
return false;
state = State.Locked;
self.CancelActivity();
var air = self.TraitOrDefault<Aircraft>();
if (air != null && !air.AtLandAltitude)
self.QueueActivity(new Land(self));
self.QueueActivity(new WaitFor(() => state != State.Locked, false));
return true;
}
void ReleaseLock(Actor self)
{
if (reservedWeight != 0)
return;
state = State.Free;
self.QueueActivity(new Wait(Info.AfterLoadDelay, false));
var air = self.TraitOrDefault<Aircraft>();
if (air != null)
self.QueueActivity(new TakeOff(self));
}
public string CursorForOrder(Actor self, Order order)
{
if (order.OrderString != "Unload")
@@ -353,6 +395,7 @@ namespace OpenRA.Mods.Common.Traits
{
reservedWeight -= w;
reserves.Remove(a);
ReleaseLock(self);
if (loadingToken != ConditionManager.InvalidConditionToken)
loadingToken = conditionManager.RevokeCondition(self, loadingToken);

View File

@@ -23,11 +23,11 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Transports actors with the `Carryable` trait.")]
public class CarryallInfo : ITraitInfo, Requires<BodyOrientationInfo>, Requires<AircraftInfo>
{
[Desc("Delay on the ground while attaching an actor to the carryall.")]
public readonly int LoadingDelay = 0;
[Desc("Delay (in ticks) on the ground while attaching an actor to the carryall.")]
public readonly int BeforeLoadDelay = 0;
[Desc("Delay on the ground while detacting an actor to the carryall.")]
public readonly int UnloadingDelay = 0;
[Desc("Delay (in ticks) on the ground while detaching an actor from the carryall.")]
public readonly int BeforeUnloadDelay = 0;
[Desc("Carryable attachment point relative to body.")]
public readonly WVec LocalOffset = WVec.Zero;
@@ -317,7 +317,7 @@ namespace OpenRA.Mods.Common.Traits
self.CancelActivity();
self.SetTargetLine(order.Target, Color.Yellow);
self.QueueActivity(order.Queued, new PickupUnit(self, order.Target.Actor, Info.LoadingDelay));
self.QueueActivity(order.Queued, new PickupUnit(self, order.Target.Actor, Info.BeforeLoadDelay));
}
}

View File

@@ -168,9 +168,13 @@ namespace OpenRA.Mods.Common.Traits
public bool Reserve(Actor self, Cargo cargo)
{
if (cargo == ReservedCargo)
return true;
Unreserve(self);
if (!cargo.ReserveSpace(self))
return false;
ReservedCargo = cargo;
return true;
}
@@ -181,6 +185,7 @@ namespace OpenRA.Mods.Common.Traits
{
if (ReservedCargo == null)
return;
ReservedCargo.UnreserveSpace(self);
ReservedCargo = null;
}

View File

@@ -0,0 +1,42 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
namespace OpenRA.Mods.Common.UpdateRules.Rules
{
public class RenameCarryallDelays : UpdateRule
{
public override string Name { get { return "Rename Carryall and Cargo delay parameters"; } }
public override string Description
{
get
{
return "Carryall's LoadingDelay and UnloadingDelay parameters have been renamed\n"
+ "to BeforeLoadDelay and BeforeUnloadDelay to match new parameters on Cargo.";
}
}
public override IEnumerable<string> UpdateActorNode(ModData modData, MiniYamlNode actorNode)
{
foreach (var carryall in actorNode.ChildrenMatching("Carryall"))
{
foreach (var node in carryall.ChildrenMatching("LoadingDelay"))
node.RenameKey("BeforeLoadDelay");
foreach (var node in carryall.ChildrenMatching("UnloadingDelay"))
node.RenameKey("BeforeUnloadDelay");
}
yield break;
}
}
}

View File

@@ -129,6 +129,7 @@ namespace OpenRA.Mods.Common.UpdateRules
new RemovePlaceBuildingPalettes(),
new RenameHoversOffsetModifier(),
new AddAirAttackTypes(),
new RenameCarryallDelays(),
})
};

View File

@@ -12,7 +12,7 @@ TRAN:
Queue: Aircraft.GDI, Aircraft.Nod
Description: Fast Infantry Transport Helicopter.\n Unarmed
Aircraft:
LandWhenIdle: true
LandWhenIdle: false
TurnSpeed: 5
Speed: 150
AltitudeVelocity: 0c100
@@ -43,6 +43,7 @@ TRAN:
Types: Infantry
MaxWeight: 10
PipCount: 10
AfterUnloadDelay: 40
SpawnActorOnDeath:
Actor: TRAN.Husk
SelectionDecorations:

View File

@@ -34,8 +34,8 @@ carryall.reinforce:
Actor: carryall.huskVTOL
RequiresCondition: !cruising
Carryall:
LoadingDelay: 10
UnloadingDelay: 15
BeforeLoadDelay: 10
BeforeUnloadDelay: 15
LocalOffset: 0, 0, -128
RenderSprites:
Image: carryall
@@ -52,8 +52,8 @@ carryall:
Inherits: carryall.reinforce
-Carryall:
AutoCarryall:
LoadingDelay: 10
UnloadingDelay: 15
BeforeLoadDelay: 10
BeforeUnloadDelay: 15
LocalOffset: 0, 0, -128
Aircraft:
MinAirborneAltitude: 400

View File

@@ -238,6 +238,7 @@ TRAN:
Range: 6c0
Type: GroundPosition
Aircraft:
LandWhenIdle: false
TurnSpeed: 5
Speed: 128
AltitudeVelocity: 0c58
@@ -261,6 +262,7 @@ TRAN:
Types: Infantry
MaxWeight: 8
PipCount: 8
AfterUnloadDelay: 40
SpawnActorOnDeath:
Actor: TRAN.Husk
SelectionDecorations:

View File

@@ -190,7 +190,7 @@ ORCATRAN:
Prerequisites: ~disabled
RenderSprites:
Aircraft:
LandWhenIdle: true
LandWhenIdle: false
TurnSpeed: 5
Speed: 84
InitialFacing: 0
@@ -210,6 +210,7 @@ ORCATRAN:
PipCount: 5
UnloadVoice: Move
EjectOnDeath: true
AfterUnloadDelay: 40
SpawnActorOnDeath:
Actor: ORCATRAN.Husk
@@ -236,6 +237,8 @@ TRNSPORT:
Carryall:
Voice: Move
LocalOffset: 0,0,-317
BeforeLoadDelay: 10
BeforeUnloadDelay: 10
Health:
HP: 17500
Armor: