Merge pull request #3648 from pchote/frozen-fog-rework

Rework frozen actors to support tooltips and orders
This commit is contained in:
Matthias Mailänder
2013-08-12 04:19:21 -07:00
62 changed files with 991 additions and 495 deletions

View File

@@ -33,7 +33,7 @@ namespace OpenRA.Mods.RA.Activities
public Attack(Target target, WRange range, bool allowMovement)
{
Target = target;
if (target.IsActor)
if (target.Type == TargetType.Actor)
targetable = target.Actor.TraitOrDefault<ITargetable>();
Range = range;
@@ -53,10 +53,11 @@ namespace OpenRA.Mods.RA.Activities
if (IsCanceled)
return NextActivity;
if (!Target.IsValid)
var type = Target.Type;
if (type != TargetType.Actor && type != TargetType.Terrain)
return NextActivity;
if (!self.Owner.HasFogVisibility() && Target.Actor != null && Target.Actor.HasTrait<Mobile>() && !self.Owner.Shroud.IsTargetable(Target.Actor))
if (type == TargetType.Actor && !self.Owner.HasFogVisibility() && Target.Actor.HasTrait<Mobile>() && !self.Owner.Shroud.IsTargetable(Target.Actor))
return NextActivity;
if (targetable != null && !targetable.TargetableBy(Target.Actor, self))

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (!target.IsValid)
if (target.Type != TargetType.Actor)
return NextActivity;
var capturable = target.Actor.Trait<Capturable>();

View File

@@ -27,13 +27,13 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
if (IsCanceled || target.Type != TargetType.Actor)
return NextActivity;
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>
{
// Can't demolish an already dead actor
if (!target.IsValid)
if (target.Type != TargetType.Actor)
return;
// Invulnerable actors can't be demolished

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid || !target.IsActor)
if (IsCanceled || target.Type != TargetType.Actor)
return NextActivity;
var targetActor = target.Actor;

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
if (IsCanceled || target.Type != TargetType.Actor)
return NextActivity;
if (!Util.AdjacentCells(target).Any(c => c == self.Location))

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Activities
protected override Activity InnerTick(Actor self, AttackBase attack)
{
if (Target.IsActor && Target.Actor.GetDamageState() == DamageState.Undamaged)
if (Target.Type == TargetType.Actor && Target.Actor.GetDamageState() == DamageState.Undamaged)
return NextActivity;
return base.InnerTick(self, attack);

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid || target.Actor.Owner == self.Owner)
if (IsCanceled || target.Type != TargetType.Actor || target.Actor.Owner == self.Owner)
return NextActivity;
foreach (var t in target.Actor.TraitsImplementing<IAcceptInfiltrator>())

View File

@@ -9,9 +9,9 @@
#endregion
using System.Linq;
using OpenRA.Traits;
using OpenRA.Mods.RA.Move;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Activities
{
@@ -23,31 +23,30 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled)
return NextActivity;
if (!target.IsValid)
if (IsCanceled || target.Type != TargetType.Actor)
return NextActivity;
var b = target.Actor.TraitOrDefault<Building>();
var actor = target.Actor;
var b = actor.TraitOrDefault<Building>();
if (b != null && b.Locked)
return NextActivity;
var capturesInfo = self.Info.Traits.Get<LegacyCapturesInfo>();
var capturableInfo = target.Actor.Info.Traits.Get<LegacyCapturableInfo>();
var capturableInfo = actor.Info.Traits.Get<LegacyCapturableInfo>();
var health = target.Actor.Trait<Health>();
var health = actor.Trait<Health>();
self.World.AddFrameEndTask(w =>
{
var lowEnoughHealth = health.HP <= capturableInfo.CaptureThreshold * health.MaxHP;
if (!capturesInfo.Sabotage || lowEnoughHealth || target.Actor.Owner.NonCombatant)
if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant)
{
var oldOwner = target.Actor.Owner;
var oldOwner = actor.Owner;
target.Actor.ChangeOwner(self.Owner);
actor.ChangeOwner(self.Owner);
foreach (var t in target.Actor.TraitsImplementing<INotifyCapture>())
t.OnCapture(target.Actor, self, oldOwner, self.Owner);
foreach (var t in actor.TraitsImplementing<INotifyCapture>())
t.OnCapture(actor, self, oldOwner, self.Owner);
if (b != null && b.Locked)
b.Unlock();
@@ -55,7 +54,7 @@ namespace OpenRA.Mods.RA.Activities
else
{
var damage = (int)(health.MaxHP * capturesInfo.SabotageHPRemoval);
target.Actor.InflictDamage(self, damage, null);
actor.InflictDamage(self, damage, null);
}
self.Destroy();

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
if (IsCanceled || target.Type != TargetType.Actor)
return NextActivity;
var hut = target.Actor.Trait<BridgeHut>();

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self)
{
if (IsCanceled || !target.IsValid)
if (IsCanceled || target.Type != TargetType.Actor)
return NextActivity;
var health = target.Actor.Trait<Health>();

View File

@@ -9,6 +9,7 @@
#endregion
using System;
using System.Drawing;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -44,6 +45,44 @@ namespace OpenRA.Mods.RA
return stance == Stance.Enemy;
}
public static Target ResolveFrozenActorOrder(this Actor self, Order order, Color targetLine)
{
// Not targeting a frozen actor
if (order.ExtraData == 0)
return Target.FromOrder(order);
// Targeted an actor under the fog
var frozenLayer = self.Owner.PlayerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
return Target.Invalid;
var frozen = frozenLayer.FromID(order.ExtraData);
if (frozen == null)
return Target.Invalid;
// Flashes the frozen proxy
self.SetTargetLine(frozen, targetLine, true);
// Target is still alive - resolve the real order
if (frozen.Actor != null && frozen.Actor.IsInWorld)
return Target.FromActor(frozen.Actor);
if (!order.Queued)
self.CancelActivity();
var move = self.TraitOrDefault<IMove>();
if (move != null)
{
// Move within sight range of the frozen actor
var sight = self.TraitOrDefault<RevealsShroud>();
var range = sight != null ? sight.Range : 2;
self.QueueActivity(move.MoveWithinRange(Target.FromPos(frozen.CenterPosition), WRange.FromCells(range)));
}
return Target.Invalid;
}
}
}

View File

