Add TransformsInto* traits to trigger construction yard undeploy.

This commit is contained in:
Paul Chote
2019-06-05 20:00:27 +00:00
committed by reaperrr
parent 1b026b7e20
commit ecd8dee575
12 changed files with 826 additions and 25 deletions

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Activities
* Queue a new instance instead.
* - Avoid calling actor.CancelActivity(). It is almost always a bug. Call activity.Cancel() instead.
*/
public abstract class Activity
public abstract class Activity : IActivityInterface
{
public ActivityState State { get; private set; }
@@ -172,9 +172,9 @@ namespace OpenRA.Activities
}
}
public IEnumerable<T> ActivitiesImplementing<T>() where T : IActivityInterface
public IEnumerable<T> ActivitiesImplementing<T>(bool includeChildren = true) where T : IActivityInterface
{
if (childActivity != null)
if (includeChildren && childActivity != null)
foreach (var a in childActivity.ActivitiesImplementing<T>())
yield return a;

View File

@@ -9,6 +9,7 @@
*/
#endregion
using System;
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Mods.Common.Traits.Render;
@@ -87,6 +88,10 @@ namespace OpenRA.Mods.Common.Activities
void DoTransform(Actor self)
{
// This activity may be buried as a child within one or more parents
// We need to consider the top-level activities when transferring orders to the new actor!
var currentActivity = self.CurrentActivity;
self.World.AddFrameEndTask(w =>
{
if (self.IsDead || self.WillDispose)
@@ -132,6 +137,14 @@ namespace OpenRA.Mods.Common.Activities
foreach (var nt in self.TraitsImplementing<INotifyTransform>())
nt.AfterTransform(a);
// Use self.CurrentActivity to capture the parent activity if Transform is a child
foreach (var transfer in currentActivity.ActivitiesImplementing<IssueOrderAfterTransform>(false))
{
var order = transfer.IssueOrderForTransformedActor(a);
foreach (var t in a.TraitsImplementing<IResolveOrder>())
t.ResolveOrder(a, order);
}
self.ReplacedByActor = a;
if (selected)
@@ -142,4 +155,27 @@ namespace OpenRA.Mods.Common.Activities
});
}
}
class IssueOrderAfterTransform : Activity
{
readonly string orderString;
readonly Target target;
public IssueOrderAfterTransform(string orderString, Target target)
{
this.orderString = orderString;
this.target = target;
}
public Order IssueOrderForTransformedActor(Actor newActor)
{
return new Order(orderString, newActor, target, true);
}
public override Activity Tick(Actor self)
{
// Activity is a placeholder that should never run
return NextActivity;
}
}
}

View File

