From 58472bf09089ada52d405d87fcac681b5472d9f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matija=20Husti=C4=87?= Date: Sun, 5 Jul 2015 22:04:03 +0100 Subject: [PATCH] Timed upgrades track sources. --- .../Traits/Upgrades/UpgradeManager.cs | 61 ++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs index 983bf4e8f7..81a7765d1e 100644 --- a/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs +++ b/OpenRA.Mods.Common/Traits/Upgrades/UpgradeManager.cs @@ -28,18 +28,37 @@ namespace OpenRA.Mods.Common.Traits { class TimedUpgrade { + public class UpgradeSource + { + public readonly object Source; + public int Remaining; + + public UpgradeSource(int duration, object source) + { + Remaining = duration; + Source = source; + } + } + public readonly string Upgrade; public readonly int Duration; - public int Remaining; + public readonly HashSet Sources; + public int Remaining; // Equal to maximum of all Sources.Remaining - public TimedUpgrade(string upgrade, int duration) + public TimedUpgrade(string upgrade, int duration, object source) { Upgrade = upgrade; Duration = duration; Remaining = duration; + Sources = new HashSet { new UpgradeSource(duration, source) }; } - public void Tick() { Remaining--; } + public void Tick() + { + Remaining--; + foreach (var source in Sources) + source.Remaining--; + } } class UpgradeState @@ -66,17 +85,36 @@ namespace OpenRA.Mods.Common.Traits }); } - public void GrantTimedUpgrade(Actor self, string upgrade, int duration) + /// Upgrade level increments are limited to one per source, i.e., if a single source + /// attempts granting multiple upgrades, they will not accumulate. They will replace each other + /// instead, leaving only the last granted upgrade active. An upgrade from each new distinct + /// source will increment the upgrade's level until AcceptsUpgrade starts returning false. Then, + /// when no new levels are accepted, the upgrade source with the shortest remaining upgrade + /// duration will be replaced by the new source. + public void GrantTimedUpgrade(Actor self, string upgrade, int duration, object source = null) { var timed = timedUpgrades.FirstOrDefault(u => u.Upgrade == upgrade); if (timed == null) { - timed = new TimedUpgrade(upgrade, duration); + timed = new TimedUpgrade(upgrade, duration, source); timedUpgrades.Add(timed); GrantUpgrade(self, upgrade, timed); + return; + } + + var src = timed.Sources.FirstOrDefault(s => s.Source == source); + if (src == null) + { + timed.Sources.Add(new TimedUpgrade.UpgradeSource(duration, source)); + if (AcceptsUpgrade(self, upgrade)) + GrantUpgrade(self, upgrade, timed); + else + timed.Sources.Remove(timed.Sources.OrderByDescending(s => s.Remaining).Last()); } else - timed.Remaining = Math.Max(duration, timed.Remaining); + src.Remaining = duration; + + timed.Remaining = Math.Max(duration, timed.Remaining); } // Different upgradeable traits may define (a) different level ranges for the same upgrade type, @@ -150,13 +188,20 @@ namespace OpenRA.Mods.Common.Traits s.Watchers.Add(action); } + /// Watchers will be receiving notifications while the upgrade's level is nonzero. + /// They will also be provided with the number of ticks before the level returns to zero, + /// as well as the duration in ticks of the timed upgrade (provided in the first call to + /// GrantTimedUpgrade). public void Tick(Actor self) { foreach (var u in timedUpgrades) { u.Tick(); - if (u.Remaining <= 0) - RevokeUpgrade(self, u.Upgrade, u); + foreach (var source in u.Sources) + if (source.Remaining <= 0) + RevokeUpgrade(self, u.Upgrade, u); + + u.Sources.RemoveWhere(source => source.Remaining <= 0); foreach (var a in upgrades.Value[u.Upgrade].Watchers) a(u.Duration, u.Remaining);