@@ -166,7 +166,7 @@ namespace OpenRA.Mods.RA.Air
{
get
{
yield return new EnterOrderTargeter<Building>("Enter", 5, false, true,
yield return new EnterAlliedActorTargeter<Building>("Enter", 5,
target => AircraftCanEnter(target), target => !Reservable.IsReserved(target));
yield return new AircraftMoveOrderTargeter();
@@ -217,15 +217,13 @@ namespace OpenRA.Mods.RA.Air
public string OrderID { get { return "Move"; } }
public int OrderPriority { get { return 4; } }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
return false;
}
if (target.Type != TargetType.Terrain)
return false;
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
cursor = self.World.Map.IsInMap(location) ? "move" : "move-blocked";
cursor = self.World.Map.IsInMap(target.CenterPosition.ToCPos()) ? "move" : "move-blocked";
return true;
}

View File

@@ -54,8 +54,8 @@ namespace OpenRA.Mods.RA
if (self.IsDisabled())
return false;
if (target.IsActor && target.Actor.HasTrait<ITargetable>() &&
!target.Actor.Trait<ITargetable>().TargetableBy(target.Actor,self))
if (target.Type == TargetType.Actor && target.Actor.HasTrait<ITargetable>() &&
!target.Actor.Trait<ITargetable>().TargetableBy(target.Actor, self))
return false;
return true;
@@ -76,6 +76,7 @@ namespace OpenRA.Mods.RA
x.Second();
delayedActions[i] = x;
}
delayedActions.RemoveAll(a => a.First <= 0);
}
@@ -104,8 +105,7 @@ namespace OpenRA.Mods.RA
if (Armaments.Count() == 0)
yield break;
var negativeDamage = (Armaments.First().Weapon.Warheads[0].Damage < 0);
var negativeDamage = Armaments.First().Weapon.Warheads[0].Damage < 0;
yield return new AttackOrderTargeter("Attack", 6, negativeDamage);
}
}
@@ -114,33 +114,42 @@ namespace OpenRA.Mods.RA
{
if (order is AttackOrderTargeter)
{
if (target.IsActor)
switch (target.Type)
{
case TargetType.Actor:
return new Order("Attack", self, queued) { TargetActor = target.Actor };
else
case TargetType.FrozenActor:
return new Order("Attack", self, queued) { ExtraData = target.FrozenActor.ID };
case TargetType.Terrain:
return new Order("Attack", self, queued) { TargetLocation = target.CenterPosition.ToCPos() };
}
}
return null;
}
public virtual void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Attack" || order.OrderString == "AttackHold")
if (order.OrderString == "Attack")
{
var target = Target.FromOrder(order);
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (!target.IsValid)
return;
self.SetTargetLine(target, Color.Red);
AttackTarget(target, order.Queued, order.OrderString == "Attack");
AttackTarget(target, order.Queued, true);
}
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "Attack" || order.OrderString == "AttackHold") ? "Attack" : null;
return order.OrderString == "Attack" ? "Attack" : null;
}
public abstract Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove);
public bool HasAnyValidWeapons(Target t) { return Armaments.Any(a => a.Weapon.IsValidAgainst(t, self.World)); }
public WRange GetMaximumRange() { return new WRange((int)(1024*Armaments.Max(a => a.Weapon.Range))); }
public WRange GetMaximumRange() { return new WRange((int)(1024 * Armaments.Max(a => a.Weapon.Range))); }
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World)); }
@@ -169,16 +178,16 @@ namespace OpenRA.Mods.RA
public string OrderID { get; private set; }
public int OrderPriority { get; private set; }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
bool CanTargetActor(Actor self, Target target, TargetModifiers modifiers, ref string cursor)
{
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
cursor = self.Info.Traits.Get<AttackBaseInfo>().Cursor;
if (self == target)
if (target.Type == TargetType.Actor && target.Actor == self)
return false;
if (!self.Trait<AttackBase>().HasAnyValidWeapons(Target.FromActor(target)))
if (!self.Trait<AttackBase>().HasAnyValidWeapons(target))
return false;
if (modifiers.HasModifier(TargetModifiers.ForceAttack))
@@ -189,10 +198,11 @@ namespace OpenRA.Mods.RA
var targetableRelationship = negativeDamage ? Stance.Ally : Stance.Enemy;
return self.Owner.Stances[target.Owner] == targetableRelationship;
var owner = target.Type == TargetType.FrozenActor ? target.FrozenActor.Owner : target.Actor.Owner;
return self.Owner.Stances[owner] == targetableRelationship;
}
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
if (!self.World.Map.IsInMap(location))
return false;
@@ -214,6 +224,20 @@ namespace OpenRA.Mods.RA
return false;
}
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
switch (target.Type)
{
case TargetType.Actor:
case TargetType.FrozenActor:
return CanTargetActor(self, target, modifiers, ref cursor);
case TargetType.Terrain:
return CanTargetLocation(self, target.CenterPosition.ToCPos(), othersAtTarget, modifiers, ref cursor);
default:
return false;
}
}
public bool IsQueued { get; protected set; }
}
}

View File

@@ -37,7 +37,7 @@ namespace OpenRA.Mods.RA
public override void DoAttack(Actor self, Target target)
{
if (!CanAttack(self, target) || !target.IsActor)
if (target.Type != TargetType.Actor || !CanAttack(self, target))
return;
var a = ChooseArmamentForTarget(target);

View File

@@ -67,7 +67,7 @@ namespace OpenRA.Mods.RA
base.ResolveOrder(self, order);
if (order.OrderString == "Stop")
target = Target.None;
target = Target.Invalid;
}
public virtual void BuildingComplete(Actor self) { buildComplete = true; }

View File

