diff --git a/OpenRA.Mods.RA/ConquestVictoryConditions.cs b/OpenRA.Mods.RA/ConquestVictoryConditions.cs index e0f5c9048e..7a5618c86f 100644 --- a/OpenRA.Mods.RA/ConquestVictoryConditions.cs +++ b/OpenRA.Mods.RA/ConquestVictoryConditions.cs @@ -13,80 +13,79 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - public class ConquestVictoryConditionsInfo : ITraitInfo + public class ConquestVictoryConditionsInfo : ITraitInfo, Requires { - [Desc("Milliseconds")] + [Desc("Delay for the end game notification in milliseconds.")] public int NotificationDelay = 1500; - public object Create(ActorInitializer init) { return new ConquestVictoryConditions(init.world, this); } + public object Create(ActorInitializer init) { return new ConquestVictoryConditions(init.self, this); } } - public class ConquestVictoryConditions : ITick, IResolveOrder + public class ConquestVictoryConditions : ITick, IResolveOrder, INotifyObjectivesUpdated { - ConquestVictoryConditionsInfo Info; - public ConquestVictoryConditions(World world, ConquestVictoryConditionsInfo info) + readonly ConquestVictoryConditionsInfo info; + readonly MissionObjectives mo; + int objectiveID = -1; + + public ConquestVictoryConditions(Actor self, ConquestVictoryConditionsInfo cvcInfo) { - world.ObserveAfterWinOrLose = true; - Info = info; + info = cvcInfo; + mo = self.Trait(); } public void Tick(Actor self) { if (self.Owner.WinState != WinState.Undefined || self.Owner.NonCombatant) return; - var hasAnything = self.World.ActorsWithTrait() - .Any(a => a.Actor.Owner == self.Owner); + if (objectiveID < 0) + objectiveID = mo.Add(self.Owner, "Destroy all opposition!"); - if (!hasAnything && !self.Owner.NonCombatant) - Lose(self); + if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits()) + mo.MarkFailed(self.Owner, objectiveID); var others = self.World.Players.Where(p => !p.NonCombatant - && p != self.Owner && p.Stances[self.Owner] != Stance.Ally); + && !p.IsAlliedWith(self.Owner)); if (!others.Any()) return; if (others.All(p => p.WinState == WinState.Lost)) - Win(self); + mo.MarkCompleted(self.Owner, objectiveID); } public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "Surrender") - Lose(self); + mo.MarkFailed(self.Owner, objectiveID); } - public void Lose(Actor self) + public void OnPlayerLost(Player player) { - if (self.Owner.WinState == WinState.Lost) return; - self.Owner.WinState = WinState.Lost; - self.World.OnPlayerWinStateChanged(self.Owner); + Game.Debug("{0} is defeated.".F(player.PlayerName)); - Game.Debug("{0} is defeated.".F(self.Owner.PlayerName)); - - foreach (var a in self.World.Actors.Where(a => a.Owner == self.Owner)) + foreach (var a in player.World.Actors.Where(a => a.Owner == player)) a.Kill(a); - if (self.Owner == self.World.LocalPlayer) + if (player == player.World.LocalPlayer) { - Game.RunAfterDelay(Info.NotificationDelay, () => + Game.RunAfterDelay(info.NotificationDelay, () => { - if (Game.IsCurrentWorld(self.World)) - Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", "Lose", self.Owner.Country.Race); + if (Game.IsCurrentWorld(player.World)) + Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Lose", player.Country.Race); }); } } - public void Win(Actor self) + public void OnPlayerWon(Player player) { - if (self.Owner.WinState == WinState.Won) return; - self.Owner.WinState = WinState.Won; - self.World.OnPlayerWinStateChanged(self.Owner); + Game.Debug("{0} is victorious.".F(player.PlayerName)); - Game.Debug("{0} is victorious.".F(self.Owner.PlayerName)); - - if (self.Owner == self.World.LocalPlayer) - Game.RunAfterDelay(Info.NotificationDelay, () => Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", "Win", self.Owner.Country.Race)); + if (player == player.World.LocalPlayer) + Game.RunAfterDelay(info.NotificationDelay, () => Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Country.Race)); } + + public void OnObjectiveAdded(Player player, int id) {} + public void OnObjectiveCompleted(Player player, int id) {} + public void OnObjectiveFailed(Player player, int id) {} } [Desc("Tag trait for things that must be destroyed for a short game to end.")] diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index bc21912282..132c5cebc8 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -552,6 +552,7 @@ + diff --git a/OpenRA.Mods.RA/Player/Extensions.cs b/OpenRA.Mods.RA/Player/Extensions.cs new file mode 100644 index 0000000000..840ef08ec7 --- /dev/null +++ b/OpenRA.Mods.RA/Player/Extensions.cs @@ -0,0 +1,23 @@ +#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 + +using System.Linq; +using OpenRA.Mods.RA; + +namespace OpenRA +{ + public static class Extensions + { + public static bool HasNoRequiredUnits(this Player player) + { + return player.World.ActorsWithTrait().All(p => p.Actor.Owner != player); + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs b/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs index 84cf7020fc..fd304d4e83 100644 --- a/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs +++ b/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs @@ -319,7 +319,7 @@ namespace OpenRA.Mods.RA.Scripting [LuaGlobal] public bool RequiredUnitsAreDestroyed(Player player) { - return world.ActorsWithTrait().All(p => p.Actor.Owner != player); + return player.HasNoRequiredUnits(); } [LuaGlobal] diff --git a/OpenRA.Mods.RA/StrategicVictoryConditions.cs b/OpenRA.Mods.RA/StrategicVictoryConditions.cs index 86c1344741..9e079990bb 100644 --- a/OpenRA.Mods.RA/StrategicVictoryConditions.cs +++ b/OpenRA.Mods.RA/StrategicVictoryConditions.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, @@ -17,41 +17,68 @@ namespace OpenRA.Mods.RA public class StrategicPointInfo : TraitInfo {} public class StrategicPoint {} - public class StrategicVictoryConditionsInfo : ITraitInfo, Requires + public class StrategicVictoryConditionsInfo : ITraitInfo, Requires { + [Desc("Amount of time (in game ticks) that the player has to hold all the strategic points.")] public readonly int TicksToHold = 25 * 60 * 5; // ~5 minutes + + [Desc("Should the timer reset when the player loses hold of a strategic point.")] public readonly bool ResetOnHoldLost = true; + + [Desc("Percentage of strategic points the player has to hold to win.")] public readonly float RatioRequired = 0.5f; // 50% required of all koth locations + [Desc("Delay for the end game notification in milliseconds.")] + public int NotificationDelay = 1500; + public object Create(ActorInitializer init) { return new StrategicVictoryConditions(init.self, this); } } - public class StrategicVictoryConditions : ITick, ISync + public class StrategicVictoryConditions : ITick, ISync, INotifyObjectivesUpdated { - Actor self; - StrategicVictoryConditionsInfo info; + readonly StrategicVictoryConditionsInfo info; - [Sync] public int TicksLeft = 0; + [Sync] public int TicksLeft; + readonly Player player; + readonly MissionObjectives mo; + int objectiveID = -1; - public StrategicVictoryConditions(Actor self, StrategicVictoryConditionsInfo info) + public StrategicVictoryConditions(Actor self, StrategicVictoryConditionsInfo svcInfo) { - this.self = self; - this.info = info; + info = svcInfo; + TicksLeft = info.TicksToHold; + player = self.Owner; + mo = self.Trait(); } public IEnumerable> AllPoints { - get { return self.World.ActorsWithTrait(); } + get { return player.World.ActorsWithTrait(); } } public int Total { get { return AllPoints.Count(); } } - int Owned { get { return AllPoints.Count( a => WorldUtils.AreMutualAllies( self.Owner, a.Actor.Owner )); } } + int Owned { get { return AllPoints.Count(a => WorldUtils.AreMutualAllies(player, a.Actor.Owner)); } } public bool Holding { get { return Owned >= info.RatioRequired * Total; } } public void Tick(Actor self) { - if (self.Owner.WinState != WinState.Undefined || self.Owner.NonCombatant) return; + if (player.WinState != WinState.Undefined || player.NonCombatant) return; + + if (objectiveID < 0) + objectiveID = mo.Add(player, "Hold all the strategic positions for a specified time!"); + + if (!self.Owner.NonCombatant && self.Owner.HasNoRequiredUnits()) + mo.MarkFailed(self.Owner, objectiveID); + + var others = self.World.Players.Where(p => !p.NonCombatant + && !p.IsAlliedWith(self.Owner)); + + if (others.All(p => p.WinState == WinState.Lost)) + mo.MarkCompleted(player, objectiveID); + + if (others.Any(p => p.WinState == WinState.Won)) + mo.MarkFailed(player, objectiveID); // See if any of the conditions are met to increase the count if (Total > 0) @@ -59,10 +86,8 @@ namespace OpenRA.Mods.RA if (Holding) { // Hah! We met ths critical owned condition - if (TicksLeft == 0) - TicksLeft = info.TicksToHold; // first tick -- this is crap. - else if (--TicksLeft == 0) - Won(); + if (--TicksLeft == 0) + mo.MarkCompleted(player, objectiveID); } else if (TicksLeft != 0) if (info.ResetOnHoldLost) @@ -70,18 +95,33 @@ namespace OpenRA.Mods.RA } } - void Won() + public void OnPlayerLost(Player player) { - // Player has won - foreach (var p in self.World.Players) - { - var cvc = p.PlayerActor.Trait(); + Game.Debug("{0} is defeated.".F(player.PlayerName)); - if (p.WinState == WinState.Undefined && WorldUtils.AreMutualAllies(self.Owner, p)) - cvc.Win(p.PlayerActor); - else if (p.WinState == WinState.Undefined) - cvc.Lose(p.PlayerActor); + foreach (var a in player.World.Actors.Where(a => a.Owner == player)) + a.Kill(a); + + if (player == player.World.LocalPlayer) + { + Game.RunAfterDelay(info.NotificationDelay, () => + { + if (Game.IsCurrentWorld(player.World)) + Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Lose", player.Country.Race); + }); } } + + public void OnPlayerWon(Player player) + { + Game.Debug("{0} is victorious.".F(player.PlayerName)); + + if (player == player.World.LocalPlayer) + Game.RunAfterDelay(info.NotificationDelay, () => Sound.PlayNotification(player.World.Map.Rules, player, "Speech", "Win", player.Country.Race)); + } + + public void OnObjectiveAdded(Player player, int id) {} + public void OnObjectiveCompleted(Player player, int id) {} + public void OnObjectiveFailed(Player player, int id) {} } } diff --git a/mods/ra/maps/koth-athena/map.yaml b/mods/ra/maps/koth-athena/map.yaml index 7dd203f32a..31441ff12b 100644 --- a/mods/ra/maps/koth-athena/map.yaml +++ b/mods/ra/maps/koth-athena/map.yaml @@ -2113,6 +2113,7 @@ Rules: -Selectable: -TargetableBuilding: Player: + -ConquestVictoryConditions: StrategicVictoryConditions: TicksToHold: 4500 ResetOnHoldLost: true diff --git a/mods/ra/maps/koth-crossroads/map.yaml b/mods/ra/maps/koth-crossroads/map.yaml index b5e2a96984..9e70dd1f57 100644 --- a/mods/ra/maps/koth-crossroads/map.yaml +++ b/mods/ra/maps/koth-crossroads/map.yaml @@ -264,6 +264,7 @@ Rules: -Selectable: -TargetableBuilding: Player: + -ConquestVictoryConditions: StrategicVictoryConditions: TicksToHold: 3000 ResetOnHoldLost: true