@@ -0,0 +1,184 @@
#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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Add to a building to expose a move cursor that triggers Transforms and issues a move order to the transformed actor.")]
public class TransformsIntoAircraftInfo : ConditionalTraitInfo, Requires<TransformsInfo>
{
[Desc("Can the actor be ordered to move in to shroud?")]
public readonly bool MoveIntoShroud = true;
[ActorReference]
[FieldLoader.Require]
public readonly HashSet<string> DockActors = new HashSet<string> { };
[VoiceReference]
public readonly string Voice = "Action";
public override object Create(ActorInitializer init) { return new TransformsIntoAircraft(init, this); }
}
public class TransformsIntoAircraft : ConditionalTrait<TransformsIntoAircraftInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{
readonly Actor self;
Transforms[] transforms;
public TransformsIntoAircraft(ActorInitializer init, TransformsIntoAircraftInfo info)
: base(info)
{
self = init.Self;
}
protected override void Created(Actor self)
{
transforms = self.TraitsImplementing<Transforms>().ToArray();
base.Created(self);
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
if (!IsTraitDisabled)
{
yield return new EnterAlliedActorTargeter<BuildingInfo>("Enter", 5, AircraftCanEnter,
target => Reservable.IsAvailableFor(target, self));
yield return new AircraftMoveOrderTargeter(self, this);
}
}
}
public bool AircraftCanEnter(Actor a)
{
return !self.AppearsHostileTo(a) && Info.DockActors.Contains(a.Info.Name);
}
// Note: Returns a valid order even if the unit can't move to the target
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "Enter" || order.OrderID == "Move")
return new Order(order.OrderID, self, target, queued);
return null;
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (IsTraitDisabled)
return;
if (order.OrderString == "Move")
{
var cell = self.World.Map.Clamp(self.World.Map.CellContaining(order.Target.CenterPosition));
if (!Info.MoveIntoShroud && !self.Owner.Shroud.IsExplored(cell))
return;
var target = Target.FromCell(self.World, cell);
self.SetTargetLine(target, Color.Green);
}
else if (order.OrderString == "Enter")
{
// Enter and Repair orders are only valid for own/allied actors,
// which are guaranteed to never be frozen.
if (order.Target.Type != TargetType.Actor)
return;
var targetActor = order.Target.Actor;
// We only want to set a target line if the order will (most likely) succeed
if (Reservable.IsAvailableFor(targetActor, self))
self.SetTargetLine(Target.FromActor(targetActor), Color.Green);
}
var currentTransform = self.CurrentActivity as Transform;
var transform = transforms.FirstOrDefault(t => !t.IsTraitDisabled && !t.IsTraitPaused);
if (transform == null && currentTransform == null)
return;
var activity = currentTransform ?? transform.GetTransformActivity(self);
activity.Queue(self, new IssueOrderAfterTransform(order.OrderString, order.Target));
if (currentTransform == null)
self.QueueActivity(order.Queued, activity);
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
if (IsTraitDisabled)
return null;
switch (order.OrderString)
{
case "Move":
if (!Info.MoveIntoShroud && order.Target.Type != TargetType.Invalid)
{
var cell = self.World.Map.CellContaining(order.Target.CenterPosition);
if (!self.Owner.Shroud.IsExplored(cell))
return null;
}
return Info.Voice;
case "Enter":
return Info.Voice;
default: return null;
}
}
class AircraftMoveOrderTargeter : IOrderTargeter
{
readonly TransformsIntoAircraft aircraft;
public bool TargetOverridesSelection(TargetModifiers modifiers)
{
return modifiers.HasModifier(TargetModifiers.ForceMove);
}
public AircraftMoveOrderTargeter(Actor self, TransformsIntoAircraft aircraft)
{
this.aircraft = aircraft;
}
public string OrderID { get { return "Move"; } }
public int OrderPriority { get { return 4; } }
public bool IsQueued { get; protected set; }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Terrain || (aircraft.Info.RequiresForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)))
return false;
var location = self.World.Map.CellContaining(target.CenterPosition);
var explored = self.Owner.Shroud.IsExplored(location);
cursor = self.World.Map.Contains(location) ?
(self.World.Map.GetTerrainInfo(location).CustomCursor ?? "move") :
"move-blocked";
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
if (!explored && !aircraft.Info.MoveIntoShroud)
cursor = "move-blocked";
return true;
}
}
}
}

View File

@@ -0,0 +1,93 @@
#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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Add to a building to expose a move cursor that triggers Transforms and issues an enter tunnel order to the transformed actor.")]
public class TransformsIntoEntersTunnelsInfo : ConditionalTraitInfo, Requires<TransformsInfo>
{
public readonly string EnterCursor = "enter";
public readonly string EnterBlockedCursor = "enter-blocked";
[VoiceReference]
public readonly string Voice = "Action";
public override object Create(ActorInitializer init) { return new TransformsIntoEntersTunnels(this); }
}
public class TransformsIntoEntersTunnels : ConditionalTrait<TransformsIntoEntersTunnelsInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{
Transforms[] transforms;
public TransformsIntoEntersTunnels(TransformsIntoEntersTunnelsInfo info)
: base(info) { }
protected override void Created(Actor self)
{
transforms = self.TraitsImplementing<Transforms>().ToArray();
base.Created(self);
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
if (!IsTraitDisabled)
yield return new EntersTunnels.EnterTunnelOrderTargeter(Info.EnterCursor, Info.EnterBlockedCursor);
}
}
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "EnterTunnel")
return new Order(order.OrderID, self, target, queued) { SuppressVisualFeedback = true };
return null;
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (IsTraitDisabled || order.OrderString != "EnterTunnel" || order.Target.Type != TargetType.Actor)
return;
var tunnel = order.Target.Actor.TraitOrDefault<TunnelEntrance>();
if (tunnel == null || !tunnel.Exit.HasValue)
return;
var currentTransform = self.CurrentActivity as Transform;
var transform = transforms.FirstOrDefault(t => !t.IsTraitDisabled && !t.IsTraitPaused);
if (transform == null && currentTransform == null)
return;
self.SetTargetLine(order.Target, Color.Green);
var activity = currentTransform ?? transform.GetTransformActivity(self);
activity.Queue(self, new IssueOrderAfterTransform(order.OrderString, order.Target));
if (currentTransform == null)
self.QueueActivity(order.Queued, activity);
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
return order.OrderString == "EnterTunnel" ? Info.Voice : null;
}
}
}