@@ -43,7 +43,8 @@ namespace OpenRA.Mods.RA
public UnitStance stance;
[Sync] public int stanceNumber { get { return (int)stance; } }
public UnitStance predictedStance; /* NOT SYNCED: do not refer to this anywhere other than UI code */
[Sync] public int AggressorID;
[Sync] public Actor Aggressor;
[Sync] public Actor TargetedActor;
public AutoTarget(Actor self, AutoTargetInfo info)
{
@@ -75,7 +76,7 @@ namespace OpenRA.Mods.RA
if (e.Damage < 0) return; // don't retaliate against healers
AggressorID = (int)e.Attacker.ActorID;
Aggressor = e.Attacker;
attack.AttackTarget(Target.FromActor(e.Attacker), false, Info.AllowMovement && stance != UnitStance.Defend);
}
@@ -87,6 +88,8 @@ namespace OpenRA.Mods.RA
var target = ScanForTarget(self, null);
if (target != null)
{
TargetedActor = target;
var t = Target.FromActor(target);
self.SetTargetLine(t, Color.Red, false);
attack.AttackTarget(t, false, Info.AllowMovement && stance != UnitStance.Defend);
@@ -113,7 +116,10 @@ namespace OpenRA.Mods.RA
{
var targetActor = ScanForTarget(self, null);
if (targetActor != null)
{
TargetedActor = targetActor;
attack.AttackTarget(Target.FromActor(targetActor), false, Info.AllowMovement && stance != UnitStance.Defend);
}
}
Actor ChooseTarget(Actor self, WRange range)

View File

@@ -26,11 +26,11 @@ namespace OpenRA.Mods.RA
class C4Demolition : IIssueOrder, IResolveOrder, IOrderVoice
{
readonly C4DemolitionInfo Info;
readonly C4DemolitionInfo info;
public C4Demolition(C4DemolitionInfo info)
{
Info = info;
this.info = info;
}
public IEnumerable<IOrderTargeter> Orders
@@ -38,28 +38,36 @@ namespace OpenRA.Mods.RA
get { yield return new TargetTypeOrderTargeter("C4", "C4", 6, "c4", true, false); }
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "C4")
return new Order("C4", self, queued) { TargetActor = target.Actor };
if (order.OrderID != "C4")
return null;
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "C4")
{
self.SetTargetLine(Target.FromOrder(order), Color.Red);
if (order.OrderString != "C4")
return;
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new Demolish(order.TargetActor, Info.C4Delay)));
}
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new Enter(target.Actor, new Demolish(target.Actor, info.C4Delay)));
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "C4") ? "Attack" : null;
return order.OrderString == "C4" ? "Attack" : null;
}
}
}

View File

@@ -28,6 +28,28 @@ namespace OpenRA.Mods.RA
[Desc("Seconds it takes to change the owner.", "You might want to add a CapturableBar: trait, too.")]
public readonly int CaptureCompleteTime = 15;
public bool CanBeTargetedBy(Actor captor, Player owner)
{
var c = captor.TraitOrDefault<Captures>();
if (c == null)
return false;
var playerRelationship = owner.Stances[captor.Owner];
if (playerRelationship == Stance.Ally && !AllowAllies)
return false;
if (playerRelationship == Stance.Enemy && !AllowEnemies)
return false;
if (playerRelationship == Stance.Neutral && !AllowNeutral)
return false;
if (!c.Info.CaptureTypes.Contains(Type))
return false;
return true;
}
public object Create(ActorInitializer init) { return new Capturable(init.self, this); }
}
@@ -45,31 +67,6 @@ namespace OpenRA.Mods.RA
Info = info;
}
public bool CanBeTargetedBy(Actor captor)
{
var c = captor.TraitOrDefault<Captures>();
if (c == null)
return false;
var playerRelationship = self.Owner.Stances[captor.Owner];
if (playerRelationship == Stance.Ally && !Info.AllowAllies)
return false;
if (playerRelationship == Stance.Enemy && !Info.AllowEnemies)
return false;
if (playerRelationship == Stance.Neutral && !Info.AllowNeutral)
return false;
if (!c.Info.CaptureTypes.Contains(Info.Type))
return false;
if (CaptureInProgress)
return false;
return true;
}
public void BeginCapture(Actor captor)
{
var building = self.TraitOrDefault<Building>();

View File

@@ -12,11 +12,11 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
using OpenRA.FileFormats;
namespace OpenRA.Mods.RA
{
@@ -34,11 +34,9 @@ namespace OpenRA.Mods.RA
class Captures : IIssueOrder, IResolveOrder, IOrderVoice
{
public readonly CapturesInfo Info;
readonly Actor self;
public Captures(Actor self, CapturesInfo info)
{
this.self = self;
Info = info;
}
@@ -46,69 +44,89 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new CaptureOrderTargeter(CanCapture);
yield return new CaptureOrderTargeter();
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "CaptureActor")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
if (order.OrderID != "CaptureActor")
return null;
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
bool IsValidOrder(Actor self, Order order)
{
// Not targeting an actor
if (order.ExtraData == 0 && order.TargetActor == null)
return false;
if (order.ExtraData != 0)
{
// Targeted an actor under the fog
var frozenLayer = self.Owner.PlayerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
return false;
var frozen = frozenLayer.FromID(order.ExtraData);
if (frozen == null)
return false;
var ci = frozen.Info.Traits.GetOrDefault<CapturableInfo>();
return ci != null && ci.CanBeTargetedBy(self, frozen.Owner);
}
var c = order.TargetActor.TraitOrDefault<Capturable>();
return c != null && !c.CaptureInProgress && c.Info.CanBeTargetedBy(self, order.TargetActor.Owner);
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "CaptureActor" && CanCapture(order.TargetActor)) ? "Attack" : null;
return order.OrderString == "CaptureActor" && IsValidOrder(self, order)
? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "CaptureActor")
{
if (!CanCapture(order.TargetActor))
return;
if (order.OrderString != "CaptureActor" || !IsValidOrder(self, order))
return;
self.SetTargetLine(Target.FromOrder(order), Color.Red);
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new CaptureActor(Target.FromOrder(order)));
}
}
bool CanCapture(Actor target)
{
var c = target.TraitOrDefault<Capturable>();
return c != null && c.CanBeTargetedBy(self);
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new CaptureActor(target));
}
}
class CaptureOrderTargeter : UnitOrderTargeter
{
readonly Func<Actor, bool> useCaptureCursor;
public CaptureOrderTargeter(Func<Actor, bool> useCaptureCursor)
: base("CaptureActor", 6, "enter", true, true)
{
this.useCaptureCursor = useCaptureCursor;
}
public CaptureOrderTargeter() : base("CaptureActor", 6, "enter", true, true) { }
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
return false;
var c = target.TraitOrDefault<Capturable>();
var canTargetActor = useCaptureCursor(target);
var canTargetActor = c != null && !c.CaptureInProgress && c.Info.CanBeTargetedBy(self, target.Owner);
cursor = canTargetActor ? "ability" : "move-blocked";
return canTargetActor;
}
if (canTargetActor)
{
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var c = target.Info.Traits.GetOrDefault<CapturableInfo>();
return false;
var canTargetActor = c != null && c.CanBeTargetedBy(self, target.Owner);
cursor = canTargetActor ? "ability" : "move-blocked";
return canTargetActor;
}
}
}

