diff --git a/CHANGELOG b/CHANGELOG index 862a6d3c10..d21165516a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -115,6 +115,8 @@ NEW: Updated shroud-based traits to use world units. Renamed AttackTesla into AttackCharge and un-hardcoded initial charge delay and delay for additional charges. Updated RenderBuildingCharge so you can set a custom charge sequence name (default is active). + Added IEffectiveOwner interface for traits/logic that temporarily alter an actor's apparent owner. + Renamed Spy trait to Disguise, SpyToolTip trait to DisguiseToolTip, and RenderSpy trait to RenderDisguise. Server: Message of the day is now shared between all mods and a default motd.txt gets created in the user directory. Asset Browser: diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 9a916d0b72..a356a4198f 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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, @@ -29,8 +29,10 @@ namespace OpenRA Lazy occupySpace; Lazy facing; Lazy health; + Lazy effectiveOwner; public IOccupySpace OccupiesSpace { get { return occupySpace.Value; } } + public IEffectiveOwner EffectiveOwner { get { return effectiveOwner.Value; } } public CPos Location { get { return occupySpace.Value.TopLeft; } } public WPos CenterPosition { get { return occupySpace.Value.CenterPosition; } } @@ -74,6 +76,7 @@ namespace OpenRA facing = Lazy.New(() => TraitOrDefault()); health = Lazy.New(() => TraitOrDefault()); + effectiveOwner = Lazy.New(() => TraitOrDefault()); applyIRender = (x, wr) => x.Render(this, wr); applyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m); diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index fc55f92187..035a52e565 100755 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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, @@ -84,6 +84,11 @@ namespace OpenRA.Traits bool IsValidTarget(Actor self, Actor saboteur); } public interface IStoreOre { int Capacity { get; } } + public interface IEffectiveOwner + { + bool Disguised { get; } + Player Owner { get; } + } public interface IToolTip { string Name(); diff --git a/OpenRA.Mods.RA/ActorExts.cs b/OpenRA.Mods.RA/ActorExts.cs index 360bfca05a..31ec3dc988 100644 --- a/OpenRA.Mods.RA/ActorExts.cs +++ b/OpenRA.Mods.RA/ActorExts.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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, @@ -15,20 +15,14 @@ namespace OpenRA.Mods.RA { public static class ActorExts { - static bool IsDisguisedSpy(this Actor a) - { - var spy = a.TraitOrDefault(); - return spy != null && spy.Disguised; - } - public static bool AppearsFriendlyTo(this Actor self, Actor toActor) { var stance = toActor.Owner.Stances[self.Owner]; if (stance == Stance.Ally) return true; - if (self.IsDisguisedSpy() && !toActor.HasTrait()) - return toActor.Owner.Stances[self.Trait().disguisedAsPlayer] == Stance.Ally; + if (self.EffectiveOwner != null && self.EffectiveOwner.Disguised && !toActor.HasTrait()) + return toActor.Owner.Stances[self.EffectiveOwner.Owner] == Stance.Ally; return stance == Stance.Ally; } @@ -39,8 +33,8 @@ namespace OpenRA.Mods.RA if (stance == Stance.Ally) return false; /* otherwise, we'll hate friendly disguised spies */ - if (self.IsDisguisedSpy() && !toActor.HasTrait()) - return toActor.Owner.Stances[self.Trait().disguisedAsPlayer] == Stance.Enemy; + if (self.EffectiveOwner != null && self.EffectiveOwner.Disguised && !toActor.HasTrait()) + return toActor.Owner.Stances[self.EffectiveOwner.Owner] == Stance.Enemy; return stance == Stance.Enemy; } diff --git a/OpenRA.Mods.RA/Spy.cs b/OpenRA.Mods.RA/Disguise.cs similarity index 61% rename from OpenRA.Mods.RA/Spy.cs rename to OpenRA.Mods.RA/Disguise.cs index 939ae66805..b442930a5d 100644 --- a/OpenRA.Mods.RA/Spy.cs +++ b/OpenRA.Mods.RA/Disguise.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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, @@ -16,69 +16,73 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - class SpyToolTipInfo : TooltipInfo, Requires + class DisguiseToolTipInfo : TooltipInfo, Requires { - public override object Create (ActorInitializer init) { return new SpyToolTip(init.self, this); } + public override object Create (ActorInitializer init) { return new DisguiseToolTip(init.self, this); } } - class SpyToolTip : IToolTip + class DisguiseToolTip : IToolTip { Actor self; - TooltipInfo Info; - Spy spy; + TooltipInfo info; + Disguise disguise; + + public DisguiseToolTip(Actor self, TooltipInfo info) + { + this.self = self; + this.info = info; + disguise = self.Trait(); + } public string Name() { - if (spy.Disguised) + if (disguise.Disguised) { if (self.Owner == self.World.LocalPlayer) - return "{0} ({1})".F(Info.Name, spy.disguisedAsName); - return spy.disguisedAsName; + return "{0} ({1})".F(info.Name, disguise.AsName); + + return disguise.AsName; } - return Info.Name; + return info.Name; } public Player Owner() { - if (spy.Disguised) + if (disguise.Disguised) { if (self.Owner == self.World.LocalPlayer) return self.Owner; - return spy.disguisedAsPlayer; + + return disguise.AsPlayer; } return self.Owner; } - - public SpyToolTip( Actor self, TooltipInfo info ) - { - this.self = self; - Info = info; - spy = self.Trait(); - } } + class DisguiseInfo : TraitInfo { } - class SpyInfo : TraitInfo { } - - class Spy : IIssueOrder, IResolveOrder, IOrderVoice, IRadarColorModifier, INotifyAttack + class Disguise : IEffectiveOwner, IIssueOrder, IResolveOrder, IOrderVoice, IRadarColorModifier, INotifyAttack { - public Player disguisedAsPlayer; - public string disguisedAsSprite, disguisedAsName; + public Player AsPlayer; + public string AsSprite; + public string AsName; - public bool Disguised { get { return disguisedAsPlayer != null; } } + public bool Disguised { get { return AsPlayer != null; } } + public Player Owner { get { return AsPlayer; } } public IEnumerable Orders { get { - yield return new TargetTypeOrderTargeter("Disguise", "Disguise", 7, "ability", true, true) { ForceAttack=false }; + yield return new TargetTypeOrderTargeter("Disguise", "Disguise", 7, "ability", true, true) { ForceAttack = false }; } } public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) { - if( order.OrderID == "Disguise" ) + if (order.OrderID == "Disguise") return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + return null; } @@ -97,7 +101,7 @@ namespace OpenRA.Mods.RA public string VoicePhraseForOrder(Actor self, Order order) { - return (order.OrderString == "Disguise") ? "Attack" : null; + return order.OrderString == "Disguise" ? "Attack" : null; } public Color RadarColorOverride(Actor self) @@ -105,28 +109,27 @@ namespace OpenRA.Mods.RA if (!Disguised || self.Owner.IsAlliedWith(self.World.RenderPlayer)) return self.Owner.Color.RGB; - return disguisedAsPlayer.Color.RGB; + return AsPlayer.Color.RGB; } void DisguiseAs(Actor target) { var tooltip = target.TraitsImplementing().FirstOrDefault(); - disguisedAsName = tooltip.Name(); - disguisedAsPlayer = tooltip.Owner(); - disguisedAsSprite = target.Trait().GetImage(target); + AsName = tooltip.Name(); + AsPlayer = tooltip.Owner(); + AsSprite = target.Trait().GetImage(target); } void DropDisguise() { - disguisedAsName = null; - disguisedAsPlayer = null; - disguisedAsSprite = null; + AsName = null; + AsPlayer = null; + AsSprite = null; } - /* lose our disguise if we attack anything */ public void Attacking(Actor self, Target target, Armament a, Barrel barrel) { DropDisguise(); } } class IgnoresDisguiseInfo : TraitInfo {} class IgnoresDisguise {} -} +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Effects/GpsDot.cs b/OpenRA.Mods.RA/Effects/GpsDot.cs index 3497ba39b1..edbe4fc485 100644 --- a/OpenRA.Mods.RA/Effects/GpsDot.cs +++ b/OpenRA.Mods.RA/Effects/GpsDot.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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, @@ -35,7 +35,7 @@ namespace OpenRA.Mods.RA.Effects Lazy huf; Lazy fuf; - Lazy spy; + Lazy disguise; Lazy cloak; Cache watcher; Cache frozen; @@ -53,7 +53,7 @@ namespace OpenRA.Mods.RA.Effects huf = Lazy.New(() => self.TraitOrDefault()); fuf = Lazy.New(() => self.TraitOrDefault()); - spy = Lazy.New(() => self.TraitOrDefault()); + disguise = Lazy.New(() => self.TraitOrDefault()); cloak = Lazy.New(() => self.TraitOrDefault()); watcher = new Cache(p => p.PlayerActor.Trait()); @@ -65,7 +65,7 @@ namespace OpenRA.Mods.RA.Effects if (cloak.Value != null && cloak.Value.Cloaked) return false; - if (spy.Value != null && spy.Value.Disguised) + if (disguise.Value != null && disguise.Value.Disguised) return false; if (huf.Value != null && !huf.Value.IsVisible(self, self.World.RenderPlayer)) diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 1dc2c1ffbf..e88021fff3 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -320,7 +320,7 @@ - + @@ -348,7 +348,7 @@ - + diff --git a/OpenRA.Mods.RA/Render/RenderDisguise.cs b/OpenRA.Mods.RA/Render/RenderDisguise.cs new file mode 100644 index 0000000000..85f39b46f6 --- /dev/null +++ b/OpenRA.Mods.RA/Render/RenderDisguise.cs @@ -0,0 +1,50 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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 + +namespace OpenRA.Mods.RA.Render +{ + class RenderDisguiseInfo : RenderInfantryProneInfo + { + public override object Create(ActorInitializer init) { return new RenderDisguise(init.self, this); } + } + + class RenderDisguise : RenderInfantryProne + { + RenderDisguiseInfo info; + string intendedSprite; + Disguise disguise; + + public RenderDisguise(Actor self, RenderDisguiseInfo info) + : base(self, info) + { + this.info = info; + disguise = self.Trait(); + intendedSprite = disguise.AsSprite; + } + + protected override string PaletteName(Actor self) + { + var player = disguise.AsPlayer ?? self.Owner; + return info.Palette ?? info.PlayerPalette + player.InternalName; + } + + public override void Tick(Actor self) + { + if (disguise.AsSprite != intendedSprite) + { + intendedSprite = disguise.AsSprite; + anim.ChangeImage(intendedSprite ?? GetImage(self), info.StandAnimations.Random(Game.CosmeticRandom)); + UpdatePalette(); + } + + base.Tick(self); + } + } +} diff --git a/OpenRA.Mods.RA/Render/RenderSpy.cs b/OpenRA.Mods.RA/Render/RenderSpy.cs deleted file mode 100755 index 6fb78c4a5b..0000000000 --- a/OpenRA.Mods.RA/Render/RenderSpy.cs +++ /dev/null @@ -1,48 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2011 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 - -namespace OpenRA.Mods.RA.Render -{ - class RenderSpyInfo : RenderInfantryProneInfo - { - public override object Create(ActorInitializer init) { return new RenderSpy(init.self, this); } - } - - class RenderSpy : RenderInfantryProne - { - RenderSpyInfo info; - string disguisedAsSprite; - Spy spy; - - public RenderSpy(Actor self, RenderSpyInfo info) : base(self, info) - { - this.info = info; - spy = self.Trait(); - disguisedAsSprite = spy.disguisedAsSprite; - } - - protected override string PaletteName(Actor self) - { - var player = spy.disguisedAsPlayer ?? self.Owner; - return info.Palette ?? info.PlayerPalette + player.InternalName; - } - - public override void Tick(Actor self) - { - if (spy.disguisedAsSprite != disguisedAsSprite) - { - disguisedAsSprite = spy.disguisedAsSprite; - anim.ChangeImage(disguisedAsSprite ?? GetImage(self), info.StandAnimations.Random(Game.CosmeticRandom)); - UpdatePalette(); - } - base.Tick(self); - } - } -} diff --git a/OpenRA.Utility/UpgradeRules.cs b/OpenRA.Utility/UpgradeRules.cs index 33154ffab3..f87cbe2c13 100644 --- a/OpenRA.Utility/UpgradeRules.cs +++ b/OpenRA.Utility/UpgradeRules.cs @@ -150,6 +150,19 @@ namespace OpenRA.Utility node.Key = "Immobile"; } + // Spy was renamed to Disguise + if (engineVersion < 20140314) + { + if (depth == 1 && node.Key == "Spy") + node.Key = "Disguise"; + + if (depth == 1 && node.Key == "SpyToolTip") + node.Key = "DisguiseToolTip"; + + if (depth == 1 && node.Key == "RenderSpy") + node.Key = "RenderDisguise"; + } + UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); } } diff --git a/mods/ra/rules/infantry.yaml b/mods/ra/rules/infantry.yaml index 61bed1839b..26be6553d6 100644 --- a/mods/ra/rules/infantry.yaml +++ b/mods/ra/rules/infantry.yaml @@ -194,7 +194,7 @@ SPY: Hotkey: p Valued: Cost: 500 - SpyToolTip: + DisguiseToolTip: Name: Spy Description: Infiltrates enemy structures to gather \nintelligence. Exact effect depends on the \nbuilding infiltrated.\n Strong vs Nothing\n Weak vs Everything\n Special Ability: Disguised Selectable: @@ -209,12 +209,12 @@ SPY: Passenger: PipType: Yellow TakeCover: - Spy: + Disguise: Infiltrates: Types: Cash, SupportPower, Exploration -AutoTarget: -RenderInfantry: - RenderSpy: + RenderDisguise: IdleAnimations: idle1,idle2 StandAnimations: stand,stand2 Armament: diff --git a/mods/ts/rules/infantry.yaml b/mods/ts/rules/infantry.yaml index 8c2fcee6ff..4534c32185 100644 --- a/mods/ts/rules/infantry.yaml +++ b/mods/ts/rules/infantry.yaml @@ -273,7 +273,7 @@ CHAMSPY: Hotkey: p Valued: Cost: 700 - SpyToolTip: + DisguiseToolTip: Name: Chameleon Spy Description: Infiltrates enemy structures to gather \nintelligence. Exact effect depends on the \nbuilding infiltrated.\n Strong vs Nothing\n Weak vs Everything\n Special Ability: Disguised Selectable: @@ -287,12 +287,12 @@ CHAMSPY: Range: 9c0 Passenger: TakeCover: - Spy: + Disguise: Infiltrates: Types: Cash, SupportPower, Exploration -AutoTarget: -RenderInfantry: - RenderSpy: + RenderDisguise: IdleAnimations: idle1,idle2 CYBORG: