Add TransformsInto* traits to trigger construction yard undeploy.
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
184
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs
Normal file
184
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoAircraft.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
192
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs
Normal file
192
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoMobile.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
142
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs
Normal file
142
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoPassenger.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
110
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs
Normal file
110
OpenRA.Mods.Common/Traits/Buildings/TransformsIntoRepairable.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user