View File

@@ -118,7 +118,7 @@ namespace OpenRA.Mods.RA
foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0]))
foreach (var unit in world.FindActorsInBox(t, t))
unit.InflictDamage(firedBy,
(int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead);
(int)(warhead.Damage * warhead.EffectivenessAgainst(unit.Info)), warhead);
} break;
}
}
@@ -173,7 +173,7 @@ namespace OpenRA.Mods.RA
var distance = (int)Math.Max(0, (target.CenterPosition - pos).Length * Game.CellSize / 1024 - health.Radius);
var falloff = (float)GetDamageFalloff(distance / warhead.Spread);
var rawDamage = (float)(warhead.Damage * modifier * falloff);
var multiplier = (float)warhead.EffectivenessAgainst(target);
var multiplier = (float)warhead.EffectivenessAgainst(target.Info);
return (float)(rawDamage * multiplier);
}

View File

@@ -17,8 +17,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
// Exception when overriding Chronoshift event; removed for now, will look into it.
class DemoTruckInfo : TraitInfo<DemoTruck>, Requires<ExplodesInfo> {}
class DemoTruckInfo : TraitInfo<DemoTruck>, Requires<ExplodesInfo> { }
class DemoTruck : IIssueOrder, IResolveOrder, IOrderVoice
{
@@ -41,10 +40,13 @@ namespace OpenRA.Mods.RA
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "DemoAttack" || order.OrderID == "DemoDeploy")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
if (order.OrderID != "DemoAttack" && order.OrderID != "DemoDeploy")
return null;
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
public string VoicePhraseForOrder(Actor self, Order order)
@@ -56,13 +58,18 @@ namespace OpenRA.Mods.RA
{
if (order.OrderString == "DemoAttack")
{
self.SetTargetLine(Target.FromOrder(order), Color.Red);
self.World.AddFrameEndTask(w =>
{
self.QueueActivity(new MoveAdjacentTo(Target.FromOrder(order)));
self.QueueActivity(new CallFunc(() => Explode(self)));
});
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new MoveAdjacentTo(target));
self.QueueActivity(new CallFunc(() => Explode(self)));
}
if (order.OrderString == "DemoDeploy")
Explode(self);
}

View File

@@ -1,53 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 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. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Effects
{
public class FrozenActorProxy : IEffect
{
readonly Actor self;
readonly IEnumerable<CPos> footprint;
IRenderable[] renderables;
public FrozenActorProxy(Actor self, IEnumerable<CPos> footprint)
{
this.self = self;
this.footprint = footprint;
}
public void Tick(World world) { }
public void SetRenderables(IEnumerable<IRenderable> r)
{
renderables = r.Select(rr => rr).ToArray();
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (renderables == null)
return SpriteRenderable.None;
if (footprint.Any(c => !wr.world.FogObscures(c)))
{
if (self.Destroyed)
self.World.AddFrameEndTask(w => w.Remove(this));
return SpriteRenderable.None;
}
return renderables;
}
}
}

View File

@@ -28,28 +28,58 @@ namespace OpenRA.Mods.RA
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "EngineerRepair")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
if (order.OrderID != "EngineerRepair")
return null;
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
bool IsValidOrder(Actor self, Order order)
{
// Not targeting a frozen actor
if (order.ExtraData == 0 && order.TargetActor == null)
return false;
if (order.ExtraData != 0)
{
// Targeted an actor under the fog
var frozenLayer = self.Owner.PlayerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
return false;
var frozen = frozenLayer.FromID(order.ExtraData);
if (frozen == null)
return false;
return frozen.DamageState > DamageState.Undamaged;
}
return order.TargetActor.GetDamageState() > DamageState.Undamaged;
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "EngineerRepair" &&
order.TargetActor.GetDamageState() > DamageState.Undamaged) ? "Attack" : null;
return order.OrderString == "EngineerRepair" && IsValidOrder(self, order)
? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "EngineerRepair" &&
order.TargetActor.GetDamageState() > DamageState.Undamaged)
{
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
if (order.OrderString != "EngineerRepair" || !IsValidOrder(self, order))
return;
var target = self.ResolveFrozenActorOrder(order, Color.Yellow);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new RepairBuilding(order.TargetActor)));
}
self.SetTargetLine(target, Color.Yellow);
self.QueueActivity(new Enter(target.Actor, new RepairBuilding(target.Actor)));
}
class EngineerRepairOrderTargeter : UnitOrderTargeter
@@ -59,22 +89,31 @@ namespace OpenRA.Mods.RA
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
return false;
if (!target.HasTrait<EngineerRepairable>())
return false;
if (self.Owner.Stances[target.Owner] != Stance.Ally)
return false;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
if (target.GetDamageState() == DamageState.Undamaged)
cursor = "goldwrench-blocked";
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
if (!target.Info.Traits.Contains<EngineerRepairable>())
return false;
if (self.Owner.Stances[target.Owner] != Stance.Ally)
return false;
if (target.DamageState == DamageState.Undamaged)
cursor = "goldwrench-blocked";
return true;
}
}
}

View File

@@ -239,7 +239,8 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new EnterOrderTargeter<IAcceptOre>("Deliver", 5, false, true, _ => true, proc => !IsEmpty && proc.Trait<IAcceptOre>().AllowDocking);
yield return new EnterAlliedActorTargeter<IAcceptOre>("Deliver", 5, _ => true,
proc => !IsEmpty && proc.Trait<IAcceptOre>().AllowDocking);
yield return new HarvestOrderTargeter();
}
}
@@ -419,16 +420,15 @@ namespace OpenRA.Mods.RA
public int OrderPriority { get { return 10; } }
public bool IsQueued { get; protected set; }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
return false;
}
if (target.Type != TargetType.Terrain)
return false;
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
if (modifiers.HasModifier(TargetModifiers.ForceMove))
return false;
var location = target.CenterPosition.ToCPos();
// Don't leak info about resources under the shroud
if (!self.Owner.Shroud.IsExplored(location))
return false;

View File

@@ -41,67 +41,96 @@ namespace OpenRA.Mods.RA
Info = info;
}
public IEnumerable<IOrderTargeter> Orders
{
get
{
yield return new InfiltratorOrderTargeter(CanInfiltrate);
}
}
public IEnumerable<IOrderTargeter> Orders { get { yield return new InfiltratorOrderTargeter(Info.Types); } }
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "Infiltrate")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
if (order.OrderID != "Infiltrate")
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return null;
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
bool IsValidOrder(Actor self, Order order)
{
// Not targeting an actor
if (order.ExtraData == 0 && order.TargetActor == null)
return false;
if (order.ExtraData != 0)
{
// Targeted an actor under the fog
var frozenLayer = self.Owner.PlayerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
return false;
var frozen = frozenLayer.FromID(order.ExtraData);
if (frozen == null)
return false;
var ii = frozen.Info.Traits.GetOrDefault<InfiltratableInfo>();
return ii != null && Info.Types.Contains(ii.Type);
}
var i = order.TargetActor.Info.Traits.GetOrDefault<InfiltratableInfo>();
return i != null && Info.Types.Contains(i.Type);
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "Infiltrate" && CanInfiltrate(order.TargetActor)) ? "Attack" : null;
return order.OrderString == "Infiltrate" && IsValidOrder(self, order)
? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Infiltrate")
{
if (!CanInfiltrate(order.TargetActor))
return;
if (order.OrderString != "Infiltrate" || !IsValidOrder(self, order))
return;
self.SetTargetLine(Target.FromOrder(order), Color.Red);
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new Infiltrate(order.TargetActor)));
}
}
bool CanInfiltrate(Actor target)
{
var infiltratable = target.Info.Traits.GetOrDefault<InfiltratableInfo>();
return infiltratable != null && Info.Types.Contains(infiltratable.Type);
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new Enter(target.Actor, new Infiltrate(target.Actor)));
}
class InfiltratorOrderTargeter : UnitOrderTargeter
{
readonly Func<Actor, bool> useEnterCursor;
public InfiltratorOrderTargeter(Func<Actor, bool> useEnterCursor)
string[] infiltrationTypes;
public InfiltratorOrderTargeter(string[] infiltrationTypes)
: base("Infiltrate", 7, "enter", true, false)
{
ForceAttack = false;
this.useEnterCursor = useEnterCursor;
this.infiltrationTypes = infiltrationTypes;
}
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
var info = target.Info.Traits.GetOrDefault<InfiltratableInfo>();
if (info == null)
return false;
if (!target.HasTrait<IAcceptInfiltrator>())
if (!infiltrationTypes.Contains(info.Type))
cursor = "enter-blocked";
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var info = target.Info.Traits.GetOrDefault<InfiltratableInfo>();
if (info == null)
return false;
if (!useEnterCursor(target))
if (!infiltrationTypes.Contains(info.Type))
cursor = "enter-blocked";
return true;

View File

@@ -15,7 +15,7 @@ using OpenRA.FileFormats;
namespace OpenRA.Mods.RA
{
[Desc("This actor can be captured by a unit with LegacyCaptures: trait.")]
class LegacyCapturableInfo : ITraitInfo
class LegacyCapturableInfo : TraitInfo<LegacyCapturable>
{
[Desc("Type of actor (the LegacyCaptures: trait defines what Types it can capture).")]
public readonly string Type = "building";
@@ -25,40 +25,28 @@ namespace OpenRA.Mods.RA
[Desc("Health percentage the target must be at (or below) before it can be captured.")]
public readonly float CaptureThreshold = 0.5f;
public object Create(ActorInitializer init) { return new LegacyCapturable(init.self, this); }
}
class LegacyCapturable
{
[Sync] Actor self;
public LegacyCapturableInfo Info;
public LegacyCapturable(Actor self, LegacyCapturableInfo info)
{
this.self = self;
Info = info;
}
public bool CanBeTargetedBy(Actor captor)
public bool CanBeTargetedBy(Actor captor, Player owner)
{
var c = captor.TraitOrDefault<LegacyCaptures>();
if (c == null)
return false;
var playerRelationship = self.Owner.Stances[captor.Owner];
if (playerRelationship == Stance.Ally && !Info.AllowAllies)
var playerRelationship = owner.Stances[captor.Owner];
if (playerRelationship == Stance.Ally && !AllowAllies)
return false;
if (playerRelationship == Stance.Enemy && !Info.AllowEnemies)
if (playerRelationship == Stance.Enemy && !AllowEnemies)
return false;
if (playerRelationship == Stance.Neutral && !Info.AllowNeutral)
if (playerRelationship == Stance.Neutral && !AllowNeutral)
return false;
if (!c.Info.CaptureTypes.Contains(Info.Type))
if (!c.Info.CaptureTypes.Contains(Type))
return false;
return true;
}
}
class LegacyCapturable { }
}

View File

@@ -12,11 +12,11 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Orders;
using OpenRA.Traits;
using OpenRA.FileFormats;
namespace OpenRA.Mods.RA
{
@@ -36,11 +36,9 @@ namespace OpenRA.Mods.RA
class LegacyCaptures : IIssueOrder, IResolveOrder, IOrderVoice
{
public readonly LegacyCapturesInfo Info;
readonly Actor self;
public LegacyCaptures(Actor self, LegacyCapturesInfo info)
{
this.self = self;
Info = info;
}
@@ -48,70 +46,85 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new LegacyCaptureOrderTargeter(CanCapture);
yield return new LegacyCaptureOrderTargeter(Info.Sabotage);
}
}
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "LegacyCaptureActor")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
if (order.OrderID != "LegacyCaptureActor")
return null;
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
public string VoicePhraseForOrder(Actor self, Order order)
{
return (order.OrderString == "LegacyCaptureActor") ? "Attack" : null;
return order.OrderString == "LegacyCaptureActor" ? "Attack" : null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "LegacyCaptureActor")
{
self.SetTargetLine(Target.FromOrder(order), Color.Red);
if (order.OrderString != "LegacyCaptureActor")
return;
var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new LegacyCaptureActor(Target.FromOrder(order))));
}
}
bool CanCapture(Actor target)
{
var c = target.TraitOrDefault<LegacyCapturable>();
return c != null && c.CanBeTargetedBy(self);
self.SetTargetLine(target, Color.Red);
self.QueueActivity(new Enter(target.Actor, new LegacyCaptureActor(target)));
}
class LegacyCaptureOrderTargeter : UnitOrderTargeter
{
readonly Func<Actor, bool> useCaptureCursor;
readonly bool sabotage;
public LegacyCaptureOrderTargeter(Func<Actor, bool> useCaptureCursor)
public LegacyCaptureOrderTargeter(bool sabotage)
: base("LegacyCaptureActor", 6, "enter", true, true)
{
this.useCaptureCursor = useCaptureCursor;
this.sabotage = sabotage;
}
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor)) return false;
var canTargetActor = useCaptureCursor(target);
if (canTargetActor)
var c = target.Info.Traits.GetOrDefault<LegacyCapturableInfo>();
if (c == null || !c.CanBeTargetedBy(self, target.Owner))
{
var c = target.Trait<LegacyCapturable>();
var health = target.Trait<Health>();
var lowEnoughHealth = health.HP <= c.Info.CaptureThreshold * health.MaxHP;
cursor = lowEnoughHealth ? "enter" : "capture";
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
return true;
cursor = "enter-blocked";
return false;
}
cursor = "enter-blocked";
return false;
var health = target.Trait<Health>();
var lowEnoughHealth = health.HP <= c.CaptureThreshold * health.MaxHP;
cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant
? "capture" : "enter";
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
var c = target.Info.Traits.GetOrDefault<LegacyCapturableInfo>();
if (c == null || !c.CanBeTargetedBy(self, target.Owner))
{
cursor = "enter-blocked";
return false;
}
var health = target.Info.Traits.GetOrDefault<HealthInfo>();
var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP;
cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant
? "capture" : "enter";
return true;
}
}
}