View File

@@ -0,0 +1,192 @@
#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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Add to a building to expose a move cursor that triggers Transforms and issues a move order to the transformed actor.")]
public class TransformsIntoMobileInfo : ConditionalTraitInfo, Requires<TransformsInfo>
{
[LocomotorReference]
[FieldLoader.Require]
[Desc("Locomotor used by the transformed actor. Must be defined on the World actor.")]
public readonly string Locomotor = null;
public readonly string Cursor = "move";
public readonly string BlockedCursor = "move-blocked";
[VoiceReference]
public readonly string Voice = "Action";
[Desc("Require the force-move modifier to display the move cursor.")]
public readonly bool RequiresForceMove = false;
public override object Create(ActorInitializer init) { return new TransformsIntoMobile(init, this); }
public LocomotorInfo LocomotorInfo { get; private set; }
public override void RulesetLoaded(Ruleset rules, ActorInfo ai)
{
var locomotorInfos = rules.Actors["world"].TraitInfos<LocomotorInfo>();
LocomotorInfo = locomotorInfos.FirstOrDefault(li => li.Name == Locomotor);
if (LocomotorInfo == null)
throw new YamlException("A locomotor named '{0}' doesn't exist.".F(Locomotor));
else if (locomotorInfos.Count(li => li.Name == Locomotor) > 1)
throw new YamlException("There is more than one locomotor named '{0}'.".F(Locomotor));
base.RulesetLoaded(rules, ai);
}
}
public class TransformsIntoMobile : ConditionalTrait<TransformsIntoMobileInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{
readonly Actor self;
Transforms[] transforms;
public TransformsIntoMobile(ActorInitializer init, TransformsIntoMobileInfo info)
: base(info)
{
self = init.Self;
}
protected override void Created(Actor self)
{
transforms = self.TraitsImplementing<Transforms>().ToArray();
base.Created(self);
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
if (!IsTraitDisabled)
yield return new MoveOrderTargeter(self, this);
}
}
// Note: Returns a valid order even if the unit can't move to the target
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order is MoveOrderTargeter)
return new Order("Move", self, target, queued);
return null;
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (IsTraitDisabled)
return;
if (order.OrderString == "Move")
{
var cell = self.World.Map.Clamp(this.self.World.Map.CellContaining(order.Target.CenterPosition));
if (!Info.LocomotorInfo.MoveIntoShroud && !self.Owner.Shroud.IsExplored(cell))
return;
var currentTransform = self.CurrentActivity as Transform;
var transform = transforms.FirstOrDefault(t => !t.IsTraitDisabled && !t.IsTraitPaused);
if (transform == null && currentTransform == null)
return;
self.SetTargetLine(Target.FromCell(self.World, cell), Color.Green);
var activity = currentTransform ?? transform.GetTransformActivity(self);
activity.Queue(self, new IssueOrderAfterTransform("Move", order.Target));
if (currentTransform == null)
self.QueueActivity(order.Queued, activity);
}
if (order.OrderString == "Stop")
self.CancelActivity();
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
if (IsTraitDisabled)
return null;
switch (order.OrderString)
{
case "Move":
if (!Info.LocomotorInfo.MoveIntoShroud && order.Target.Type != TargetType.Invalid)
{
var cell = self.World.Map.CellContaining(order.Target.CenterPosition);
if (!self.Owner.Shroud.IsExplored(cell))
return null;
}
return Info.Voice;
case "Stop":
return Info.Voice;
default:
return null;
}
}
class MoveOrderTargeter : IOrderTargeter
{
readonly TransformsIntoMobile mobile;
readonly bool rejectMove;
public bool TargetOverridesSelection(TargetModifiers modifiers)
{
return modifiers.HasModifier(TargetModifiers.ForceMove);
}
public MoveOrderTargeter(Actor self, TransformsIntoMobile mobile)
{
this.mobile = mobile;
rejectMove = !self.AcceptsOrder("Move");
}
public string OrderID { get { return "Move"; } }
public int OrderPriority { get { return 4; } }
public bool IsQueued { get; protected set; }
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor)
{
if (rejectMove || target.Type != TargetType.Terrain || (mobile.Info.RequiresForceMove && !modifiers.HasModifier(TargetModifiers.ForceMove)))
return false;
var location = self.World.Map.CellContaining(target.CenterPosition);
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
var explored = self.Owner.Shroud.IsExplored(location);
cursor = self.World.Map.Contains(location) ?
(self.World.Map.GetTerrainInfo(location).CustomCursor ?? mobile.Info.Cursor) : mobile.Info.BlockedCursor;
var locomotor = mobile.Info.LocomotorInfo;
if (!mobile.transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused)
|| (!explored && !locomotor.MoveIntoShroud)
|| (explored && !CanEnterCell(self.World, self, location)))
cursor = mobile.Info.BlockedCursor;
return true;
}
bool CanEnterCell(World world, Actor self, CPos cell)
{
if (mobile.Info.LocomotorInfo.MovementCostForCell(world, cell) == int.MaxValue)
return false;
return mobile.Info.LocomotorInfo.CanMoveFreelyInto(world, self, cell, null, CellConditions.BlockedByMovers);
}
}
}
}

View File

@@ -0,0 +1,142 @@
#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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Add to a building to expose a move cursor that triggers Transforms and issues an EnterTransport order to the transformed actor.")]
public class TransformsIntoPassengerInfo : ConditionalTraitInfo, Requires<TransformsInfo>
{
public readonly string CargoType = null;
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;
[VoiceReference]
public readonly string Voice = "Action";
public override object Create(ActorInitializer init) { return new TransformsIntoPassenger(this); }
}
public class TransformsIntoPassenger : ConditionalTrait<TransformsIntoPassengerInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{
readonly IOrderTargeter[] orders;
Transforms[] transforms;
public TransformsIntoPassenger(TransformsIntoPassengerInfo info)
: base(info)
{
Func<Actor, bool> canTarget = IsCorrectCargoType;
Func<Actor, bool> useEnterCursor = CanEnter;
orders = new EnterAlliedActorTargeter<CargoInfo>[]
{
new EnterTransportTargeter("EnterTransport", 5, canTarget, useEnterCursor, Info.AlternateTransportsMode),
new EnterTransportsTargeter("EnterTransports", 5, canTarget, useEnterCursor, Info.AlternateTransportsMode)
};
}
protected override void Created(Actor self)
{
transforms = self.TraitsImplementing<Transforms>().ToArray();
base.Created(self);
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
return !IsTraitDisabled ? orders : Enumerable.Empty<IOrderTargeter>();
}
}
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "EnterTransport" || order.OrderID == "EnterTransports")
return new Order(order.OrderID, self, target, queued);
return null;
}
bool IsCorrectCargoType(Actor target)
{
var ci = target.Info.TraitInfo<CargoInfo>();
return ci.Types.Contains(Info.CargoType);
}
bool CanEnter(Actor target)
{
if (!transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused))
return false;
var cargo = target.TraitOrDefault<Cargo>();
return cargo != null && cargo.HasSpace(Info.Weight);
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (IsTraitDisabled)
return;
if (order.OrderString != "EnterTransport" && order.OrderString != "EnterTransports")
return;
// Enter orders are only valid for own/allied actors,
// which are guaranteed to never be frozen.
if (order.Target.Type != TargetType.Actor)
return;
var targetActor = order.Target.Actor;
if (!CanEnter(targetActor))
return;
if (!IsCorrectCargoType(targetActor))
return;
var currentTransform = self.CurrentActivity as Transform;
var transform = transforms.FirstOrDefault(t => !t.IsTraitDisabled && !t.IsTraitPaused);
if (transform == null && currentTransform == null)
return;
self.SetTargetLine(order.Target, Color.Green);
var activity = currentTransform ?? transform.GetTransformActivity(self);
activity.Queue(self, new IssueOrderAfterTransform(order.OrderString, order.Target));
if (currentTransform == null)
self.QueueActivity(order.Queued, activity);
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
if (IsTraitDisabled)
return null;
if (order.OrderString != "EnterTransport" && order.OrderString != "EnterTransports")
return null;
if (order.Target.Type != TargetType.Actor || !CanEnter(order.Target.Actor))
return null;
return Info.Voice;
}
}
}

