diff --git a/OpenRA.Game/GameRules/Settings.cs b/OpenRA.Game/GameRules/Settings.cs index 7fe4a0a860..52166860f1 100644 --- a/OpenRA.Game/GameRules/Settings.cs +++ b/OpenRA.Game/GameRules/Settings.cs @@ -151,6 +151,7 @@ namespace OpenRA.GameRules public string ScatterKey = "x"; public string DeployKey = "f"; public string StanceCycleKey = "z"; + public string GuardKey = "d"; public string CycleTabsKey = "tab"; } diff --git a/OpenRA.Mods.RA/Guard.cs b/OpenRA.Mods.RA/Guard.cs new file mode 100644 index 0000000000..6c0696cc85 --- /dev/null +++ b/OpenRA.Mods.RA/Guard.cs @@ -0,0 +1,98 @@ +#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.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Mods.RA.Activities; +using OpenRA.Mods.RA.Move; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + class GuardInfo : TraitInfo, Requires { } + + class Guard : IResolveOrder, IOrderVoice + { + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Guard") + { + var target = Target.FromActor(order.TargetActor); + self.SetTargetLine(target, Color.Yellow); + self.QueueActivity(false, new AttackMove.AttackMoveActivity(self, + new Follow(target, target.Actor.Info.Traits.Get().Range))); + } + } + + public string VoicePhraseForOrder(Actor self, Order order) + { + return order.OrderString == "Guard" ? "Move" : null; + } + } + + class GuardOrderGenerator : IOrderGenerator + { + readonly IEnumerable subjects; + + public GuardOrderGenerator(IEnumerable subjects) + { + this.subjects = subjects; + } + + public IEnumerable Order(World world, CPos xy, MouseInput mi) + { + if (mi.Button == Game.mouseButtonPreference.Cancel) + { + world.CancelInputMode(); + yield break; + } + + var target = FriendlyGuardableUnitsAtMouse(world, mi).FirstOrDefault(); + + if (target == null || subjects.All(s => s.IsDead())) + yield break; + + foreach (var actor in subjects) + yield return new Order("Guard", actor, false) { TargetActor = target }; + } + + public void Tick(World world) + { + if (subjects.All(s => s.IsDead() || !s.HasTrait())) + world.CancelInputMode(); + } + + public void RenderBeforeWorld(WorldRenderer wr, World world) { } + public void RenderAfterWorld(WorldRenderer wr, World world) { } + + public string GetCursor(World world, CPos xy, MouseInput mi) + { + return world.Map.IsInMap(xy) + && FriendlyGuardableUnitsAtMouse(world, mi).Any() + ? "guard" + : "move-blocked"; + } + + static IEnumerable FriendlyGuardableUnitsAtMouse(World world, MouseInput mi) + { + return world.FindUnitsAtMouse(mi.Location) + .Where(a => !a.IsDead() && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait()); + } + } + + class GuardableInfo : TraitInfo + { + public readonly int Range = 2; + } + + class Guardable { } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 52529e9f6c..69be5dc366 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -1,4 +1,4 @@ - + Debug @@ -229,6 +229,7 @@ + diff --git a/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs index bea9cf55dc..c527591091 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/SettingsMenuLogic.cs @@ -208,6 +208,10 @@ namespace OpenRA.Mods.RA.Widgets.Logic SetupKeyBinding(deployKey, "Deploy:", () => keyConfig.DeployKey, k => keyConfig.DeployKey = k); unitCommandHotkeyList.AddChild(deployKey); + var guardKey = ScrollItemWidget.Setup(unitCommandHotkeyTemplate, () => false, () => { }); + SetupKeyBinding(guardKey, "Guard: ", () => keyConfig.GuardKey, k => keyConfig.GuardKey = k); + unitCommandHotkeyList.AddChild(guardKey); + // Debug var debug = bg.Get("DEBUG_PANE"); diff --git a/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs b/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs index 8e7ea65594..b9c54387ec 100644 --- a/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs +++ b/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs @@ -70,6 +70,9 @@ namespace OpenRA.Mods.RA.Widgets if (e.KeyName == Game.Settings.Keys.StanceCycleKey) return PerformStanceCycle(); + + if (e.KeyName == Game.Settings.Keys.GuardKey) + return PerformGuard(); } return false; @@ -79,12 +82,11 @@ namespace OpenRA.Mods.RA.Widgets bool PerformAttackMove() { - var actors = World.Selection.Actors - .Where(a => a.Owner == World.LocalPlayer).ToArray(); + var actors = World.Selection.Actors.Where(a => a.Owner == World.LocalPlayer).ToArray(); - if (actors.Length > 0) - World.OrderGenerator = new GenericSelectTarget(actors, "AttackMove", - "attackmove", Game.mouseButtonPreference.Action); + if (actors.Any()) + World.OrderGenerator = new GenericSelectTarget(actors, + "AttackMove", "attackmove", Game.mouseButtonPreference.Action); return true; } @@ -146,6 +148,16 @@ namespace OpenRA.Mods.RA.Widgets return true; } + bool PerformGuard() + { + var actors = World.Selection.Actors.Where(a => a.Owner == World.LocalPlayer && a.HasTrait()); + + if (actors.Any()) + World.OrderGenerator = new GuardOrderGenerator(actors); + + return true; + } + bool CycleBases() { var bases = World.ActorsWithTrait() diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index be63c1339c..f486b9afef 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -31,6 +31,8 @@ AcceptsCloakCrate: WithSmoke: DebugMuzzlePositions: + Guard: + Guardable: ^Tank: AppearsOnRadar: @@ -68,6 +70,8 @@ Weapon: UnitExplodeSmall EmptyWeapon: UnitExplodeSmall DebugMuzzlePositions: + Guard: + Guardable: ^Helicopter: AppearsOnRadar: @@ -145,6 +149,8 @@ Buildings: hosp CloseEnough: 1 DebugMuzzlePositions: + Guard: + Guardable: ^CivInfantry: Inherits: ^Infantry @@ -176,7 +182,7 @@ Notification: CivilianKilled NotifyAll: true ScaredyCat: - RenderInfantryPanic: + RenderInfantryPanic: AttackMove: JustMove: yes CrushableInfantry: @@ -214,6 +220,8 @@ ActorLostNotification: AttackMove: DebugMuzzlePositions: + Guard: + Guardable: ^Building: AppearsOnRadar: @@ -260,6 +268,8 @@ Capturable: CapturableBar: DebugMuzzlePositions: + Guardable: + Range: 3 ^CivBuilding: Inherits: ^Building @@ -347,6 +357,7 @@ RelativeToTopLeft: yes AutoTargetIgnore: Sellable: + Guardable: ^Tree: Tooltip: diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index f11c1130df..81c8803b2f 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -31,6 +31,8 @@ Repairable: RepairBuildings: repair DebugMuzzlePositions: + Guard: + Guardable: ^Tank: AppearsOnRadar: @@ -65,6 +67,8 @@ Repairable: RepairBuildings: repair DebugMuzzlePositions: + Guard: + Guardable: ^Husk: Health: @@ -145,6 +149,8 @@ Buildings: barra, barro CloseEnough: 1 DebugMuzzlePositions: + Guard: + Guardable: ^Plane: AppearsOnRadar: @@ -200,7 +206,6 @@ ActorTypes: rifle,rifle,rifle,rifle,rifle,bazooka,bazooka,bazooka,engineer MustBeDestroyed: GivesExperience: - FrozenUnderFog: CaptureNotification: ActorLostNotification: @@ -215,3 +220,5 @@ GivesBounty: DebugMuzzlePositions: Bib: + Guardable: + Range: 3 diff --git a/mods/d2k/rules/structures.yaml b/mods/d2k/rules/structures.yaml index 1aa1d45850..14da641683 100644 --- a/mods/d2k/rules/structures.yaml +++ b/mods/d2k/rules/structures.yaml @@ -371,6 +371,7 @@ WALL: ProximityCaptor: Types:Wall Sellable: + Guardable: GUNTOWER: Inherits: ^Building diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 1d4af8da3e..ee043ad8b4 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -34,6 +34,8 @@ WithSmoke: UpdatesPlayerStatistics: DebugMuzzlePositions: + Guard: + Guardable: ^Tank: AppearsOnRadar: @@ -71,6 +73,8 @@ WithSmoke: UpdatesPlayerStatistics: DebugMuzzlePositions: + Guard: + Guardable: ^Infantry: AppearsOnRadar: @@ -122,6 +126,8 @@ CloseEnough: 1 UpdatesPlayerStatistics: DebugMuzzlePositions: + Guard: + Guardable: ^Ship: AppearsOnRadar: @@ -151,6 +157,8 @@ WithSmoke: UpdatesPlayerStatistics: DebugMuzzlePositions: + Guard: + Guardable: ^Plane: AppearsOnRadar: @@ -224,6 +232,8 @@ GivesBounty: UpdatesPlayerStatistics: DebugMuzzlePositions: + Guardable: + Range: 3 ^Wall: AppearsOnRadar: @@ -257,6 +267,7 @@ Types:Wall Sellable: UpdatesPlayerStatistics: + Guardable: ^TechBuilding: Inherits: ^Building