View File

@@ -156,21 +156,21 @@ namespace OpenRA.Mods.RA
public string OrderID { get { return "BeginMinefield"; } }
public int OrderPriority { get { return 5; } }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
return false;
}
if (target.Type != TargetType.Terrain)
return false;
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
var location = target.CenterPosition.ToCPos();
if (!self.World.Map.IsInMap(location))
return false;
cursor = "ability";
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
return (actorsAtLocation.Count == 0 && modifiers.HasModifier(TargetModifiers.ForceAttack));
return !othersAtTarget.Any() && modifiers.HasModifier(TargetModifiers.ForceAttack);
}
public bool IsQueued { get; protected set; }
}
}

View File

@@ -457,7 +457,7 @@ namespace OpenRA.Mods.RA.Missions
}
}
class Allies04HijackableInfo : ITraitInfo
class Allies04HijackableInfo : ITraitInfo, Requires<InfiltratableInfo>
{
public object Create(ActorInitializer init) { return new Allies04Hijackable(init.self); }
}

View File

@@ -9,7 +9,9 @@
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Effects;
@@ -24,50 +26,97 @@ namespace OpenRA.Mods.RA
public object Create(ActorInitializer init) { return new FrozenUnderFog(init, this); }
}
public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITickRender
public class FrozenUnderFog : IRenderModifier, IVisibilityModifier, ITick, ITickRender, ISync
{
FrozenActorProxy proxy;
[Sync] public int VisibilityHash;
bool initialized, startsRevealed;
IEnumerable<CPos> footprint;
bool visible, cacheFirstFrame;
Lazy<IToolTip> tooltip;
Lazy<Health> health;
Dictionary<Player, bool> visible;
Dictionary<Player, FrozenActor> frozen;
public FrozenUnderFog(ActorInitializer init, FrozenUnderFogInfo info)
{
footprint = FootprintUtils.Tiles(init.self);
proxy = new FrozenActorProxy(init.self, footprint);
init.world.AddFrameEndTask(w => w.Add(proxy));
// Spawned actors (e.g. building husks) shouldn't be revealed
cacheFirstFrame = info.StartsRevealed && !init.Contains<ParentActorInit>();
startsRevealed = info.StartsRevealed && !init.Contains<ParentActorInit>();
footprint = FootprintUtils.Tiles(init.self);
tooltip = Lazy.New(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
tooltip = Lazy.New(() => init.self.TraitsImplementing<IToolTip>().FirstOrDefault());
health = Lazy.New(() => init.self.TraitOrDefault<Health>());
frozen = new Dictionary<Player, FrozenActor>();
visible = init.world.Players.ToDictionary(p => p, p => false);
}
public bool IsVisible(Actor self, Player byPlayer)
{
return byPlayer == null || footprint.Any(c => byPlayer.Shroud.IsVisible(c));
return byPlayer == null || visible[byPlayer];
}
public void TickRender(WorldRenderer wr, Actor self)
public void Tick(Actor self)
{
if (self.Destroyed)
return;
visible = IsVisible(self, self.World.RenderPlayer);
if (cacheFirstFrame)
VisibilityHash = 0;
foreach (var p in self.World.Players)
{
visible = true;
cacheFirstFrame = false;
visible[p] = footprint.Any(c => p.Shroud.IsVisible(c));
if (visible[p])
VisibilityHash += p.ClientIndex;
}
if (visible)
if (!initialized)
{
var comparer = new RenderableComparer(wr);
proxy.SetRenderables(self.Render(wr).OrderBy(r => r, comparer));
foreach (var p in self.World.Players)
{
visible[p] |= startsRevealed;
frozen[p] = new FrozenActor(self, footprint);
p.PlayerActor.Trait<FrozenActorLayer>().Add(frozen[p]);
}
initialized = true;
}
foreach (var player in self.World.Players)
{
if (!visible[player])
continue;
frozen[player].Owner = self.Owner;
if (health.Value != null)
{
frozen[player].HP = health.Value.HP;
frozen[player].DamageState = health.Value.DamageState;
}
if (tooltip.Value != null)
{
frozen[player].TooltipName = tooltip.Value.Name();
frozen[player].TooltipOwner = tooltip.Value.Owner();
}
}
}
public void TickRender(WorldRenderer wr, Actor self)
{
if (self.Destroyed || !initialized || !visible.Any(v => v.Value))
return;
// Force a copy of the underlying data
var renderables = self.Render(wr).Select(rr => rr).ToArray();
foreach (var player in self.World.Players)
if (visible[player])
frozen[player].Renderables = renderables;
}
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
{
return visible ? r : SpriteRenderable.None;
return IsVisible(self, self.World.RenderPlayer) ? r : SpriteRenderable.None;
}
}
}