View File

@@ -0,0 +1,110 @@
#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;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Add to a building to expose a move cursor that triggers Transforms and issues a repair order to the transformed actor.")]
public class TransformsIntoRepairableInfo : ConditionalTraitInfo, Requires<TransformsInfo>, Requires<IHealthInfo>
{
[ActorReference]
[FieldLoader.Require]
public readonly HashSet<string> RepairActors = new HashSet<string> { };
[VoiceReference]
public readonly string Voice = "Action";
public override object Create(ActorInitializer init) { return new TransformsIntoRepairable(this); }
}
public class TransformsIntoRepairable : ConditionalTrait<TransformsIntoRepairableInfo>, IIssueOrder, IResolveOrder, IOrderVoice
{
Transforms[] transforms;
IHealth health;
public TransformsIntoRepairable(TransformsIntoRepairableInfo info)
: base(info) { }
protected override void Created(Actor self)
{
transforms = self.TraitsImplementing<Transforms>().ToArray();
health = self.Trait<IHealth>();
base.Created(self);
}
IEnumerable<IOrderTargeter> IIssueOrder.Orders
{
get
{
if (!IsTraitDisabled)
yield return new EnterAlliedActorTargeter<BuildingInfo>("Repair", 5, CanRepairAt, _ => CanRepair());
}
}
bool CanRepair()
{
return health.DamageState > DamageState.Undamaged && transforms.Any(t => !t.IsTraitDisabled && !t.IsTraitPaused);
}
bool CanRepairAt(Actor target)
{
return Info.RepairActors.Contains(target.Info.Name);
}
Order IIssueOrder.IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "Repair")
return new Order(order.OrderID, self, target, queued);
return null;
}
void IResolveOrder.ResolveOrder(Actor self, Order order)
{
if (IsTraitDisabled || order.OrderString != "Repair")
return;
// Repair orders are only valid for own/allied actors,
// which are guaranteed to never be frozen.
if (order.Target.Type != TargetType.Actor)
return;
if (!CanRepairAt(order.Target.Actor) || !CanRepair())
return;
var currentTransform = self.CurrentActivity as Transform;
var transform = transforms.FirstOrDefault(t => !t.IsTraitDisabled && !t.IsTraitPaused);
if (transform == null && currentTransform == null)
return;
self.SetTargetLine(order.Target, Color.Green);
var activity = currentTransform ?? transform.GetTransformActivity(self);
activity.Queue(self, new IssueOrderAfterTransform(order.OrderString, order.Target));
if (currentTransform == null)
self.QueueActivity(order.Queued, activity);
}
string IOrderVoice.VoicePhraseForOrder(Actor self, Order order)
{
return order.OrderString == "Repair" && !IsTraitDisabled && CanRepair() ? Info.Voice : null;
}
}
}

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Mods.Common.Traits
{
get
{
yield return new EnterTunnelOrderTargeter(info);
yield return new EnterTunnelOrderTargeter(info.EnterCursor, info.EnterBlockedCursor);
}
}
@@ -78,14 +78,16 @@ namespace OpenRA.Mods.Common.Traits
self.QueueActivity(move.MoveTo(tunnel.Exit.Value, tunnel.NearEnough));
}
class EnterTunnelOrderTargeter : UnitOrderTargeter
public class EnterTunnelOrderTargeter : UnitOrderTargeter
{
readonly EntersTunnelsInfo info;
readonly string enterCursor;
readonly string enterBlockedCursor;
public EnterTunnelOrderTargeter(EntersTunnelsInfo info)
: base("EnterTunnel", 6, info.EnterCursor, true, true)
public EnterTunnelOrderTargeter(string enterCursor, string enterBlockedCursor)
: base("EnterTunnel", 6, enterCursor, true, true)
{
this.info = info;
this.enterCursor = enterCursor;
this.enterBlockedCursor = enterBlockedCursor;
}
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
@@ -109,11 +111,11 @@ namespace OpenRA.Mods.Common.Traits
if (!tunnel.Exit.HasValue)
{
cursor = info.EnterBlockedCursor;
cursor = enterBlockedCursor;
return false;
}
cursor = info.EnterCursor;
cursor = enterCursor;
return true;
}

