diff --git a/OpenRA.Mods.RA/Buildings/RepairableBuilding.cs b/OpenRA.Mods.RA/Buildings/RepairableBuilding.cs index 0b2dd70e89..5ef8fd8a79 100755 --- a/OpenRA.Mods.RA/Buildings/RepairableBuilding.cs +++ b/OpenRA.Mods.RA/Buildings/RepairableBuilding.cs @@ -9,6 +9,9 @@ #endregion using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; using OpenRA.Mods.RA.Effects; using OpenRA.Traits; @@ -20,6 +23,8 @@ namespace OpenRA.Mods.RA.Buildings public readonly int RepairPercent = 20; public readonly int RepairInterval = 24; public readonly int RepairStep = 7; + public readonly int[] RepairBonuses = { 100, 150, 175, 200, 220, 240, 260, 280, 300 }; + public readonly string IndicatorPalettePrefix = "player"; public object Create(ActorInitializer init) { return new RepairableBuilding(init.self, this); } @@ -27,7 +32,9 @@ namespace OpenRA.Mods.RA.Buildings public class RepairableBuilding : ITick, ISync { - [Sync] public Player Repairer = null; + [Sync] + public int RepairersHash { get { return Repairers.Aggregate(0, (code, player) => code ^ Sync.hash_player(player)); } } + public List Repairers = new List(); Health Health; RepairableBuildingInfo Info; @@ -39,26 +46,21 @@ namespace OpenRA.Mods.RA.Buildings Info = info; } - public void RepairBuilding(Actor self, Player p) + public void RepairBuilding(Actor self, Player player) { - if (self.HasTrait()) + if (self.AppearsFriendlyTo(player.PlayerActor)) { - if (self.AppearsFriendlyTo(p.PlayerActor)) + // If the player won't affect the repair, we won't add him + if (!Repairers.Remove(player) && Repairers.Count < Info.RepairBonuses.Length) { - if (Repairer == p) - Repairer = null; + Repairers.Add(player); + Sound.PlayNotification(self.World.Map.Rules, player, "Speech", "Repairing", self.Owner.Country.Race); - else + self.World.AddFrameEndTask(w => { - Repairer = p; - Sound.PlayNotification(self.World.Map.Rules, Repairer, "Speech", "Repairing", self.Owner.Country.Race); - - self.World.AddFrameEndTask(w => - { - if (!self.IsDead()) - w.Add(new RepairIndicator(self, Info.IndicatorPalettePrefix, p)); - }); - } + if (!self.IsDead()) + w.Add(new RepairIndicator(self, Info.IndicatorPalettePrefix)); + }); } } } @@ -67,32 +69,39 @@ namespace OpenRA.Mods.RA.Buildings public void Tick(Actor self) { - if (Repairer == null) return; - if (remainingTicks == 0) { - if (Repairer.WinState != WinState.Undefined || Repairer.Stances[self.Owner] != Stance.Ally) - { - Repairer = null; - return; - } + Repairers = Repairers.Where(player => player.WinState == WinState.Undefined + && player.Stances[self.Owner] == Stance.Ally).ToList(); + // If after the previous operation there's no repairers left, stop + if (!Repairers.Any()) return; var buildingValue = self.GetSellValue(); + // The cost is the same regardless of the amount of people repairing var hpToRepair = Math.Min(Info.RepairStep, Health.MaxHP - Health.HP); var cost = Math.Max(1, (hpToRepair * Info.RepairPercent * buildingValue) / (Health.MaxHP * 100)); - RepairActive = Repairer.PlayerActor.Trait().TakeCash(cost); + + // TakeCash will return false if the player can't pay, and will stop him from contributing this Tick + var activePlayers = Repairers.Count(player => player.PlayerActor.Trait().TakeCash(cost)); + + RepairActive = activePlayers > 0; + if (!RepairActive) { remainingTicks = 1; return; } - self.InflictDamage(self, -hpToRepair, null); + // Bonus is applied after finding players who can pay + + // activePlayers won't cause IndexOutOfRange because we capped the max amount of players + // to the length of the array + self.InflictDamage(self, -(hpToRepair * Info.RepairBonuses[activePlayers - 1] / 100), null); if (Health.DamageState == DamageState.Undamaged) { - Repairer = null; + Repairers.Clear(); return; } diff --git a/OpenRA.Mods.RA/Effects/RepairIndicator.cs b/OpenRA.Mods.RA/Effects/RepairIndicator.cs index 445fd4a8b2..6e2c6fe5fc 100755 --- a/OpenRA.Mods.RA/Effects/RepairIndicator.cs +++ b/OpenRA.Mods.RA/Effects/RepairIndicator.cs @@ -9,6 +9,7 @@ #endregion using System.Collections.Generic; +using System.Linq; using OpenRA.Effects; using OpenRA.Graphics; using OpenRA.Mods.RA.Buildings; @@ -18,27 +19,27 @@ namespace OpenRA.Mods.RA.Effects class RepairIndicator : IEffect { readonly Actor building; - readonly Player player; readonly string palettePrefix; readonly Animation anim; readonly RepairableBuilding rb; + int shownPlayer = 0; - public RepairIndicator(Actor building, string palettePrefix, Player player) + public RepairIndicator(Actor building, string palettePrefix) { this.building = building; - this.player = player; this.palettePrefix = palettePrefix; rb = building.TraitOrDefault(); anim = new Animation(building.World, "allyrepair"); anim.Paused = () => !rb.RepairActive; - anim.PlayRepeating("repair"); + + CycleRepairer(); } public void Tick(World world) { - if (!building.IsInWorld || building.IsDead() || - rb == null || rb.Repairer == null || rb.Repairer != player) + if (!building.IsInWorld || building.IsDead() || + rb == null || !rb.Repairers.Any()) world.AddFrameEndTask(w => w.Remove(this)); anim.Tick(); @@ -49,8 +50,16 @@ namespace OpenRA.Mods.RA.Effects if (building.Destroyed || wr.world.FogObscures(building)) return SpriteRenderable.None; - return anim.Render(building.CenterPosition, - wr.Palette(palettePrefix+player.InternalName)); + return anim.Render(building.CenterPosition, + wr.Palette(palettePrefix + rb.Repairers[shownPlayer % rb.Repairers.Count].InternalName)); + } + + void CycleRepairer() + { + anim.PlayThen("repair", CycleRepairer); + + if (++shownPlayer == rb.Repairers.Count) + shownPlayer = 0; } } }