View File

@@ -494,13 +494,12 @@ namespace OpenRA.Mods.RA.Move
public int OrderPriority { get { return 4; } }
public bool IsQueued { get; protected set; }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
return false;
}
if (!target.IsValid)
return false;
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
var location = target.CenterPosition.ToCPos();
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
cursor = "move";

View File

@@ -258,7 +258,7 @@ namespace OpenRA.Mods.RA.Move
return Enumerable.Reverse(path).Select(c => Target.FromCell(c));
if (destination != null)
return new Target[] { Target.FromCell(destination.Value) };
return Target.NoTargets;
return Target.None;
}
abstract class MovePart : Activity

View File

@@ -282,7 +282,6 @@
<Compile Include="NullLoadScreen.cs" />
<Compile Include="LoadWidgetAtGameStart.cs" />
<Compile Include="Orders\DeployOrderTargeter.cs" />
<Compile Include="Orders\EnterBuildingOrderTargeter.cs" />
<Compile Include="Orders\PlaceBuildingOrderGenerator.cs" />
<Compile Include="Orders\PowerDownOrderGenerator.cs" />
<Compile Include="Orders\RepairOrderGenerator.cs" />
@@ -462,10 +461,10 @@
<Compile Include="World\DomainIndex.cs" />
<Compile Include="MPStartUnits.cs" />
<Compile Include="Orders\SetChronoTankDestination.cs" />
<Compile Include="Effects\FrozenActorProxy.cs" />
<Compile Include="Widgets\Logic\WorldTooltipLogic.cs" />
<Compile Include="TeslaZapRenderable.cs" />
<Compile Include="Buildings\Bib.cs" />
<Compile Include="Orders\EnterAlliedActorTargeter.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -35,16 +35,15 @@ namespace OpenRA.Mods.RA.Orders
public string OrderID { get; private set; }
public int OrderPriority { get; private set; }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
if (target.Type != TargetType.Actor)
return false;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
cursor = useDeployCursor() ? "deploy" : "deploy-blocked";
return self == target;
}
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
return false;
return self == target.Actor;
}
public bool IsQueued { get; protected set; }

View File

@@ -9,18 +9,19 @@
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.Orders
{
public class EnterOrderTargeter<T> : UnitOrderTargeter
public class EnterAlliedActorTargeter<T> : UnitOrderTargeter
{
readonly Func<Actor, bool> canTarget;
readonly Func<Actor, bool> useEnterCursor;
public EnterOrderTargeter(string order, int priority, bool targetEnemy, bool targetAlly,
public EnterAlliedActorTargeter(string order, int priority,
Func<Actor, bool> canTarget, Func<Actor, bool> useEnterCursor)
: base (order, priority, "enter", targetEnemy, targetAlly)
: base (order, priority, "enter", false, true)
{
this.canTarget = canTarget;
this.useEnterCursor = useEnterCursor;
@@ -28,18 +29,17 @@ namespace OpenRA.Mods.RA.Orders
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
return false;
if (!target.HasTrait<T>())
return false;
if (!canTarget(target))
if (!target.HasTrait<T>() || !canTarget(target))
return false;
cursor = useEnterCursor(target) ? "enter" : "enter-blocked";
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
// Allied actors are never frozen
return false;
}
}
}

View File

@@ -15,12 +15,12 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA.Orders
{
public class UnitOrderTargeter : IOrderTargeter
public abstract class UnitOrderTargeter : IOrderTargeter
{
readonly string cursor;
readonly bool targetEnemyUnits, targetAllyUnits;
public UnitOrderTargeter( string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits )
public UnitOrderTargeter(string order, int priority, string cursor, bool targetEnemyUnits, bool targetAllyUnits)
{
this.OrderID = order;
this.OrderPriority = priority;
@@ -33,27 +33,33 @@ namespace OpenRA.Mods.RA.Orders
public int OrderPriority { get; private set; }
public bool? ForceAttack = null;
public virtual bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public abstract bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor);
public abstract bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor);
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
if( self == null ) throw new ArgumentNullException( "self" );
if( target == null ) throw new ArgumentNullException( "target" );
var type = target.Type;
if (type != TargetType.Actor && type != TargetType.FrozenActor)
return false;
cursor = this.cursor;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack) return false;
if (ForceAttack != null && modifiers.HasModifier(TargetModifiers.ForceAttack) != ForceAttack)
return false;
var playerRelationship = self.Owner.Stances[target.Owner];
var owner = type == TargetType.FrozenActor ? target.FrozenActor.Owner : target.Actor.Owner;
var playerRelationship = self.Owner.Stances[owner];
if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits) return false;
if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits) return false;
if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Ally && !targetAllyUnits)
return false;
return true;
}
if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && playerRelationship == Stance.Enemy && !targetEnemyUnits)
return false;
public virtual bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
return false;
return type == TargetType.FrozenActor ?
CanTargetFrozenActor(self, target.FrozenActor, modifiers, ref cursor) :
CanTargetActor(self, target.Actor, modifiers, ref cursor);
}
public virtual bool IsQueued { get; protected set; }
@@ -71,15 +77,12 @@ namespace OpenRA.Mods.RA.Orders
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
return false;
return target.TraitsImplementing<ITargetable>().Any(t => t.TargetTypes.Contains(targetType));
}
if (!target.TraitsImplementing<ITargetable>().Any(t => t.TargetTypes.Contains(targetType)))
return false;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
return true;
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
return target.Info.Traits.WithInterface<ITargetableInfo>().Any(t => t.GetTargetTypes().Contains(targetType));
}
}
}

View File

@@ -35,8 +35,8 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new EnterOrderTargeter<Cargo>( "EnterTransport", 6, false, true,
target => IsCorrectCargoType( target ), target => CanEnter( target ) );
yield return new EnterAlliedActorTargeter<Cargo>("EnterTransport", 6,
target => IsCorrectCargoType(target), target => CanEnter(target));
}
}

View File

