diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index 6e48aa3145..73311e8565 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -60,6 +60,8 @@
+
+
diff --git a/OpenRA.Mods.RA/Strategic/StrategicPoint.cs b/OpenRA.Mods.RA/Strategic/StrategicPoint.cs
new file mode 100644
index 0000000000..d798664b66
--- /dev/null
+++ b/OpenRA.Mods.RA/Strategic/StrategicPoint.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA
+{
+ public class StrategicPointInfo : ITraitInfo
+ {
+ public readonly bool Critical = false;
+
+ public object Create(ActorInitializer init) { return new StrategicPoint(init.self, this); }
+ }
+
+ public class StrategicPoint : INotifyCapture, ITick
+ {
+ [Sync] public Actor Self;
+ [Sync] public bool Critical;
+ [Sync] public Player OriginalOwner;
+ [Sync] public int TicksOwned = 0;
+
+ public StrategicPointInfo Info;
+
+ public StrategicPoint(Actor self, StrategicPointInfo info)
+ {
+ Self = self;
+ Info = info;
+ OriginalOwner = self.Owner;
+
+ Critical = info.Critical;
+ }
+
+ public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
+ {
+ TicksOwned = 0;
+ }
+
+ public void Tick(Actor self)
+ {
+ if (OriginalOwner == self.Owner || self.Owner.WinState != WinState.Undefined) return;
+
+ TicksOwned++;
+ }
+ }
+}
diff --git a/OpenRA.Mods.RA/Strategic/StrategicVictoryConditions.cs b/OpenRA.Mods.RA/Strategic/StrategicVictoryConditions.cs
new file mode 100644
index 0000000000..111b70b9fb
--- /dev/null
+++ b/OpenRA.Mods.RA/Strategic/StrategicVictoryConditions.cs
@@ -0,0 +1,211 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2010 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 LICENSE.
+ */
+#endregion
+
+using System;
+using System.Linq;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.RA
+{
+ ///
+ /// Attach to players only kthx :)
+ ///
+ public class StrategicVictoryConditionsInfo : ITraitInfo
+ {
+ public readonly int TicksToHold = 25 * 60 * 5; // ~5 minutes
+ public readonly bool ResetOnHoldLost = true;
+ public readonly float RatioRequired = 0.5f; // 50% required of all koth locations
+ public readonly float CriticalRatioRequired = 1f; // if someone owns 100% of all critical locations
+ public readonly bool SplitHolds = true; // disallow or allow the 'holdsrequired' to include critical locations
+
+ public object Create(ActorInitializer init) { return new StrategicVictoryConditions(init.self, this); }
+ }
+
+ public class StrategicVictoryConditions : ITick
+ {
+ [Sync] public Actor Self;
+ [Sync] public StrategicVictoryConditionsInfo Info;
+
+ [Sync] public int TicksToHold;
+ [Sync] public bool ResetOnHoldLost;
+ [Sync] public float RatioRequired;
+ [Sync] public float CriticalRatioRequired;
+ [Sync] public bool SplitHolds;
+ [Sync] public int TicksLeft = 0;
+ [Sync] public int CriticalTicksLeft = 0;
+
+ public StrategicVictoryConditions(Actor self, StrategicVictoryConditionsInfo info)
+ {
+ Self = self;
+ Info = info;
+
+ TicksToHold = info.TicksToHold;
+ ResetOnHoldLost = info.ResetOnHoldLost;
+ RatioRequired = info.RatioRequired;
+ CriticalRatioRequired = info.CriticalRatioRequired;
+ SplitHolds = info.SplitHolds;
+ }
+
+ ///
+ /// Includes your allies as well
+ ///
+ public int Owned
+ {
+ get { return (SplitHolds) ? CountOwnedPoints(false) : CountOwnedPoints(false) + OwnedCritical; }
+ }
+
+ ///
+ /// Includes your allies as well
+ ///
+ public int OwnedCritical
+ {
+ get { return CountOwnedPoints(true); }
+ }
+
+ public int Total
+ {
+ get
+ {
+ return (SplitHolds) ? Self.World.Actors.Where(a => !a.Destroyed && a.HasTrait() && a.TraitOrDefault().Critical == false).Count() : Self.World.Actors.Where(a => a.HasTrait()).Count();
+ }
+ }
+
+ public int TotalCritical
+ {
+ get
+ {
+ return Self.World.Actors.Where(a => !a.Destroyed && a.HasTrait() && a.TraitOrDefault().Critical).Count();
+ }
+ }
+
+ public int CountOwnedPoints(bool critical)
+ {
+ int total = 0;
+
+ foreach (var p in Self.World.players.Select(k => k.Value))
+ {
+ if (p == Self.Owner || (p.Stances[Self.Owner] == Stance.Ally && Self.Owner.Stances[p] == Stance.Ally))
+ {
+ total += Self.World.Queries.OwnedBy[p].Where(a => a.HasTrait() && a.TraitOrDefault().Critical == critical).Count();
+ }
+ }
+ return total;
+ }
+
+ public bool HoldingCritical
+ {
+ get
+ {
+ var criticalOwned = 1f / TotalCritical * OwnedCritical;
+
+ return (criticalOwned >= CriticalRatioRequired);
+ }
+ }
+
+ public bool Holding
+ {
+ get
+ {
+ var owned = 1f / Total * Owned;
+
+ return (owned >= RatioRequired);
+ }
+ }
+
+ public void Tick(Actor self)
+ {
+ if (self.Owner.WinState != WinState.Undefined || self.Owner.NonCombatant) return;
+
+ var cvc = self.TraitOrDefault();
+ if (cvc == null)
+ return; // Cannot work without ConquestVictoryConditions
+
+ // See if any of the conditions are met to increase the count
+ if (TotalCritical > 0)
+ {
+ if (HoldingCritical)
+ {
+ // Hah! We met ths critical owned condition
+ if (CriticalTicksLeft == 0)
+ {
+ // First time
+ CriticalTicksLeft = TicksToHold;
+ }
+ else
+ {
+ // nth time
+ if (--CriticalTicksLeft == 0)
+ {
+ // Player & allies have won!
+ Won();
+ }
+ }
+ }
+ else if (CriticalTicksLeft != 0)
+ {
+ // we lost the hold :/
+ if (ResetOnHoldLost)
+ {
+ CriticalTicksLeft = TicksToHold; // Reset the time hold
+ }
+ }
+ }
+
+ // See if any of the conditions are met to increase the count
+ if (Total > 0)
+ {
+ if (Holding)
+ {
+ // Hah! We met ths critical owned condition
+ if (TicksLeft == 0)
+ {
+ // First time
+ TicksLeft = TicksToHold;
+ }
+ else
+ {
+ // nth time
+ if (--TicksLeft == 0)
+ {
+ // Player & allies have won!
+ Won();
+ }
+ }
+ }
+ else if (TicksLeft != 0)
+ {
+ // we lost the hold :/
+ if (ResetOnHoldLost)
+ {
+ TicksLeft = TicksToHold; // Reset the time hold
+ }
+ }
+ }
+ }
+
+ public void Won()
+ {
+ // Player has won
+ foreach (var p in Self.World.players.Select(k => k.Value))
+ {
+ var cvc = p.PlayerActor.TraitOrDefault();
+
+ if ((p.WinState == WinState.Undefined) && (p == Self.Owner || (p.Stances[Self.Owner] == Stance.Ally && Self.Owner.Stances[p] == Stance.Ally)))
+ {
+ cvc.Win(p.PlayerActor);
+ }
+ else if (p.WinState == WinState.Undefined)
+ {
+ cvc.Surrender(p.PlayerActor);
+ }
+ }
+ }
+ }
+}