Merge pull request #3726 from pchote/cloak-fix

Cloak fixes
This commit is contained in:
Matthias Mailänder
2013-08-23 09:27:56 -07:00
36 changed files with 135 additions and 132 deletions

View File

@@ -99,7 +99,7 @@ namespace OpenRA.Orders
if (self.Owner != self.World.LocalPlayer) if (self.Owner != self.World.LocalPlayer)
return null; return null;
if (self.Destroyed || target.Type == TargetType.Invalid) if (self.Destroyed || !target.IsValidFor(self))
return null; return null;
if (mi.Button == Game.mouseButtonPreference.Action) if (mi.Button == Game.mouseButtonPreference.Action)

View File

@@ -64,7 +64,7 @@ namespace OpenRA.Traits
foreach (var target in targets) foreach (var target in targets)
{ {
if (!target.IsValid) if (target.Type == TargetType.Invalid)
continue; continue;
var to = wr.ScreenPxPosition(target.CenterPosition); var to = wr.ScreenPxPosition(target.CenterPosition);

View File

@@ -161,7 +161,7 @@ namespace OpenRA.Traits
var c = Color.Green; var c = Color.Green;
var wlr = Game.Renderer.WorldLineRenderer; var wlr = Game.Renderer.WorldLineRenderer;
foreach (var stp in targets.Where(t => t.IsValid).Select(p => wr.ScreenPxPosition(p.CenterPosition))) foreach (var stp in targets.Where(t => t.Type != TargetType.Invalid).Select(p => wr.ScreenPxPosition(p.CenterPosition)))
{ {
wlr.DrawLine(stp + new float2(-1, -1), stp + new float2(-1, 1), c, c); wlr.DrawLine(stp + new float2(-1, -1), stp + new float2(-1, 1), c, c);
wlr.DrawLine(stp + new float2(-1, 1), stp + new float2(1, 1), c, c); wlr.DrawLine(stp + new float2(-1, 1), stp + new float2(1, 1), c, c);

View File

@@ -22,6 +22,7 @@ namespace OpenRA.Traits
TargetType type; TargetType type;
Actor actor; Actor actor;
ITargetable targetable;
FrozenActor frozen; FrozenActor frozen;
WPos pos; WPos pos;
int generation; int generation;
@@ -37,17 +38,20 @@ namespace OpenRA.Traits
public static Target FromActor(Actor a) public static Target FromActor(Actor a)
{ {
if (a == null)
return Target.Invalid;
return new Target return new Target
{ {
actor = a, actor = a,
type = a != null ? TargetType.Actor : TargetType.Invalid, targetable = a.TraitOrDefault<ITargetable>(),
type = TargetType.Actor,
generation = a.Generation, generation = a.Generation,
}; };
} }
public static Target FromFrozenActor(FrozenActor a) { return new Target { frozen = a, type = TargetType.FrozenActor }; } public static Target FromFrozenActor(FrozenActor a) { return new Target { frozen = a, type = TargetType.FrozenActor }; }
public bool IsValid { get { return Type != TargetType.Invalid; } }
public Actor Actor { get { return actor; } } public Actor Actor { get { return actor; } }
public FrozenActor FrozenActor { get { return frozen; } } public FrozenActor FrozenActor { get { return frozen; } }
@@ -70,6 +74,17 @@ namespace OpenRA.Traits
} }
} }
public bool IsValidFor(Actor targeter)
{
if (targeter == null || Type == TargetType.Invalid)
return false;
if (targetable != null && !targetable.TargetableBy(actor, targeter))
return false;
return true;
}
// Representative position - see Positions for the full set of targetable positions. // Representative position - see Positions for the full set of targetable positions.
public WPos CenterPosition public WPos CenterPosition
{ {

View File

@@ -99,7 +99,7 @@ namespace OpenRA.Mods.RA.AI
HackyAI bot; HackyAI bot;
XRandom random; XRandom random;
Actor target; Target target;
StateMachine fsm; StateMachine fsm;
//fuzzy //fuzzy
@@ -113,7 +113,8 @@ namespace OpenRA.Mods.RA.AI
this.world = bot.world; this.world = bot.world;
this.random = bot.random; this.random = bot.random;
this.type = type; this.type = type;
this.target = target; this.target = Traits.Target.FromActor(target);
fsm = new StateMachine(this); fsm = new StateMachine(this);
switch (type) switch (type)
@@ -144,14 +145,13 @@ namespace OpenRA.Mods.RA.AI
public Actor Target public Actor Target
{ {
get { return target; } get { return target.Actor; }
set { target = value; } set { target = Traits.Target.FromActor(value); }
} }
public bool TargetIsValid public bool TargetIsValid
{ {
get { return (target != null && !target.IsDead() && !target.Destroyed get { return target.IsValidFor(units.FirstOrDefault()) && !target.Actor.HasTrait<Husk>(); }
&& target.IsInWorld && !target.HasTrait<Husk>()); }
} }
//********************************************************************************** //**********************************************************************************

View File

@@ -18,7 +18,6 @@ namespace OpenRA.Mods.RA.Activities
public class Attack : Activity public class Attack : Activity
{ {
protected Target Target; protected Target Target;
ITargetable targetable;
WRange Range; WRange Range;
bool AllowMovement; bool AllowMovement;
@@ -33,9 +32,6 @@ namespace OpenRA.Mods.RA.Activities
public Attack(Target target, WRange range, bool allowMovement) public Attack(Target target, WRange range, bool allowMovement)
{ {
Target = target; Target = target;
if (target.Type == TargetType.Actor)
targetable = target.Actor.TraitOrDefault<ITargetable>();
Range = range; Range = range;
AllowMovement = allowMovement; AllowMovement = allowMovement;
} }
@@ -54,13 +50,11 @@ namespace OpenRA.Mods.RA.Activities
return NextActivity; return NextActivity;
var type = Target.Type; var type = Target.Type;
if (type != TargetType.Actor && type != TargetType.Terrain) if (!Target.IsValidFor(self) || type == TargetType.FrozenActor)
return NextActivity;
if (type == TargetType.Actor && !self.Owner.HasFogVisibility() && Target.Actor.HasTrait<Mobile>() && !self.Owner.Shroud.IsTargetable(Target.Actor))
return NextActivity; return NextActivity;
if (targetable != null && !targetable.TargetableBy(Target.Actor, self)) // TODO: This is horrible, and probably wrong. Work out what it is trying to solve, then redo it properly.
if (type == TargetType.Actor && !self.Owner.HasFogVisibility() && Target.Actor.HasTrait<Mobile>() && !self.Owner.Shroud.IsTargetable(Target.Actor))
return NextActivity; return NextActivity;
if (!Target.IsInRange(self.CenterPosition, Range)) if (!Target.IsInRange(self.CenterPosition, Range))

View File

@@ -29,7 +29,7 @@ namespace OpenRA.Mods.RA.Activities
var capturable = target.Actor.Trait<Capturable>(); var capturable = target.Actor.Trait<Capturable>();
if (IsCanceled || !self.IsInWorld || self.IsDead()) if (IsCanceled || !self.IsInWorld || self.IsDead() || !target.IsValidFor(self))
{ {
if (capturable.CaptureInProgress) if (capturable.CaptureInProgress)
capturable.EndCapture(); capturable.EndCapture();

View File

@@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (IsCanceled || target.Type != TargetType.Actor) if (IsCanceled || !target.IsValidFor(self))
return NextActivity; return NextActivity;
self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () => self.World.AddFrameEndTask(w => w.Add(new DelayedAction(delay, () =>

View File

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

View File

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

View File

@@ -16,21 +16,23 @@ namespace OpenRA.Mods.RA.Activities
public class Follow : Activity public class Follow : Activity
{ {
Target target; Target target;
Mobile mobile;
WRange range; WRange range;
int nextPathTime; int nextPathTime;
const int delayBetweenPathingAttempts = 20; const int delayBetweenPathingAttempts = 20;
const int delaySpread = 5; const int delaySpread = 5;
public Follow(Target target, WRange range) public Follow(Actor self, Target target, WRange range)
{ {
this.target = target; this.target = target;
mobile = self.Trait<Mobile>();
this.range = range; this.range = range;
} }
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (IsCanceled || !target.IsValid) if (IsCanceled || !target.IsValidFor(self))
return NextActivity; return NextActivity;
if (target.IsInRange(self.CenterPosition, range) || --nextPathTime > 0) if (target.IsInRange(self.CenterPosition, range) || --nextPathTime > 0)
@@ -39,7 +41,6 @@ namespace OpenRA.Mods.RA.Activities
nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread, nextPathTime = self.World.SharedRandom.Next(delayBetweenPathingAttempts - delaySpread,
delayBetweenPathingAttempts + delaySpread); delayBetweenPathingAttempts + delaySpread);
var mobile = self.Trait<Mobile>();
return Util.SequenceActivities(mobile.MoveWithinRange(target, range), this); return Util.SequenceActivities(mobile.MoveWithinRange(target, range), this);
} }
} }

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Air
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (!target.IsValid) if (!target.IsValidFor(self))
Cancel(self); Cancel(self);
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>(); var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();

View File

@@ -21,7 +21,7 @@ namespace OpenRA.Mods.RA.Air
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (IsCanceled || !target.IsValid) if (IsCanceled || !target.IsValidFor(self))
return NextActivity; return NextActivity;
var limitedAmmo = self.TraitOrDefault<LimitedAmmo>(); var limitedAmmo = self.TraitOrDefault<LimitedAmmo>();

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Air
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (!target.IsValid) if (!target.IsValidFor(self))
Cancel(self); Cancel(self);
if (IsCanceled) if (IsCanceled)

View File

@@ -45,7 +45,7 @@ namespace OpenRA.Mods.RA
if (!self.IsInWorld) if (!self.IsInWorld)
return false; return false;
if (!target.IsValid) if (!target.IsValidFor(self))
return false; return false;
if (Armaments.All(a => a.IsReloading)) if (Armaments.All(a => a.IsReloading))
@@ -54,10 +54,6 @@ namespace OpenRA.Mods.RA
if (self.IsDisabled()) if (self.IsDisabled())
return false; return false;
if (target.Type == TargetType.Actor && target.Actor.HasTrait<ITargetable>() &&
!target.Actor.Trait<ITargetable>().TargetableBy(target.Actor, self))
return false;
return true; return true;
} }
@@ -133,7 +129,7 @@ namespace OpenRA.Mods.RA
if (order.OrderString == "Attack") if (order.OrderString == "Attack")
{ {
var target = self.ResolveFrozenActorOrder(order, Color.Red); var target = self.ResolveFrozenActorOrder(order, Color.Red);
if (!target.IsValid) if (!target.IsValidFor(self))
return; return;
self.SetTargetLine(target, Color.Red); self.SetTargetLine(target, Color.Red);
@@ -155,7 +151,7 @@ namespace OpenRA.Mods.RA
public void AttackTarget(Target target, bool queued, bool allowMove) public void AttackTarget(Target target, bool queued, bool allowMove)
{ {
if (!target.IsValid) if (!target.IsValidFor(self))
return; return;
if (!queued) if (!queued)

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA
public override Activity Tick( Actor self ) public override Activity Tick( Actor self )
{ {
if( IsCanceled || !target.IsValid ) if (IsCanceled || !target.IsValidFor(self))
return NextActivity; return NextActivity;
self.Trait<AttackOmni>().DoAttack(self, target); self.Trait<AttackOmni>().DoAttack(self, target);

View File

@@ -67,7 +67,8 @@ namespace OpenRA.Mods.RA
public override Activity Tick( Actor self ) public override Activity Tick( Actor self )
{ {
if( IsCanceled || !target.IsValid ) return NextActivity; if (IsCanceled || !target.IsValidFor(self))
return NextActivity;
var attack = self.Trait<AttackTesla>(); var attack = self.Trait<AttackTesla>();
if( attack.charges == 0 || !attack.CanAttack( self, target ) ) if( attack.charges == 0 || !attack.CanAttack( self, target ) )
@@ -85,7 +86,8 @@ namespace OpenRA.Mods.RA
public override Activity Tick( Actor self ) public override Activity Tick( Actor self )
{ {
if( IsCanceled || !target.IsValid ) return NextActivity; if (IsCanceled || !target.IsValidFor(self))
return NextActivity;
var attack = self.Trait<AttackTesla>(); var attack = self.Trait<AttackTesla>();
if( attack.charges == 0 ) return NextActivity; if( attack.charges == 0 ) return NextActivity;

View File

@@ -38,7 +38,8 @@ namespace OpenRA.Mods.RA
if (self.HasTrait<Building>() && !buildComplete) if (self.HasTrait<Building>() && !buildComplete)
return false; return false;
if (!target.IsValid) return false; if (!target.IsValidFor(self))
return false;
bool canAttack = false; bool canAttack = false;
foreach (var t in turrets) foreach (var t in turrets)
@@ -53,7 +54,7 @@ namespace OpenRA.Mods.RA
{ {
base.Tick(self); base.Tick(self);
DoAttack(self, Target); DoAttack(self, Target);
IsAttacking = Target.IsValid; IsAttacking = Target.IsValidFor(self);
} }
public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove) public override Activity GetAttackActivity(Actor self, Target newTarget, bool allowMove)
@@ -84,7 +85,8 @@ namespace OpenRA.Mods.RA
public override Activity Tick(Actor self) public override Activity Tick(Actor self)
{ {
if (IsCanceled || !target.IsValid) return NextActivity; if (IsCanceled || !target.IsValidFor(self))
return NextActivity;
if (self.IsDisabled()) return this; if (self.IsDisabled()) return this;
@@ -98,7 +100,7 @@ namespace OpenRA.Mods.RA
attack.Target = target; attack.Target = target;
if (allowMove && self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails) if (allowMove && self.HasTrait<Mobile>() && !self.Info.Traits.Get<MobileInfo>().OnRails)
return Util.SequenceActivities(new Follow(target, range), this); return Util.SequenceActivities(new Follow(self, target, range), this);
} }
return NextActivity; return NextActivity;

View File

@@ -19,12 +19,14 @@ namespace OpenRA.Mods.RA
{ {
public class CloakInfo : ITraitInfo public class CloakInfo : ITraitInfo
{ {
public int InitialDelay = 10; // Ticks public readonly int InitialDelay = 10; // Ticks
public int CloakDelay = 30; // Ticks public readonly int CloakDelay = 30; // Ticks
public string CloakSound = "subshow1.aud";
public string UncloakSound = "subshow1.aud";
public readonly string Palette = "cloak";
public readonly bool UncloakOnMove = false; public readonly bool UncloakOnMove = false;
public readonly bool RequiresCrate = false;
public readonly string CloakSound = "subshow1.aud";
public readonly string UncloakSound = "subshow1.aud";
public readonly string Palette = "cloak";
public object Create(ActorInitializer init) { return new Cloak(init.self, this); } public object Create(ActorInitializer init) { return new Cloak(init.self, this); }
} }
@@ -32,7 +34,8 @@ namespace OpenRA.Mods.RA
public class Cloak : IRenderModifier, INotifyDamageStateChanged, INotifyAttack, ITick, IVisibilityModifier, IRadarColorModifier, ISync public class Cloak : IRenderModifier, INotifyDamageStateChanged, INotifyAttack, ITick, IVisibilityModifier, IRadarColorModifier, ISync
{ {
[Sync] int remainingTime; [Sync] int remainingTime;
[Sync] bool canCloak = true; [Sync] bool damageDisabled;
[Sync] bool crateDisabled;
Actor self; Actor self;
CloakInfo info; CloakInfo info;
@@ -44,6 +47,7 @@ namespace OpenRA.Mods.RA
this.self = self; this.self = self;
remainingTime = info.InitialDelay; remainingTime = info.InitialDelay;
crateDisabled = info.RequiresCrate;
} }
public void Uncloak() { Uncloak(info.CloakDelay); } public void Uncloak() { Uncloak(info.CloakDelay); }
@@ -62,8 +66,9 @@ namespace OpenRA.Mods.RA
public void DamageStateChanged(Actor self, AttackInfo e) public void DamageStateChanged(Actor self, AttackInfo e)
{ {
canCloak = (e.DamageState < DamageState.Critical); damageDisabled = e.DamageState >= DamageState.Critical;
if (!canCloak) Uncloak(); if (damageDisabled)
Uncloak();
} }
public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r) public IEnumerable<IRenderable> ModifyRender(Actor self, WorldRenderer wr, IEnumerable<IRenderable> r)
@@ -82,9 +87,12 @@ namespace OpenRA.Mods.RA
public void Tick(Actor self) public void Tick(Actor self)
{ {
if (remainingTime > 0 && canCloak) if (remainingTime > 0 && !crateDisabled && !damageDisabled && --remainingTime <= 0)
if (--remainingTime <= 0) {
Sound.Play(info.CloakSound, self.CenterPosition); self.Generation++;
Sound.Play(info.CloakSound, self.CenterPosition);
}
if (self.IsDisabled()) if (self.IsDisabled())
Uncloak(); Uncloak();
@@ -95,15 +103,14 @@ namespace OpenRA.Mods.RA
} }
} }
public bool IsVisible(Actor self, Player byPlayer) public bool IsVisible(Actor self, Player viewer)
{ {
if (!Cloaked || self.Owner.IsAlliedWith(byPlayer)) if (!Cloaked || self.Owner.IsAlliedWith(viewer))
return true; return true;
// TODO: Change this to be per-player? A cloak detector revealing to everyone is dumb var centerPosition = self.CenterPosition;
return self.World.ActorsWithTrait<DetectCloaked>().Any(a => return self.World.ActorsWithTrait<DetectCloaked>().Any(a => a.Actor.Owner.IsAlliedWith(viewer) &&
a.Actor.Owner.Stances[self.Owner] != Stance.Ally && (centerPosition - a.Actor.CenterPosition).Length < WRange.FromCells(a.Actor.Info.Traits.Get<DetectCloakedInfo>().Range).Range);
(self.Location - a.Actor.Location).Length < a.Actor.Info.Traits.Get<DetectCloakedInfo>().Range);
} }
public Color RadarColorOverride(Actor self) public Color RadarColorOverride(Actor self)
@@ -113,5 +120,12 @@ namespace OpenRA.Mods.RA
c = Color.FromArgb(128, c); c = Color.FromArgb(128, c);
return c; return c;
} }
public bool AcceptsCloakCrate { get { return info.RequiresCrate && crateDisabled; } }
public void ReceivedCloakCrate(Actor self)
{
crateDisabled = false;
}
} }
} }

View File

@@ -16,52 +16,27 @@ namespace OpenRA.Mods.RA.Crates
{ {
public class CloakCrateActionInfo : CrateActionInfo public class CloakCrateActionInfo : CrateActionInfo
{ {
public readonly int InitialDelay = 10;
public readonly int CloakDelay = 30;
public readonly string CloakSound = "subshow1.aud";
public readonly string UncloakSound = "subshow1.aud";
public override object Create(ActorInitializer init) { return new CloakCrateAction(init.self, this); } public override object Create(ActorInitializer init) { return new CloakCrateAction(init.self, this); }
} }
public class CloakCrateAction : CrateAction public class CloakCrateAction : CrateAction
{ {
CloakCrateActionInfo Info;
public CloakCrateAction(Actor self, CloakCrateActionInfo info) public CloakCrateAction(Actor self, CloakCrateActionInfo info)
: base(self, info) { Info = info; } : base(self, info) { }
public override int GetSelectionShares(Actor collector) public override int GetSelectionShares(Actor collector)
{ {
return collector.HasTrait<AcceptsCloakCrate>() && !collector.HasTrait<Cloak>() var cloak = collector.TraitOrDefault<Cloak>();
? base.GetSelectionShares(collector) : 0; if (cloak == null || !cloak.AcceptsCloakCrate)
return 0;
return base.GetSelectionShares(collector);
} }
public override void Activate(Actor collector) public override void Activate(Actor collector)
{ {
var cloakInfo = new CloakInfo() collector.Trait<Cloak>().ReceivedCloakCrate(collector);
{
InitialDelay = Info.InitialDelay,
CloakDelay = Info.CloakDelay,
CloakSound = Info.CloakSound,
UncloakSound = Info.UncloakSound
};
var cloak = new Cloak(collector, cloakInfo);
collector.World.AddFrameEndTask(w =>
{
w.Remove(collector);
collector.AddTrait(cloak);
var t = collector.TraitOrDefault<TargetableUnit>();
if (t != null) t.ReceivedCloak(collector);
w.Add(collector);
});
base.Activate(collector); base.Activate(collector);
} }
} }
public class AcceptsCloakCrateInfo : TraitInfo<AcceptsCloakCrate> {}
public class AcceptsCloakCrate {}
} }

View File

@@ -36,6 +36,7 @@ namespace OpenRA.Mods.RA.Effects
Lazy<HiddenUnderFog> huf; Lazy<HiddenUnderFog> huf;
Lazy<FrozenUnderFog> fuf; Lazy<FrozenUnderFog> fuf;
Lazy<Spy> spy; Lazy<Spy> spy;
Lazy<Cloak> cloak;
Cache<Player, GpsWatcher> watcher; Cache<Player, GpsWatcher> watcher;
Cache<Player, FrozenActorLayer> frozen; Cache<Player, FrozenActorLayer> frozen;
@@ -53,15 +54,15 @@ namespace OpenRA.Mods.RA.Effects
huf = Lazy.New(() => self.TraitOrDefault<HiddenUnderFog>()); huf = Lazy.New(() => self.TraitOrDefault<HiddenUnderFog>());
fuf = Lazy.New(() => self.TraitOrDefault<FrozenUnderFog>()); fuf = Lazy.New(() => self.TraitOrDefault<FrozenUnderFog>());
spy = Lazy.New(() => self.TraitOrDefault<Spy>()); spy = Lazy.New(() => self.TraitOrDefault<Spy>());
cloak = Lazy.New(() => self.TraitOrDefault<Cloak>());
watcher = new Cache<Player, GpsWatcher>(p => p.PlayerActor.Trait<GpsWatcher>()); watcher = new Cache<Player, GpsWatcher>(p => p.PlayerActor.Trait<GpsWatcher>());
frozen = new Cache<Player, FrozenActorLayer>(p => p.PlayerActor.Trait<FrozenActorLayer>()); frozen = new Cache<Player, FrozenActorLayer>(p => p.PlayerActor.Trait<FrozenActorLayer>());
} }
bool ShouldShowIndicator() bool ShouldShowIndicator()
{ {
// Can be granted at runtime via a crate, so can't cache if (cloak.Value != null && cloak.Value.Cloaked)
var cloak = self.TraitOrDefault<Cloak>();
if (cloak != null && cloak.Cloaked)
return false; return false;
if (spy.Value != null && spy.Value.Disguised) if (spy.Value != null && spy.Value.Disguised)

View File

@@ -59,7 +59,7 @@ namespace OpenRA.Mods.RA.Effects
public void Tick(World world) public void Tick(World world)
{ {
// Beam tracks target // Beam tracks target
if (args.guidedTarget.IsValid) if (args.guidedTarget.IsValidFor(args.sourceActor))
target = args.guidedTarget.CenterPosition; target = args.guidedTarget.CenterPosition;
if (!doneDamage) if (!doneDamage)

View File

@@ -120,7 +120,7 @@ namespace OpenRA.Mods.RA.Effects
anim.Tick(); anim.Tick();
// Missile tracks target // Missile tracks target
if (args.guidedTarget.IsValid) if (args.guidedTarget.IsValidFor(args.sourceActor))
target = args.guidedTarget.CenterPosition; target = args.guidedTarget.CenterPosition;
var dist = target + offset - pos; var dist = target + offset - pos;
@@ -133,7 +133,7 @@ namespace OpenRA.Mods.RA.Effects
desiredFacing = facing + world.SharedRandom.Next(-20, 21); desiredFacing = facing + world.SharedRandom.Next(-20, 21);
desiredAltitude = world.SharedRandom.Next(-43, 86); desiredAltitude = world.SharedRandom.Next(-43, 86);
} }
else if (!args.guidedTarget.IsValid) else if (!args.guidedTarget.IsValidFor(args.sourceActor))
desiredFacing = facing; desiredFacing = facing;
facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT); facing = Traits.Util.TickFacing(facing, desiredFacing, info.ROT);

View File

@@ -48,7 +48,7 @@ namespace OpenRA.Mods.RA.Effects
if (!doneDamage) if (!doneDamage)
{ {
var pos = Args.guidedTarget.IsValid ? Args.guidedTarget.CenterPosition : Args.passiveTarget; var pos = Args.guidedTarget.IsValidFor(Args.sourceActor) ? Args.guidedTarget.CenterPosition : Args.passiveTarget;
Combat.DoImpacts(pos, Args.sourceActor, Args.weapon, Args.firepowerModifier); Combat.DoImpacts(pos, Args.sourceActor, Args.weapon, Args.firepowerModifier);
doneDamage = true; doneDamage = true;
} }
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA.Effects
{ {
if (!initialized) if (!initialized)
{ {
var pos = Args.guidedTarget.IsValid ? Args.guidedTarget.CenterPosition : Args.passiveTarget; var pos = Args.guidedTarget.IsValidFor(Args.sourceActor) ? Args.guidedTarget.CenterPosition : Args.passiveTarget;
zap = new TeslaZapRenderable(Args.source, 0, pos - Args.source, Info.Image, Info.BrightZaps, Info.DimZaps); zap = new TeslaZapRenderable(Args.source, 0, pos - Args.source, Info.Image, Info.BrightZaps, Info.DimZaps);
} }
yield return zap; yield return zap;

View File

@@ -31,7 +31,7 @@ namespace OpenRA.Mods.RA
var range = WRange.FromCells(target.Actor.Info.Traits.Get<GuardableInfo>().Range); var range = WRange.FromCells(target.Actor.Info.Traits.Get<GuardableInfo>().Range);
self.QueueActivity(false, new AttackMove.AttackMoveActivity(self, self.QueueActivity(false, new AttackMove.AttackMoveActivity(self,
new Follow(target, range))); new Follow(self, target, range)));
} }
} }

View File

@@ -496,7 +496,7 @@ namespace OpenRA.Mods.RA.Move
public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor) public bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, TargetModifiers modifiers, ref string cursor)
{ {
if (!target.IsValid) if (!target.IsValidFor(self))
return false; return false;
var location = target.CenterPosition.ToCPos(); var location = target.CenterPosition.ToCPos();

View File

@@ -73,7 +73,7 @@ namespace OpenRA.Mods.RA.Move
{ {
this.getPath = (self, mobile) => this.getPath = (self, mobile) =>
{ {
if (!target.IsValid) if (!target.IsValidFor(self))
return NoPath; return NoPath;
return self.World.WorldActor.Trait<PathFinder>().FindUnitPathToRange( return self.World.WorldActor.Trait<PathFinder>().FindUnitPathToRange(

View File

@@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA
if (reservedFor == null) if (reservedFor == null)
return; /* nothing to do */ return; /* nothing to do */
if (!Target.FromActor( reservedFor ).IsValid) if (!Target.FromActor(reservedFor).IsValidFor(self))
reservedFor = null; /* not likely to arrive now. */ reservedFor = null; /* not likely to arrive now. */
} }

View File

@@ -30,24 +30,15 @@ namespace OpenRA.Mods.RA
public TargetableUnit(Actor self, TargetableUnitInfo info) public TargetableUnit(Actor self, TargetableUnitInfo info)
{ {
this.info = info; this.info = info;
ReceivedCloak(self);
}
// Arbitrary units can receive cloak via a crate during gameplay
public void ReceivedCloak(Actor self)
{
cloak = self.TraitOrDefault<Cloak>(); cloak = self.TraitOrDefault<Cloak>();
} }
public virtual bool TargetableBy(Actor self, Actor byActor) public virtual bool TargetableBy(Actor self, Actor viewer)
{ {
if (cloak == null || !cloak.Cloaked) if (cloak == null || !cloak.Cloaked)
return true; return true;
if (self.Owner.IsAlliedWith(byActor.Owner)) return cloak.IsVisible(self, viewer.Owner);
return true;
return self.World.ActorsWithTrait<DetectCloaked>().Any(a => (self.Location - a.Actor.Location).Length < a.Actor.Info.Traits.Get<DetectCloakedInfo>().Range);
} }
public virtual string[] TargetTypes { get { return info.TargetTypes; } } public virtual string[] TargetTypes { get { return info.TargetTypes; } }

View File

@@ -28,13 +28,18 @@
DrawLineToTarget: DrawLineToTarget:
ActorLostNotification: ActorLostNotification:
AttackMove: AttackMove:
AcceptsCloakCrate:
WithSmoke: WithSmoke:
DebugMuzzlePositions: DebugMuzzlePositions:
Guard: Guard:
Guardable: Guardable:
BodyOrientation: BodyOrientation:
UpdatesPlayerStatistics: UpdatesPlayerStatistics:
Cloak:
RequiresCrate: true
InitialDelay: 15
CloakDelay: 90
CloakSound: trans1.aud
UncloakSound: trans1.aud
^Tank: ^Tank:
AppearsOnRadar: AppearsOnRadar:
@@ -66,7 +71,6 @@
DrawLineToTarget: DrawLineToTarget:
ActorLostNotification: ActorLostNotification:
AttackMove: AttackMove:
AcceptsCloakCrate:
WithSmoke: WithSmoke:
Explodes: Explodes:
Weapon: UnitExplodeSmall Weapon: UnitExplodeSmall
@@ -76,6 +80,12 @@
Guardable: Guardable:
BodyOrientation: BodyOrientation:
UpdatesPlayerStatistics: UpdatesPlayerStatistics:
Cloak:
RequiresCrate: true
InitialDelay: 15
CloakDelay: 90
CloakSound: trans1.aud
UncloakSound: trans1.aud
^Helicopter: ^Helicopter:
AppearsOnRadar: AppearsOnRadar:

View File

@@ -393,10 +393,6 @@ CRATE:
SelectionShares: 5 SelectionShares: 5
CloakCrateAction: CloakCrateAction:
SelectionShares: 5 SelectionShares: 5
InitialDelay: 15
CloakDelay: 90
CloakSound: trans1.aud
UncloakSound: trans1.aud
Effect: cloak Effect: cloak
GiveMcvCrateAction: GiveMcvCrateAction:
SelectionShares: 0 SelectionShares: 0

View File

@@ -33,7 +33,7 @@ MCV:
LeavesHusk: LeavesHusk:
HuskActor: MCV.Husk HuskActor: MCV.Husk
-GainsExperience: -GainsExperience:
-AcceptsCloakCrate: -Cloak:
Explodes: Explodes:
Weapon: UnitExplodeSmall Weapon: UnitExplodeSmall
EmptyWeapon: UnitExplodeSmall EmptyWeapon: UnitExplodeSmall
@@ -519,6 +519,7 @@ STNK:
RevealsShroud: RevealsShroud:
Range: 7 Range: 7
Cloak: Cloak:
RequiresCrate: false
InitialDelay: 90 InitialDelay: 90
CloakDelay: 90 CloakDelay: 90
CloakSound: trans1.aud CloakSound: trans1.aud

View File

@@ -554,13 +554,6 @@ CRATE:
SelectionShares: 0 SelectionShares: 0
NoBaseSelectionShares: 9001 NoBaseSelectionShares: 9001
Unit: mcvo Unit: mcvo
CloakCrateAction:
SelectionShares: 15
InitialDelay: 15
CloakDelay: 90
CloakSound: STEALTH1.WAV
UncloakSound: STEALTH2.WAV
Effect: cloak
RenderSimple: RenderSimple:
ProximityCaptor: ProximityCaptor:
Types:Crate Types:Crate