@@ -58,13 +58,12 @@ namespace OpenRA.Mods.RA
public string OrderID { get { return "SetRallyPoint"; } }
public int OrderPriority { get { return 0; } }
public bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{
return false;
}
if (target.Type != TargetType.Terrain)
return false;
public bool CanTargetLocation(Actor self, CPos location, List<Actor> actorsAtLocation, TargetModifiers modifiers, ref string cursor)
{
var location = target.CenterPosition.ToCPos();
if (self.World.Map.IsInMap(location))
{
cursor = "ability";

View File

@@ -38,7 +38,8 @@ namespace OpenRA.Mods.RA
public IEnumerable<IOrderTargeter> Orders
{
get { yield return new EnterOrderTargeter<Building>( "Repair", 5, false, true, target => CanRepairAt( target ), _ => CanRepair() ); }
get { yield return new EnterAlliedActorTargeter<Building>("Repair", 5,
target => CanRepairAt(target), _ => CanRepair()); }
}
public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued )

View File

@@ -38,8 +38,8 @@ namespace OpenRA.Mods.RA
{
get
{
yield return new EnterOrderTargeter<Building>( "RepairNear", 5, false, true,
target => CanRepairAt( target ), _ => ShouldRepair() );
yield return new EnterAlliedActorTargeter<Building>("RepairNear", 5,
target => CanRepairAt(target), _ => ShouldRepair());
}
}

View File

@@ -71,9 +71,6 @@ namespace OpenRA.Mods.RA
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
return false;
var bridge = target.TraitOrDefault<BridgeHut>();
if (bridge == null)
return false;
@@ -83,14 +80,18 @@ namespace OpenRA.Mods.RA
if (!modifiers.HasModifier(TargetModifiers.ForceAttack) && damage != DamageState.Dead)
return false;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
// Can't repair an undamaged bridge
if (damage == DamageState.Undamaged)
cursor = "goldwrench-blocked";
return true;
}
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
// TODO: Bridges don't yet support FrozenUnderFog.
return false;
}
}
}
}

View File

@@ -10,10 +10,10 @@
using System.Collections.Generic;
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Orders;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -28,11 +28,11 @@ namespace OpenRA.Mods.RA
class SupplyTruck : IIssueOrder, IResolveOrder, IOrderVoice
{
SupplyTruckInfo Info;
SupplyTruckInfo info;
public SupplyTruck(SupplyTruckInfo info)
{
Info = info;
this.info = info;
}
public IEnumerable<IOrderTargeter> Orders
@@ -42,10 +42,13 @@ namespace OpenRA.Mods.RA
public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued)
{
if (order.OrderID == "DeliverSupplies")
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
if (order.OrderID != "DeliverSupplies")
return null;
return null;
if (target.Type == TargetType.FrozenActor)
return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID };
return new Order(order.OrderID, self, queued) { TargetActor = target.Actor };
}
public string VoicePhraseForOrder(Actor self, Order order)
@@ -55,12 +58,18 @@ namespace OpenRA.Mods.RA
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "DeliverSupplies")
{
self.SetTargetLine(Target.FromOrder(order), Color.Yellow);
if (order.OrderString != "DeliverSupplies")
return;
var target = self.ResolveFrozenActorOrder(order, Color.Yellow);
if (target.Type != TargetType.Actor)
return;
if (!order.Queued)
self.CancelActivity();
self.QueueActivity(new Enter(order.TargetActor, new DonateSupplies(order.TargetActor, Info.Payload)));
}
self.SetTargetLine(target, Color.Yellow);
self.QueueActivity(new Enter(target.Actor, new DonateSupplies(target.Actor, info.Payload)));
}
class SupplyTruckOrderTargeter : UnitOrderTargeter
@@ -72,17 +81,12 @@ namespace OpenRA.Mods.RA
public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor)
{
if (!base.CanTargetActor(self, target, modifiers, ref cursor))
return false;
return target.HasTrait<AcceptsSupplies>();
}
if (target.AppearsHostileTo(self))
return false;
if (!target.HasTrait<AcceptsSupplies>())
return false;
IsQueued = modifiers.HasModifier(TargetModifiers.ForceQueue);
return true;
public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor)
{
return target.Info.Traits.Contains<AcceptsSuppliesInfo>();
}
}
}

View File

@@ -15,10 +15,11 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class TargetableBuildingInfo : ITraitInfo, Requires<BuildingInfo>
public class TargetableBuildingInfo : ITraitInfo, ITargetableInfo, Requires<BuildingInfo>
{
public readonly string[] TargetTypes = { };
public string[] GetTargetTypes() { return TargetTypes; }
public object Create(ActorInitializer init) { return new TargetableBuilding(init.self, this); }
}

View File

@@ -14,10 +14,11 @@ using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class TargetableUnitInfo : ITraitInfo
public class TargetableUnitInfo : ITraitInfo, ITargetableInfo
{
public readonly string[] TargetTypes = { };
public string[] GetTargetTypes() { return TargetTypes; }
public virtual object Create(ActorInitializer init) { return new TargetableUnit(init.self, this); }
}

View File

@@ -77,7 +77,7 @@ namespace OpenRA.Mods.RA
public virtual void ResolveOrder(Actor self, Order order)
{
if (info.AlignWhenIdle && order.OrderString != "Attack" && order.OrderString != "AttackHold")
if (info.AlignWhenIdle && order.OrderString != "Attack")
desiredFacing = null;
}

View File

@@ -41,16 +41,30 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (viewport == null || viewport.TooltipType == WorldTooltipType.None)
return;
labelText = viewport.TooltipType == WorldTooltipType.Unexplored ? "Unexplored Terrain" :
viewport.ActorTooltip.Name();
Player o = null;
switch (viewport.TooltipType)
{
case WorldTooltipType.Unexplored:
labelText = "Unexplored Terrain";
break;
case WorldTooltipType.Actor:
labelText = viewport.ActorTooltip.Name();
o = viewport.ActorTooltip.Owner();
break;
case WorldTooltipType.FrozenActor:
labelText = viewport.FrozenActorTooltip.TooltipName;
o = viewport.FrozenActorTooltip.TooltipOwner;
break;
}
var textWidth = font.Measure(labelText).X;
if (textWidth != cachedWidth)
{
label.Bounds.Width = textWidth;
widget.Bounds.Width = 2*label.Bounds.X + textWidth;
}
var o = viewport.ActorTooltip != null ? viewport.ActorTooltip.Owner() : null;
showOwner = viewport.TooltipType == WorldTooltipType.Actor && o != null && !o.NonCombatant;
showOwner = o != null && !o.NonCombatant;
if (showOwner)
{