View File

@@ -10,6 +10,7 @@
#endregion
using System.Collections.Generic;
using OpenRA.Activities;
using OpenRA.Mods.Common.Activities;
using OpenRA.Mods.Common.Orders;
using OpenRA.Traits;
@@ -85,6 +86,18 @@ namespace OpenRA.Mods.Common.Traits
return buildingInfo == null || self.World.CanPlaceBuilding(self.Location + Info.Offset, actorInfo, buildingInfo, self);
}
public Activity GetTransformActivity(Actor self)
{
return new Transform(self, Info.IntoActor)
{
Offset = Info.Offset,
Facing = Info.Facing,
Sounds = Info.TransformSounds,
Notification = Info.TransformNotification,
Faction = faction
};
}
public IEnumerable<IOrderTargeter> Orders
{
get
@@ -127,14 +140,7 @@ namespace OpenRA.Mods.Common.Traits
if (!queued)
self.CancelActivity();
self.QueueActivity(new Transform(self, Info.IntoActor)
{
Offset = Info.Offset,
Facing = Info.Facing,
Sounds = Info.TransformSounds,
Notification = Info.TransformNotification,
Faction = faction
});
self.QueueActivity(GetTransformActivity(self));
}
public void ResolveOrder(Actor self, Order order)

View File

@@ -20,11 +20,21 @@ FACT:
Production:
Produces: Building.GDI, Building.Nod, Defence.GDI, Defence.Nod
Transforms:
RequiresCondition: factundeploy && !build-incomplete
PauseOnCondition: being-demolished
RequiresCondition: factundeploy
PauseOnCondition: being-demolished || build-incomplete
IntoActor: mcv
Offset: 1,1
Facing: 108
TransformsIntoMobile:
RequiresCondition: factundeploy
Locomotor: heavywheeled
RequiresForceMove: true
TransformsIntoPassenger:
RequiresCondition: factundeploy
CargoType: Vehicle
TransformsIntoRepairable:
RequiresCondition: factundeploy
RepairActors: fix
GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY:
Condition: factundeploy
Prerequisites: global-factundeploy

View File

@@ -1137,11 +1137,21 @@ FACT:
ActorTypes: e1,e1,e1,tecn,tecn,e6
BaseBuilding:
Transforms:
RequiresCondition: factundeploy && !build-incomplete
PauseOnCondition: chrono-vortex || being-captured || being-demolished
RequiresCondition: factundeploy
PauseOnCondition: chrono-vortex || being-captured || being-demolished || build-incomplete
IntoActor: mcv
Offset: 1,1
Facing: 96
TransformsIntoMobile:
RequiresCondition: factundeploy
Locomotor: heavywheeled
RequiresForceMove: true
TransformsIntoPassenger:
RequiresCondition: factundeploy
CargoType: Vehicle
TransformsIntoRepairable:
RequiresCondition: factundeploy
RepairActors: fix
Sellable:
RequiresCondition: !build-incomplete && !chrono-vortex && !being-captured && !being-demolished
GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY:

View File

@@ -28,12 +28,28 @@ GACNST:
Value: 2500
BaseBuilding:
Transforms:
RequiresCondition: factundeploy && !build-incomplete
PauseOnCondition: empdisable || being-demolished
RequiresCondition: factundeploy
PauseOnCondition: empdisable || being-demolished || build-incomplete
IntoActor: mcv
Offset: 1,1
Facing: 96
DeployCursor: undeploy
TransformsIntoMobile:
RequiresCondition: factundeploy
Locomotor: tracked
Voice: Move
RequiresForceMove: true
TransformsIntoRepairable:
RequiresCondition: factundeploy
RepairActors: gadept
Voice: Move
TransformsIntoEntersTunnels:
RequiresCondition: factundeploy
Voice: Move
TransformsIntoPassenger:
RequiresCondition: factundeploy
CargoType: Vehicle
Voice: Move
GrantConditionOnPrerequisite@GLOBALFACTUNDEPLOY:
Condition: factundeploy
Prerequisites: global-factundeploy