From 6fba888d4562ad199c27959d55aa240eadb1b5a7 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 30 Jul 2010 00:01:59 +1200 Subject: [PATCH] Shift Actor.Health onto a trait. Known regressions: - cnc only - health bar colors - can't repair buildings --- OpenRA.Game/Actor.cs | 63 ------ OpenRA.Game/Effects/RepairIndicator.cs | 2 +- OpenRA.Game/Exts.cs | 7 - OpenRA.Game/GameRules/WeaponInfo.cs | 8 +- OpenRA.Game/OpenRA.Game.csproj | 2 + OpenRA.Game/Traits/Activities/Sell.cs | 10 +- OpenRA.Game/Traits/Building.cs | 60 +----- OpenRA.Game/Traits/Health.cs | 186 ++++++++++++++++++ OpenRA.Game/Traits/RepairableBuilding.cs | 85 ++++++++ OpenRA.Game/Traits/Selectable.cs | 12 +- OpenRA.Game/Traits/TraitsInterfaces.cs | 2 + OpenRA.Game/Traits/Unit.cs | 2 +- OpenRA.Game/Traits/World/AircraftInfluence.cs | 2 +- OpenRA.Game/Traits/World/GlobalDefaults.cs | 4 - OpenRA.Game/Traits/World/UnitInfluence.cs | 2 +- OpenRA.Mods.Cnc/CriticalBuildingState.cs | 4 +- OpenRA.Mods.Cnc/ProductionAirdrop.cs | 2 +- OpenRA.Mods.Cnc/TiberiumRefineryDockAction.cs | 2 +- OpenRA.Mods.RA/Activities/CaptureBuilding.cs | 2 +- OpenRA.Mods.RA/Activities/Demolish.cs | 6 +- OpenRA.Mods.RA/Activities/Infiltrate.cs | 2 +- OpenRA.Mods.RA/Activities/Land.cs | 2 +- OpenRA.Mods.RA/Activities/Leap.cs | 2 +- OpenRA.Mods.RA/Activities/Repair.cs | 10 +- OpenRA.Mods.RA/Activities/RepairBuilding.cs | 7 +- .../Activities/TransformIntoActor.cs | 17 +- OpenRA.Mods.RA/Activities/UndeployMcv.cs | 8 +- OpenRA.Mods.RA/AttackBase.cs | 8 +- OpenRA.Mods.RA/AutoHeal.cs | 7 +- OpenRA.Mods.RA/AutoTarget.cs | 2 - OpenRA.Mods.RA/Bridge.cs | 26 +-- OpenRA.Mods.RA/Combat.cs | 8 +- OpenRA.Mods.RA/ConquestVictoryConditions.cs | 2 +- OpenRA.Mods.RA/DemoTruck.cs | 2 +- OpenRA.Mods.RA/Effects/InvulnEffect.cs | 2 +- OpenRA.Mods.RA/EmitInfantryOnSell.cs | 5 +- OpenRA.Mods.RA/EngineerRepair.cs | 14 +- OpenRA.Mods.RA/Explodes.cs | 2 +- OpenRA.Mods.RA/Harvester.cs | 2 +- OpenRA.Mods.RA/Minelayer.cs | 2 +- OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs | 5 +- OpenRA.Mods.RA/OreRefinery.cs | 2 +- OpenRA.Mods.RA/Render/RenderBuildingWall.cs | 28 +-- OpenRA.Mods.RA/Repairable.cs | 15 +- OpenRA.Mods.RA/RepairableNear.cs | 6 +- OpenRA.Mods.RA/Reservable.cs | 2 +- OpenRA.Mods.RA/SelfHealing.cs | 5 +- OpenRA.Mods.RA/StoresOre.cs | 2 +- OpenRA.Mods.RA/Wall.cs | 2 +- mods/cnc/civilian.yaml | 2 + mods/cnc/defaults.yaml | 10 +- mods/cnc/infantry.yaml | 51 +++-- mods/cnc/structures.yaml | 26 ++- mods/cnc/system.yaml | 1 - mods/cnc/trees.yaml | 4 +- mods/cnc/vehicles.yaml | 79 +++++--- 56 files changed, 530 insertions(+), 303 deletions(-) create mode 100644 OpenRA.Game/Traits/Health.cs create mode 100644 OpenRA.Game/Traits/RepairableBuilding.cs diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index 439272e60c..eaf14c9b48 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -31,8 +31,6 @@ namespace OpenRA public int2 Location { get { return traits.Get().TopLeft; } } [Sync] public Player Owner; - [Sync] - public int Health; IActivity currentActivity; public Group Group; @@ -51,8 +49,6 @@ namespace OpenRA throw new NotImplementedException("No rules definition for unit {0}".F(name.ToLowerInvariant())); Info = Rules.Info[name.ToLowerInvariant()]; - Health = this.GetMaxHP(); - foreach (var trait in Info.TraitsInConstructOrder()) traits.Add(trait.Create(init)); } @@ -147,66 +143,7 @@ namespace OpenRA return new RectangleF(loc.X, loc.Y, size.X, size.Y); } - public bool IsDead { get { return Health <= 0; } } public bool IsInWorld { get; set; } - public bool RemoveOnDeath = true; - - public DamageState GetDamageState() - { - if (Health <= 0) - return DamageState.Dead; - - if (Health < this.GetMaxHP() * World.Defaults.ConditionYellow) - return DamageState.Half; - - return DamageState.Normal; - } - - public void InflictDamage(Actor attacker, int damage, WarheadInfo warhead) - { - if (IsDead) return; /* overkill! don't count extra hits as more kills! */ - - var oldState = GetDamageState(); - - /* apply the damage modifiers, if we have any. */ - var modifier = (float)traits.WithInterface() - .Select(t => t.GetDamageModifier(warhead)).Product(); - - damage = (int)(damage * modifier); - - Health -= damage; - if (Health <= 0) - { - Health = 0; - - attacker.Owner.Kills++; - Owner.Deaths++; - - if (RemoveOnDeath) - World.AddFrameEndTask(w => w.Remove(this)); - - Log.Write("debug", "{0} #{1} killed by {2} #{3}", Info.Name, ActorID, attacker.Info.Name, attacker.ActorID); - } - - var maxHP = this.GetMaxHP(); - - if (Health > maxHP) Health = maxHP; - -// Log.Write("debug", "InflictDamage: {0} #{1} -> {2} #{3} raw={4} adj={5} hp={6} mod={7}", -// attacker.Info.Name, attacker.ActorID, Info.Name, ActorID, rawDamage, damage, Health, modifier); - - var newState = GetDamageState(); - - foreach (var nd in traits.WithInterface()) - nd.Damaged(this, new AttackInfo - { - Attacker = attacker, - Damage = damage, - DamageState = newState, - DamageStateChanged = newState != oldState, - Warhead = warhead - }); - } public void QueueActivity( IActivity nextActivity ) { diff --git a/OpenRA.Game/Effects/RepairIndicator.cs b/OpenRA.Game/Effects/RepairIndicator.cs index fe80d8dc90..8ec946a8c2 100755 --- a/OpenRA.Game/Effects/RepairIndicator.cs +++ b/OpenRA.Game/Effects/RepairIndicator.cs @@ -28,7 +28,7 @@ namespace OpenRA.Effects public void Tick( World world ) { - if (--framesLeft == 0 || a.IsDead) + if (--framesLeft == 0 || a.IsDead()) world.AddFrameEndTask(w => w.Remove(this)); } diff --git a/OpenRA.Game/Exts.cs b/OpenRA.Game/Exts.cs index 6f67c95399..4a2b65487a 100644 --- a/OpenRA.Game/Exts.cs +++ b/OpenRA.Game/Exts.cs @@ -34,13 +34,6 @@ namespace OpenRA return xs.Aggregate(1f, (a, x) => a * x); } - public static int GetMaxHP(this Actor self) - { - var oai = self.Info.Traits.GetOrDefault(); - if (oai == null) return 0; - return oai.HP; - } - public static V GetOrAdd( this Dictionary d, K k ) where V : new() { diff --git a/OpenRA.Game/GameRules/WeaponInfo.cs b/OpenRA.Game/GameRules/WeaponInfo.cs index 92ae3fdd07..5dcbe6c3ad 100644 --- a/OpenRA.Game/GameRules/WeaponInfo.cs +++ b/OpenRA.Game/GameRules/WeaponInfo.cs @@ -31,7 +31,13 @@ namespace OpenRA.GameRules public readonly int Delay = 0; // delay in ticks before dealing the damage. 0=instant (old model) public readonly DamageModel DamageModel = DamageModel.Normal; // which damage model to use - public float EffectivenessAgainst(ArmorType at) { return Verses[(int)at]; } + public float EffectivenessAgainst(Actor self) + { + var health = self.Info.Traits.GetOrDefault(); + if (health == null) return 0f; + + return Verses[(int)(health.Armor)]; + } } public enum ArmorType diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 9bffd05e4f..76b26863c7 100755 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -229,6 +229,8 @@ + + diff --git a/OpenRA.Game/Traits/Activities/Sell.cs b/OpenRA.Game/Traits/Activities/Sell.cs index 0260cf0d39..26cd7cd29a 100644 --- a/OpenRA.Game/Traits/Activities/Sell.cs +++ b/OpenRA.Game/Traits/Activities/Sell.cs @@ -22,11 +22,13 @@ namespace OpenRA.Traits.Activities { var csv = self.Info.Traits.GetOrDefault(); var cost = csv != null ? csv.Value : self.Info.Traits.Get().Cost; - var hp = self.Info.Traits.Get().HP; - var refund = self.World.Defaults.RefundPercent * self.Health * cost / hp; + + var health = self.traits.GetOrDefault(); + var refundFraction = self.World.Defaults.RefundPercent * (health == null ? 1f : health.HPFraction); - self.Owner.PlayerActor.traits.Get().GiveCash((int)refund); - self.Health = 0; + self.Owner.PlayerActor.traits.Get().GiveCash((int)(refundFraction * cost)); + self.Kill(self); + foreach (var ns in self.traits.WithInterface()) ns.Sold(self); self.World.AddFrameEndTask( _ => self.World.Remove( self ) ); diff --git a/OpenRA.Game/Traits/Building.cs b/OpenRA.Game/Traits/Building.cs index 0907ca00eb..cdf2156229 100644 --- a/OpenRA.Game/Traits/Building.cs +++ b/OpenRA.Game/Traits/Building.cs @@ -18,13 +18,7 @@ using OpenRA.Traits.Activities; namespace OpenRA.Traits { - public class OwnedActorInfo - { - public readonly int HP = 0; - public readonly ArmorType Armor = ArmorType.none; - } - - public class BuildingInfo : OwnedActorInfo, ITraitInfo + public class BuildingInfo : ITraitInfo { public readonly int Power = 0; public readonly bool BaseNormal = true; @@ -44,14 +38,12 @@ namespace OpenRA.Traits public object Create(ActorInitializer init) { return new Building(init); } } - public class Building : INotifyDamage, IResolveOrder, ITick, IRenderModifier, IOccupySpace, IRadarSignature + public class Building : INotifyDamage, IResolveOrder, IRenderModifier, IOccupySpace, IRadarSignature { readonly Actor self; public readonly BuildingInfo Info; [Sync] readonly int2 topLeft; - [Sync] - bool isRepairing = false; public bool Disabled { @@ -73,11 +65,13 @@ namespace OpenRA.Traits .WithInterface() .Select(t => t.GetPowerModifier()) .Product(); - - var maxHP = self.Info.Traits.Get().HP; - + if (Info.Power > 0) - return (int)(modifier*(self.Health * Info.Power) / maxHP); + { + var health = self.traits.GetOrDefault(); + var healthFraction = (health == null) ? 1f : health.HPFraction; + return (int)(modifier * healthFraction * Info.Power); + } else return (int)(modifier * Info.Power); } @@ -98,44 +92,6 @@ namespace OpenRA.Traits self.CancelActivity(); self.QueueActivity(new Sell()); } - - if (order.OrderString == "Repair") - { - isRepairing = !isRepairing; - } - } - - int remainingTicks; - - public void Tick(Actor self) - { - if (!isRepairing) return; - - if (remainingTicks == 0) - { - var csv = self.Info.Traits.GetOrDefault(); - var buildingValue = csv != null ? csv.Value : self.Info.Traits.Get().Cost; - var maxHP = self.Info.Traits.Get().HP; - var costPerHp = (self.World.Defaults.RepairPercent * buildingValue) / maxHP; - var hpToRepair = Math.Min(self.World.Defaults.RepairStep, maxHP - self.Health); - var cost = (int)Math.Ceiling(costPerHp * hpToRepair); - if (!self.Owner.PlayerActor.traits.Get().TakeCash(cost)) - { - remainingTicks = 1; - return; - } - - self.World.AddFrameEndTask(w => w.Add(new RepairIndicator(self))); - self.InflictDamage(self, -hpToRepair, null); - if (self.Health == maxHP) - { - isRepairing = false; - return; - } - remainingTicks = (int)(self.World.Defaults.RepairRate * 60 * 25); - } - else - --remainingTicks; } public IEnumerable ModifyRender(Actor self, IEnumerable r) diff --git a/OpenRA.Game/Traits/Health.cs b/OpenRA.Game/Traits/Health.cs new file mode 100644 index 0000000000..ca0d68cc08 --- /dev/null +++ b/OpenRA.Game/Traits/Health.cs @@ -0,0 +1,186 @@ +#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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Effects; +using OpenRA.Traits.Activities; +using OpenRA.GameRules; + +namespace OpenRA.Traits +{ + public class HealthInfo : ITraitInfo + { + public readonly int HP = 0; + public readonly int InitialHP = 0; + public readonly ArmorType Armor = ArmorType.none; + + public readonly float ConditionRed = 0.25f; + public readonly float ConditionYellow = 0.5f; + + public virtual object Create(ActorInitializer init) { return new Health(init, this); } + } + + public enum ExtendedDamageState { Normal, ThreeQuarter, Half, Quarter, Dead }; + + public class Health + { + public readonly HealthInfo Info; + + [Sync] + int hp; + + public Health(ActorInitializer init, HealthInfo info) + { + Info = info; + MaxHP = info.HP; + hp = (info.InitialHP == 0) ? MaxHP : info.InitialHP; + } + + public int HP { get { return hp; } } + public readonly int MaxHP; + public float HPFraction { get { return hp*1f/MaxHP; } } + + public bool IsDead { get { return hp <= 0; } } + public bool RemoveOnDeath = true; + + public Color HealthColor + { + get + { + return (hp < Info.ConditionRed) ? Color.Red + : (hp < Info.ConditionYellow) ? Color.Yellow + : Color.LimeGreen; + } + } + + public DamageState DamageState + { + get + { + if (hp <= 0) + return DamageState.Dead; + + if (hp < MaxHP * Info.ConditionYellow) + return DamageState.Half; + + return DamageState.Normal; + } + } + + public ExtendedDamageState ExtendedDamageState + { + get + { + if (hp <= 0) + return ExtendedDamageState.Dead; + + if (hp < MaxHP * Info.ConditionRed) + return ExtendedDamageState.Quarter; + + if (hp < MaxHP * Info.ConditionYellow) + return ExtendedDamageState.Half; + + if (hp < MaxHP * 0.75) + return ExtendedDamageState.ThreeQuarter; + + return ExtendedDamageState.Normal; + } + } + + public void InflictDamage(Actor self, Actor attacker, int damage, WarheadInfo warhead) + { + if (IsDead) return; /* overkill! don't count extra hits as more kills! */ + + var oldState = this.DamageState; + var oldExtendedState = this.ExtendedDamageState; + + /* apply the damage modifiers, if we have any. */ + var modifier = (float)self.traits.WithInterface() + .Select(t => t.GetDamageModifier(warhead)).Product(); + + damage = (int)(damage * modifier); + + hp -= damage; + if (hp <= 0) + { + hp = 0; + + attacker.Owner.Kills++; + self.Owner.Deaths++; + + if (RemoveOnDeath) + self.World.AddFrameEndTask(w => w.Remove(self)); + + Log.Write("debug", "{0} #{1} killed by {2} #{3}", self.Info.Name, self.ActorID, attacker.Info.Name, attacker.ActorID); + } + + if (hp > MaxHP) hp = MaxHP; + + foreach (var nd in self.traits.WithInterface()) + nd.Damaged(self, new AttackInfo + { + Attacker = attacker, + Damage = damage, + DamageState = this.DamageState, + ExtendedDamageState = this.ExtendedDamageState, + DamageStateChanged = this.DamageState != oldState, + ExtendedDamageStateChanged = this.ExtendedDamageState != oldExtendedState, + Warhead = warhead + }); + } + + public void TransferHPFromActor(Actor self, Actor from, bool transferPercentage) + { + var fromHealth = from.traits.GetOrDefault(); + if (fromHealth == null) + return; + + hp = (transferPercentage) ? (int)(fromHealth.HPFraction*MaxHP) : Math.Min(fromHealth.HP, MaxHP); + } + } + + public static class HealthExts + { + public static int Health(this Actor self) + { + var health = self.traits.GetOrDefault(); + return (health == null) ? 0 : health.HP; + } + + public static bool IsDead(this Actor self) + { + var health = self.traits.GetOrDefault(); + return (health == null) ? true : health.IsDead; + } + + public static DamageState GetDamageState(this Actor self) + { + var health = self.traits.GetOrDefault(); + return (health == null) ? DamageState.Normal : health.DamageState; + } + + public static void InflictDamage(this Actor self, Actor attacker, int damage, WarheadInfo warhead) + { + var health = self.traits.GetOrDefault(); + if (health == null) return; + health.InflictDamage(self, attacker, damage, warhead); + } + + public static void Kill(this Actor self, Actor attacker) + { + var health = self.traits.GetOrDefault(); + if (health == null) return; + health.InflictDamage(self, attacker, health.HP, null); + } + } +} diff --git a/OpenRA.Game/Traits/RepairableBuilding.cs b/OpenRA.Game/Traits/RepairableBuilding.cs new file mode 100644 index 0000000000..c3db7e5eac --- /dev/null +++ b/OpenRA.Game/Traits/RepairableBuilding.cs @@ -0,0 +1,85 @@ +#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.Collections.Generic; +using System.Drawing; +using System.Linq; +using OpenRA.Effects; +using OpenRA.GameRules; +using OpenRA.Traits.Activities; + +namespace OpenRA.Traits +{ + public class RepairableBuildingInfo : ITraitInfo, ITraitPrerequisite + { + public readonly float RepairPercent = 0.2f; + public readonly float RepairRate = 0.016f; + public readonly int RepairStep = 7; + public object Create(ActorInitializer init) { return new RepairableBuilding(init.self, this); } + } + + public class RepairableBuilding : ITick, IResolveOrder + { + [Sync] + bool isRepairing = false; + + Health Health; + RepairableBuildingInfo Info; + public RepairableBuilding(Actor self, RepairableBuildingInfo info) + { + Health = self.traits.Get(); + Info = info; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "Repair") + { + isRepairing = !isRepairing; + } + } + + int remainingTicks; + public void Tick(Actor self) + { + if (!isRepairing) return; + + if (remainingTicks == 0) + { + var csv = self.Info.Traits.GetOrDefault(); + var buildingValue = csv != null ? csv.Value : self.Info.Traits.Get().Cost; + + var costPerHp = (Info.RepairPercent * buildingValue) / Health.MaxHP; + var hpToRepair = Math.Min(Info.RepairStep, Health.MaxHP - Health.HP); + var cost = (int)Math.Ceiling(costPerHp * hpToRepair); + if (!self.Owner.PlayerActor.traits.Get().TakeCash(cost)) + { + remainingTicks = 1; + return; + } + + self.World.AddFrameEndTask(w => w.Add(new RepairIndicator(self))); + self.InflictDamage(self, -hpToRepair, null); + if (Health.HP == Health.MaxHP) + { + isRepairing = false; + return; + } + remainingTicks = (int)(Info.RepairRate * 60 * 25); + } + else + --remainingTicks; + } + } + public class AllowsBuildingRepairInfo : TraitInfo {} + public class AllowsBuildingRepair {} + +} diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 9a8ef3d30b..5cee700f51 100755 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -61,22 +61,22 @@ namespace OpenRA.Traits void DrawHealthBar(Actor self, float2 xy, float2 Xy) { + var health = self.traits.GetOrDefault(); + if (health == null) + return; + var c = Color.Gray; Game.Renderer.LineRenderer.DrawLine(xy + new float2(0, -2), xy + new float2(0, -4), c, c); Game.Renderer.LineRenderer.DrawLine(Xy + new float2(0, -2), Xy + new float2(0, -4), c, c); - var healthAmount = (float)self.Health / self.Info.Traits.Get().HP; - var healthColor = (healthAmount < self.World.Defaults.ConditionRed) ? Color.Red - : (healthAmount < self.World.Defaults.ConditionYellow) ? Color.Yellow - : Color.LimeGreen; - + var healthColor = health.HealthColor; var healthColor2 = Color.FromArgb( 255, healthColor.R / 2, healthColor.G / 2, healthColor.B / 2); - var z = float2.Lerp(xy, Xy, healthAmount); + var z = float2.Lerp(xy, Xy, health.HPFraction); Game.Renderer.LineRenderer.DrawLine(z + new float2(0, -4), Xy + new float2(0, -4), c, c); Game.Renderer.LineRenderer.DrawLine(z + new float2(0, -2), Xy + new float2(0, -2), c, c); diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index cc5fbca88b..a695ed8a73 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -28,7 +28,9 @@ namespace OpenRA.Traits public WarheadInfo Warhead; public int Damage; public DamageState DamageState; + public ExtendedDamageState ExtendedDamageState; public bool DamageStateChanged; + public bool ExtendedDamageStateChanged; } public interface ITick { void Tick(Actor self); } diff --git a/OpenRA.Game/Traits/Unit.cs b/OpenRA.Game/Traits/Unit.cs index 9b64f11f5b..d2b9c57e7b 100755 --- a/OpenRA.Game/Traits/Unit.cs +++ b/OpenRA.Game/Traits/Unit.cs @@ -14,7 +14,7 @@ using System.Linq; namespace OpenRA.Traits { - public class UnitInfo : OwnedActorInfo, ITraitInfo + public class UnitInfo : ITraitInfo { public readonly int InitialFacing = 128; public readonly int ROT = 255; diff --git a/OpenRA.Game/Traits/World/AircraftInfluence.cs b/OpenRA.Game/Traits/World/AircraftInfluence.cs index be6af50237..78eabf500f 100644 --- a/OpenRA.Game/Traits/World/AircraftInfluence.cs +++ b/OpenRA.Game/Traits/World/AircraftInfluence.cs @@ -81,7 +81,7 @@ namespace OpenRA.Traits public void Update(Actor self, IOccupyAir unit) { Remove(self, unit); - if (!self.IsDead) Add(self, unit); + if (!self.IsDead()) Add(self, unit); } } } diff --git a/OpenRA.Game/Traits/World/GlobalDefaults.cs b/OpenRA.Game/Traits/World/GlobalDefaults.cs index 7f3e50ddf6..11df518971 100644 --- a/OpenRA.Game/Traits/World/GlobalDefaults.cs +++ b/OpenRA.Game/Traits/World/GlobalDefaults.cs @@ -19,10 +19,6 @@ namespace OpenRA.Traits public readonly float RepairPercent = 0.2f; public readonly float RepairRate = 0.016f; public readonly int RepairStep = 7; - - /* Audo/Visual Map Controls */ - public readonly float ConditionRed = 0.25f; - public readonly float ConditionYellow = 0.5f; } public class GlobalDefaults {} diff --git a/OpenRA.Game/Traits/World/UnitInfluence.cs b/OpenRA.Game/Traits/World/UnitInfluence.cs index cf55ddf6f8..16922cd431 100644 --- a/OpenRA.Game/Traits/World/UnitInfluence.cs +++ b/OpenRA.Game/Traits/World/UnitInfluence.cs @@ -86,7 +86,7 @@ namespace OpenRA.Traits public void Update(Actor self, IOccupySpace unit) { Remove(self, unit); - if (!self.IsDead) Add(self, unit); + if (!self.IsDead()) Add(self, unit); } } } diff --git a/OpenRA.Mods.Cnc/CriticalBuildingState.cs b/OpenRA.Mods.Cnc/CriticalBuildingState.cs index c6357a36c4..7532912440 100644 --- a/OpenRA.Mods.Cnc/CriticalBuildingState.cs +++ b/OpenRA.Mods.Cnc/CriticalBuildingState.cs @@ -13,7 +13,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.Cnc { - class CriticalBuildingStateInfo : ITraitInfo + class CriticalBuildingStateInfo : ITraitInfo, ITraitPrerequisite { public readonly int LingerTime = 20; public object Create(ActorInitializer init) { return new CriticalBuildingState(init.self, this); } @@ -26,7 +26,7 @@ namespace OpenRA.Mods.Cnc public CriticalBuildingState(Actor self, CriticalBuildingStateInfo info) { this.info = info; - self.RemoveOnDeath = false; + self.traits.Get().RemoveOnDeath = false; } public void Damaged(Actor self, AttackInfo e) diff --git a/OpenRA.Mods.Cnc/ProductionAirdrop.cs b/OpenRA.Mods.Cnc/ProductionAirdrop.cs index 4b4f3578dd..2df903a0dc 100644 --- a/OpenRA.Mods.Cnc/ProductionAirdrop.cs +++ b/OpenRA.Mods.Cnc/ProductionAirdrop.cs @@ -51,7 +51,7 @@ namespace OpenRA.Mods.Cnc a.QueueActivity(new Land(self)); a.QueueActivity(new CallFunc(() => { - if (self.IsDead) + if (self.IsDead()) return; var actor = cargo.Unload(self); diff --git a/OpenRA.Mods.Cnc/TiberiumRefineryDockAction.cs b/OpenRA.Mods.Cnc/TiberiumRefineryDockAction.cs index daa8a6c2db..ce0d45e2aa 100644 --- a/OpenRA.Mods.Cnc/TiberiumRefineryDockAction.cs +++ b/OpenRA.Mods.Cnc/TiberiumRefineryDockAction.cs @@ -82,7 +82,7 @@ namespace OpenRA.Mods.Cnc public void Damaged (Actor self, AttackInfo e) { - if (self.IsDead) + if (e.DamageState == DamageState.Dead) CancelDock(self, dockedHarv); } diff --git a/OpenRA.Mods.RA/Activities/CaptureBuilding.cs b/OpenRA.Mods.RA/Activities/CaptureBuilding.cs index e58287a2da..c1e80425bb 100644 --- a/OpenRA.Mods.RA/Activities/CaptureBuilding.cs +++ b/OpenRA.Mods.RA/Activities/CaptureBuilding.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.RA.Activities public IActivity Tick(Actor self) { - if (target == null || target.IsDead) return NextActivity; + if (target == null || target.IsDead()) return NextActivity; target.World.AddFrameEndTask(w => { diff --git a/OpenRA.Mods.RA/Activities/Demolish.cs b/OpenRA.Mods.RA/Activities/Demolish.cs index 8fc0826cf9..95498ab3eb 100644 --- a/OpenRA.Mods.RA/Activities/Demolish.cs +++ b/OpenRA.Mods.RA/Activities/Demolish.cs @@ -24,10 +24,10 @@ namespace OpenRA.Mods.RA.Activities } public IActivity Tick(Actor self) - { - if (target == null || target.IsDead) return NextActivity; + { + if (target == null || target.IsDead()) return NextActivity; self.World.AddFrameEndTask(w => w.Add(new DelayedAction(25 * 2, - () => target.InflictDamage(self, target.Health, null)))); + () => target.Kill(self)))); return NextActivity; } diff --git a/OpenRA.Mods.RA/Activities/Infiltrate.cs b/OpenRA.Mods.RA/Activities/Infiltrate.cs index d0da39cdc8..18d5c83fc7 100644 --- a/OpenRA.Mods.RA/Activities/Infiltrate.cs +++ b/OpenRA.Mods.RA/Activities/Infiltrate.cs @@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA.Activities public IActivity Tick(Actor self) { - if (target == null || target.IsDead) return NextActivity; + if (target == null || target.IsDead()) return NextActivity; if (target.Owner == self.Owner) return NextActivity; foreach (var t in target.traits.WithInterface()) diff --git a/OpenRA.Mods.RA/Activities/Land.cs b/OpenRA.Mods.RA/Activities/Land.cs index 1456808f3f..28d6e56d2f 100644 --- a/OpenRA.Mods.RA/Activities/Land.cs +++ b/OpenRA.Mods.RA/Activities/Land.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Activities public IActivity Tick(Actor self) { - if (Structure != null && Structure.IsDead) + if (Structure != null && Structure.IsDead()) { Structure = null; isCanceled = true; diff --git a/OpenRA.Mods.RA/Activities/Leap.cs b/OpenRA.Mods.RA/Activities/Leap.cs index 3f71ccfbdd..c73929b7f0 100644 --- a/OpenRA.Mods.RA/Activities/Leap.cs +++ b/OpenRA.Mods.RA/Activities/Leap.cs @@ -48,7 +48,7 @@ namespace OpenRA.Mods.RA.Activities .SetPosition(self, Util.CellContaining(target.CenterLocation)); if (target.IsActor) - target.Actor.InflictDamage(self, target.Actor.Health, null); // kill it + target.Actor.Kill(self); return NextActivity; } diff --git a/OpenRA.Mods.RA/Activities/Repair.cs b/OpenRA.Mods.RA/Activities/Repair.cs index 28140545cf..a0b4174abc 100644 --- a/OpenRA.Mods.RA/Activities/Repair.cs +++ b/OpenRA.Mods.RA/Activities/Repair.cs @@ -28,11 +28,13 @@ namespace OpenRA.Mods.RA.Activities if (isCanceled) return NextActivity; if (remainingTicks == 0) { + var health = self.traits.GetOrDefault(); + if (health == null) return NextActivity; + var unitCost = self.Info.Traits.Get().Cost; - var hp = self.Info.Traits.Get().HP; - var costPerHp = (host.Info.Traits.Get().URepairPercent * unitCost) / hp; - var hpToRepair = Math.Min(host.Info.Traits.Get().URepairStep, hp - self.Health); + var costPerHp = (host.Info.Traits.Get().URepairPercent * unitCost) / health.MaxHP; + var hpToRepair = Math.Min(host.Info.Traits.Get().URepairStep, health.MaxHP - health.HP); var cost = (int)Math.Ceiling(costPerHp * hpToRepair); if (!self.Owner.PlayerActor.traits.Get().TakeCash(cost)) { @@ -41,7 +43,7 @@ namespace OpenRA.Mods.RA.Activities } self.InflictDamage(self, -hpToRepair, null); - if (self.Health == hp) + if (health.MaxHP == health.HP) return NextActivity; if (host != null) diff --git a/OpenRA.Mods.RA/Activities/RepairBuilding.cs b/OpenRA.Mods.RA/Activities/RepairBuilding.cs index 49dfa30eb5..95b07f1d99 100644 --- a/OpenRA.Mods.RA/Activities/RepairBuilding.cs +++ b/OpenRA.Mods.RA/Activities/RepairBuilding.cs @@ -22,11 +22,12 @@ namespace OpenRA.Mods.RA.Activities public IActivity Tick(Actor self) { - if (target == null || target.IsDead) return NextActivity; - if (target.Health == target.GetMaxHP()) + if (target == null || target.IsDead()) return NextActivity; + var health = target.traits.Get(); + if (health.HP == health.MaxHP) return NextActivity; - target.InflictDamage(self, -target.GetMaxHP(), null); + target.InflictDamage(self, -health.MaxHP, null); self.World.AddFrameEndTask(w => w.Remove(self)); return NextActivity; diff --git a/OpenRA.Mods.RA/Activities/TransformIntoActor.cs b/OpenRA.Mods.RA/Activities/TransformIntoActor.cs index c63c776b4b..3765421ee5 100644 --- a/OpenRA.Mods.RA/Activities/TransformIntoActor.cs +++ b/OpenRA.Mods.RA/Activities/TransformIntoActor.cs @@ -47,8 +47,12 @@ namespace OpenRA.Mods.RA.Activities Sound.PlayToPlayer(self.Owner, s, self.CenterLocation); var a = w.CreateActor(actor, self.Location + offset, self.Owner); - a.Health = GetHealthToTransfer(self, a, transferPercentage); - + var health = a.traits.GetOrDefault(); + if (health != null) + { + health.TransferHPFromActor(a, self, transferPercentage); + } + if (selected) w.Selection.Add(w, a); }); @@ -56,14 +60,5 @@ namespace OpenRA.Mods.RA.Activities } public void Cancel(Actor self) { isCanceled = true; NextActivity = null; } - - public static int GetHealthToTransfer(Actor from, Actor to, bool transferPercentage) - { - var oldHP = from.GetMaxHP(); - var newHP = to.GetMaxHP(); - return (transferPercentage) - ? (int)((float)from.Health / oldHP * newHP) - : Math.Min(from.Health, newHP); - } } } diff --git a/OpenRA.Mods.RA/Activities/UndeployMcv.cs b/OpenRA.Mods.RA/Activities/UndeployMcv.cs index 0b78189d73..66e6875435 100755 --- a/OpenRA.Mods.RA/Activities/UndeployMcv.cs +++ b/OpenRA.Mods.RA/Activities/UndeployMcv.cs @@ -24,7 +24,13 @@ namespace OpenRA.Mods.RA.Activities w.Remove(self); var mcv = w.CreateActor("mcv", self.Location + new int2(1, 1), self.Owner); - mcv.Health = TransformIntoActor.GetHealthToTransfer(self, mcv, true); + + var health = mcv.traits.GetOrDefault(); + if (health != null) + { + health.TransferHPFromActor(mcv, self, true); + } + mcv.traits.Get().Facing = 96; if (selected) diff --git a/OpenRA.Mods.RA/AttackBase.cs b/OpenRA.Mods.RA/AttackBase.cs index be1994e3aa..4ce8193d37 100755 --- a/OpenRA.Mods.RA/AttackBase.cs +++ b/OpenRA.Mods.RA/AttackBase.cs @@ -218,10 +218,16 @@ namespace OpenRA.Mods.RA { // we can never "heal ground"; that makes no sense. if (!target.IsActor) return null; + // unless forced, only heal allies. if (self.Owner.Stances[underCursor.Owner] != Stance.Ally && !forceFire) return null; + + // we can only heal actors with health + var health = underCursor.traits.GetOrDefault(); + if (health == null) return null; + // don't allow healing of fully-healed stuff! - if (underCursor.Health >= underCursor.GetMaxHP()) return null; + if (health.HP >= health.MaxHP) return null; } else { diff --git a/OpenRA.Mods.RA/AutoHeal.cs b/OpenRA.Mods.RA/AutoHeal.cs index 5ac26a9f3a..9a0e1defb5 100644 --- a/OpenRA.Mods.RA/AutoHeal.cs +++ b/OpenRA.Mods.RA/AutoHeal.cs @@ -37,8 +37,9 @@ namespace OpenRA.Mods.RA return true; // he's dead. if ((attack.target.CenterLocation - self.Location).LengthSquared > range * range + 2) return true; // wandered off faster than we could follow - if (attack.target.IsActor && attack.target.Actor.Health - == attack.target.Actor.Info.Traits.Get().HP) + + var health = attack.target.Actor.traits.GetOrDefault(); + if (attack.target.IsActor && health.HP == health.MaxHP) return true; // fully healed return false; @@ -59,7 +60,7 @@ namespace OpenRA.Mods.RA return inRange .Where(a => a != self && self.Owner.Stances[ a.Owner ] == Stance.Ally) .Where(a => Combat.HasAnyValidWeapons(self, Target.FromActor(a))) - .Where(a => a.Health < a.Info.Traits.Get().HP) + .Where(a => a.traits.Contains() && a.traits.Get().HPFraction < 1f) .OrderBy(a => (a.Location - self.Location).LengthSquared) .FirstOrDefault(); } diff --git a/OpenRA.Mods.RA/AutoTarget.cs b/OpenRA.Mods.RA/AutoTarget.cs index 8ef987e9bf..67c8c194c0 100644 --- a/OpenRA.Mods.RA/AutoTarget.cs +++ b/OpenRA.Mods.RA/AutoTarget.cs @@ -66,8 +66,6 @@ namespace OpenRA.Mods.RA { if (!self.IsIdle) return; - if (!e.Attacker.Info.Traits.Contains()) return; - // not a lot we can do about things we can't hurt... although maybe we should automatically run away? if (!Combat.HasAnyValidWeapons(self, Target.FromActor(e.Attacker))) return; diff --git a/OpenRA.Mods.RA/Bridge.cs b/OpenRA.Mods.RA/Bridge.cs index 15bafdb25b..2943389501 100644 --- a/OpenRA.Mods.RA/Bridge.cs +++ b/OpenRA.Mods.RA/Bridge.cs @@ -17,7 +17,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - class BridgeInfo : ITraitInfo + class BridgeInfo : ITraitInfo, ITraitPrerequisite { public readonly bool Long = false; @@ -75,11 +75,13 @@ namespace OpenRA.Mods.RA BridgeInfo Info; public string Type; Bridge northNeighbour, southNeighbour; - + Health Health; + public Bridge(Actor self, BridgeInfo info) { this.self = self; - self.RemoveOnDeath = false; + Health = self.traits.Get(); + Health.RemoveOnDeath = false; this.Info = info; this.Type = self.Info.Name; } @@ -88,9 +90,9 @@ namespace OpenRA.Mods.RA { currentTemplate = template; if (template == Info.DamagedTemplate) - self.Health = (int)(self.World.Defaults.ConditionYellow*self.GetMaxHP()); + Health.InflictDamage(self, self, Health.MaxHP/2, null); else if (template != Info.Template) - self.Health = 0; + Health.InflictDamage(self, self, Health.MaxHP, null); // Create a new cache to store the tile data if (cachedTileset != self.World.Map.Tileset) @@ -141,7 +143,7 @@ namespace OpenRA.Mods.RA bool IsIntact(Bridge b) { - return b != null && b.self.IsInWorld && b.self.Health > 0; + return b != null && b.self.IsInWorld && !b.self.IsDead(); } void KillUnitsOnBridge() @@ -151,23 +153,21 @@ namespace OpenRA.Mods.RA foreach (var c in TileSprites[currentTemplate].Keys) foreach (var a in uim.GetUnitsAt(c)) if (!a.traits.Get().CanEnterCell(c)) - a.InflictDamage(self, a.Health, null); + a.Kill(self); } bool dead = false; void UpdateState() - { - var ds = self.GetDamageState(); - + { // If this is a long bridge next to a destroyed shore piece, we need die to give clean edges to the break - if (Info.Long && ds != DamageState.Dead && + if (Info.Long && Health.DamageState != DamageState.Dead && ((southNeighbour != null && Info.ShorePieces.Contains(southNeighbour.Type) && !IsIntact(southNeighbour)) || (northNeighbour != null && Info.ShorePieces.Contains(northNeighbour.Type) && !IsIntact(northNeighbour)))) { - self.Health = 0; - ds = DamageState.Dead; + self.Kill(self); // this changes the damagestate } + var ds = Health.DamageState; currentTemplate = (ds == DamageState.Half && Info.DamagedTemplate > 0) ? Info.DamagedTemplate : (ds == DamageState.Dead && Info.DestroyedTemplate > 0) ? Info.DestroyedTemplate : Info.Template; diff --git a/OpenRA.Mods.RA/Combat.cs b/OpenRA.Mods.RA/Combat.cs index 5c54bf0cc1..99f854f539 100755 --- a/OpenRA.Mods.RA/Combat.cs +++ b/OpenRA.Mods.RA/Combat.cs @@ -94,8 +94,7 @@ namespace OpenRA.Mods.RA foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0])) foreach (var unit in world.FindUnits(Game.CellSize * t, Game.CellSize * (t + new float2(1,1)))) unit.InflictDamage(args.firedBy, - (int)(warhead.Damage * warhead.EffectivenessAgainst( - unit.Info.Traits.Get().Armor)), warhead); + (int)(warhead.Damage * warhead.EffectivenessAgainst(unit)), warhead); } break; } } @@ -154,7 +153,7 @@ namespace OpenRA.Mods.RA var distance = (int)Math.Max(0, (target.CenterLocation - args.dest).Length - radius); var falloff = (float)GetDamageFalloff(distance / warhead.Spread); var rawDamage = (float)(warhead.Damage * modifier * falloff); - var multiplier = (float)warhead.EffectivenessAgainst(target.Info.Traits.Get().Armor); + var multiplier = (float)warhead.EffectivenessAgainst(target); return (float)(rawDamage * multiplier); } @@ -171,8 +170,7 @@ namespace OpenRA.Mods.RA if (targetable == null || !weapon.ValidTargets.Intersect(targetable.TargetTypes).Any()) return false; - var ownedInfo = target.Actor.Info.Traits.GetOrDefault(); - if (weapon.Warheads.All( w => w.EffectivenessAgainst(ownedInfo.Armor) <= 0)) + if (weapon.Warheads.All( w => w.EffectivenessAgainst(target.Actor) <= 0)) return false; return true; diff --git a/OpenRA.Mods.RA/ConquestVictoryConditions.cs b/OpenRA.Mods.RA/ConquestVictoryConditions.cs index f197736bb9..3fc56a0415 100644 --- a/OpenRA.Mods.RA/ConquestVictoryConditions.cs +++ b/OpenRA.Mods.RA/ConquestVictoryConditions.cs @@ -47,7 +47,7 @@ namespace OpenRA.Mods.RA Game.Debug("{0} is defeated.".F(self.Owner.PlayerName)); foreach (var a in self.World.Queries.OwnedBy[self.Owner]) - a.InflictDamage(a, a.Health, null); + a.Kill(a); self.Owner.Shroud.Disabled = true; diff --git a/OpenRA.Mods.RA/DemoTruck.cs b/OpenRA.Mods.RA/DemoTruck.cs index 96e36d1e18..6daa3102c8 100644 --- a/OpenRA.Mods.RA/DemoTruck.cs +++ b/OpenRA.Mods.RA/DemoTruck.cs @@ -45,7 +45,7 @@ namespace OpenRA.Mods.RA Sound.Play(report + ".aud", self.CenterLocation); // Remove from world - self.Health = 0; + self.Kill(self); detonatedBy.Owner.Kills++; self.Owner.Deaths++; w.Remove(self); diff --git a/OpenRA.Mods.RA/Effects/InvulnEffect.cs b/OpenRA.Mods.RA/Effects/InvulnEffect.cs index 89fc925381..e65926fe96 100644 --- a/OpenRA.Mods.RA/Effects/InvulnEffect.cs +++ b/OpenRA.Mods.RA/Effects/InvulnEffect.cs @@ -27,7 +27,7 @@ namespace OpenRA.Mods.RA.Effects public void Tick( World world ) { - if (a.IsDead || b.GetDamageModifier(null) > 0) + if (a.IsDead() || b.GetDamageModifier(null) > 0) world.AddFrameEndTask(w => w.Remove(this)); } diff --git a/OpenRA.Mods.RA/EmitInfantryOnSell.cs b/OpenRA.Mods.RA/EmitInfantryOnSell.cs index fa1612c971..9105a0c02a 100644 --- a/OpenRA.Mods.RA/EmitInfantryOnSell.cs +++ b/OpenRA.Mods.RA/EmitInfantryOnSell.cs @@ -34,8 +34,9 @@ namespace OpenRA.Mods.RA var csv = self.Info.Traits.GetOrDefault(); var valued = self.Info.Traits.GetOrDefault(); var cost = csv != null ? csv.Value : (valued != null ? valued.Cost : 0); - var hp = self.Info.Traits.Get().HP; - var hpFraction = Math.Max(info.MinHpFraction, hp / self.GetMaxHP()); + + var health = self.traits.GetOrDefault(); + var hpFraction = (health == null) ? 1f : health.HPFraction; var dudesValue = (int)(hpFraction * info.ValueFraction * cost); var eligibleLocations = Footprint.Tiles(self).ToList(); var actorTypes = info.ActorTypes.Select(a => new { Name = a, Cost = Rules.Info[a].Traits.Get().Cost }).ToArray(); diff --git a/OpenRA.Mods.RA/EngineerRepair.cs b/OpenRA.Mods.RA/EngineerRepair.cs index f1c97a3208..e5ac591150 100644 --- a/OpenRA.Mods.RA/EngineerRepair.cs +++ b/OpenRA.Mods.RA/EngineerRepair.cs @@ -39,19 +39,25 @@ namespace OpenRA.Mods.RA { if (order.OrderString != "EngineerRepair") return null; if (order.TargetActor == null) return null; - - return (order.TargetActor.Health == order.TargetActor.GetMaxHP()) ? "goldwrench-blocked" : "goldwrench"; + var health = order.TargetActor.traits.GetOrDefault(); + if (health == null) return null; + return (health.HP == health.MaxHP) ? "goldwrench-blocked" : "goldwrench"; } public string VoicePhraseForOrder(Actor self, Order order) { + var health = order.TargetActor.traits.GetOrDefault(); + if (health == null) return null; return (order.OrderString == "EngineerRepair" - && order.TargetActor.Health < order.TargetActor.GetMaxHP()) ? "Attack" : null; + && health.HP < health.MaxHP) ? "Attack" : null; } public void ResolveOrder(Actor self, Order order) { - if (order.OrderString == "EngineerRepair" && order.TargetActor.Health < order.TargetActor.GetMaxHP()) + var health = order.TargetActor.traits.GetOrDefault(); + if (health == null) return; + + if (order.OrderString == "EngineerRepair" && health.HP < health.MaxHP) { if (self.Owner == self.World.LocalPlayer) self.World.AddFrameEndTask(w => diff --git a/OpenRA.Mods.RA/Explodes.cs b/OpenRA.Mods.RA/Explodes.cs index 7869e5ed84..3f991c7b00 100644 --- a/OpenRA.Mods.RA/Explodes.cs +++ b/OpenRA.Mods.RA/Explodes.cs @@ -25,7 +25,7 @@ namespace OpenRA.Mods.RA { public void Damaged(Actor self, AttackInfo e) { - if (self.IsDead) + if (e.DamageState == DamageState.Dead) { var weapon = ChooseWeaponForExplosion(self); if (weapon != null) diff --git a/OpenRA.Mods.RA/Harvester.cs b/OpenRA.Mods.RA/Harvester.cs index 376d4ce4aa..0ebd2cfe06 100755 --- a/OpenRA.Mods.RA/Harvester.cs +++ b/OpenRA.Mods.RA/Harvester.cs @@ -164,7 +164,7 @@ namespace OpenRA.Mods.RA public void Damaged(Actor self, AttackInfo e) { - if (self.IsDead) + if (e.DamageState == DamageState.Dead) if (LinkedProc != null) LinkedProc.traits.WithInterface().FirstOrDefault().UnlinkHarvester(LinkedProc,self); } diff --git a/OpenRA.Mods.RA/Minelayer.cs b/OpenRA.Mods.RA/Minelayer.cs index 2027d498e5..b6e669efc7 100644 --- a/OpenRA.Mods.RA/Minelayer.cs +++ b/OpenRA.Mods.RA/Minelayer.cs @@ -115,7 +115,7 @@ namespace OpenRA.Mods.RA public void Tick(World world) { - if (minelayer.IsDead || !minelayer.IsInWorld) + if (minelayer.IsDead() || !minelayer.IsInWorld) world.CancelInputMode(); } diff --git a/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs b/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs index 826d6eec97..3fb588708a 100755 --- a/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/RepairOrderGenerator.cs @@ -34,8 +34,9 @@ namespace OpenRA.Mods.RA.Orders && a.traits.Contains()).FirstOrDefault(); var building = underCursor != null ? underCursor.Info.Traits.Get() : null; - - if (building != null && building.Repairable && underCursor.Health < building.HP) + var health = underCursor != null ? underCursor.traits.GetOrDefault() : null; + + if (building != null && building.Repairable && health != null && health.HPFraction < 1f) yield return new Order("Repair", underCursor); } } diff --git a/OpenRA.Mods.RA/OreRefinery.cs b/OpenRA.Mods.RA/OreRefinery.cs index a15dde588c..5e6ef0c827 100755 --- a/OpenRA.Mods.RA/OreRefinery.cs +++ b/OpenRA.Mods.RA/OreRefinery.cs @@ -92,7 +92,7 @@ namespace OpenRA.Mods.RA public void Damaged (Actor self, AttackInfo e) { - if (self.IsDead) + if (e.DamageState == DamageState.Dead) foreach (var harv in LinkedHarv) harv.traits.Get ().UnlinkProc(harv, self); } diff --git a/OpenRA.Mods.RA/Render/RenderBuildingWall.cs b/OpenRA.Mods.RA/Render/RenderBuildingWall.cs index 4d8abe5ae5..715e37472d 100755 --- a/OpenRA.Mods.RA/Render/RenderBuildingWall.cs +++ b/OpenRA.Mods.RA/Render/RenderBuildingWall.cs @@ -31,37 +31,13 @@ namespace OpenRA.Mods.RA.Render anim.PlayFetchIndex(seqName, () => adjacentWalls); } - enum ExtendedDamageState { Normal, ThreeQuarter, Half, Quarter, Dead }; - - ExtendedDamageState GetExtendedState( Actor self, int damage ) - { - var effectiveHealth = self.Health + damage; - - if (effectiveHealth <= 0) - return ExtendedDamageState.Dead; - - if (effectiveHealth < self.GetMaxHP() * self.World.Defaults.ConditionRed) - return ExtendedDamageState.Quarter; - - if (effectiveHealth < self.GetMaxHP() * self.World.Defaults.ConditionYellow) - return ExtendedDamageState.Half; - - if (effectiveHealth < self.GetMaxHP() * 0.75) - return ExtendedDamageState.ThreeQuarter; - - return ExtendedDamageState.Normal; - } - public override void Damaged(Actor self, AttackInfo e) { - var oldState = GetExtendedState(self, e.Damage); - var newState = GetExtendedState(self, 0); - var numStates = self.Info.Traits.Get().DamageStates; - if (oldState == newState) return; + if (!e.ExtendedDamageStateChanged) return; - switch (newState) + switch (e.ExtendedDamageState) { case ExtendedDamageState.Normal: seqName = "idle"; diff --git a/OpenRA.Mods.RA/Repairable.cs b/OpenRA.Mods.RA/Repairable.cs index 1c21b8f579..e5d374f523 100644 --- a/OpenRA.Mods.RA/Repairable.cs +++ b/OpenRA.Mods.RA/Repairable.cs @@ -17,10 +17,21 @@ using System.Drawing; namespace OpenRA.Mods.RA { - class RepairableInfo : TraitInfo { public readonly string[] RepairBuildings = { "fix" }; } + class RepairableInfo : ITraitInfo, ITraitPrerequisite + { + public readonly string[] RepairBuildings = { "fix" }; + + public virtual object Create(ActorInitializer init) { return new Repairable(init.self); } + } class Repairable : IIssueOrder, IResolveOrder, IOrderCursor, IOrderVoice { + Health Health; + public Repairable(Actor self) + { + Health = self.traits.Get(); + } + public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) { if (mi.Button != MouseButton.Right) return null; @@ -36,7 +47,7 @@ namespace OpenRA.Mods.RA bool CanRepair(Actor self) { var li = self.traits.GetOrDefault(); - return (self.Health < self.GetMaxHP() || (li != null && !li.FullAmmo()) ); + return (Health.HPFraction < 1f || (li != null && !li.FullAmmo()) ); } public string CursorForOrder(Actor self, Order order) diff --git a/OpenRA.Mods.RA/RepairableNear.cs b/OpenRA.Mods.RA/RepairableNear.cs index b4466218de..41cba62382 100644 --- a/OpenRA.Mods.RA/RepairableNear.cs +++ b/OpenRA.Mods.RA/RepairableNear.cs @@ -16,7 +16,7 @@ using System.Drawing; namespace OpenRA.Mods.RA { - class RepairableNearInfo : TraitInfo + class RepairableNearInfo : TraitInfo, ITraitPrerequisite { [ActorReference] public readonly string[] Buildings = { "spen", "syrd" }; @@ -28,10 +28,10 @@ namespace OpenRA.Mods.RA { if (mi.Button != MouseButton.Right) return null; if (underCursor == null) return null; - + if (underCursor.Owner == self.Owner && self.Info.Traits.Get().Buildings.Contains( underCursor.Info.Name ) && - self.Health < self.GetMaxHP()) + self.traits.Get().HPFraction < 1f) return new Order("Enter", self, underCursor); return null; diff --git a/OpenRA.Mods.RA/Reservable.cs b/OpenRA.Mods.RA/Reservable.cs index 86933c0538..a6ac5707d2 100644 --- a/OpenRA.Mods.RA/Reservable.cs +++ b/OpenRA.Mods.RA/Reservable.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA if (reservedFor == null) return; /* nothing to do */ - if (reservedFor.IsDead) reservedFor = null; /* not likely to arrive now. */ + if (reservedFor.IsDead()) reservedFor = null; /* not likely to arrive now. */ } public IDisposable Reserve(Actor forActor) diff --git a/OpenRA.Mods.RA/SelfHealing.cs b/OpenRA.Mods.RA/SelfHealing.cs index 84df7e7eed..814e8ff474 100644 --- a/OpenRA.Mods.RA/SelfHealing.cs +++ b/OpenRA.Mods.RA/SelfHealing.cs @@ -12,7 +12,7 @@ using OpenRA.Traits; namespace OpenRA.Mods.RA { - class SelfHealingInfo : TraitInfo + class SelfHealingInfo : TraitInfo, ITraitPrerequisite { public readonly int Step = 5; public readonly int Ticks = 5; @@ -27,8 +27,7 @@ namespace OpenRA.Mods.RA public void Tick(Actor self) { var info = self.Info.Traits.Get(); - - if ((float)self.Health / self.GetMaxHP() >= info.HealIfBelow) + if (self.traits.Get().HPFraction >= info.HealIfBelow) return; if (--ticks <= 0) diff --git a/OpenRA.Mods.RA/StoresOre.cs b/OpenRA.Mods.RA/StoresOre.cs index f01268efaf..533aeb6562 100644 --- a/OpenRA.Mods.RA/StoresOre.cs +++ b/OpenRA.Mods.RA/StoresOre.cs @@ -49,7 +49,7 @@ namespace OpenRA.Mods.RA public void Damaged(Actor self, AttackInfo e) { - if (self.IsDead && Player.GetSiloFullness() > 0) + if (self.IsDead() && Player.GetSiloFullness() > 0) Player.TakeOre(Stored(self)); // Lose the stored ore } diff --git a/OpenRA.Mods.RA/Wall.cs b/OpenRA.Mods.RA/Wall.cs index 9951167b4b..0cee5cfce5 100644 --- a/OpenRA.Mods.RA/Wall.cs +++ b/OpenRA.Mods.RA/Wall.cs @@ -33,6 +33,6 @@ namespace OpenRA.Mods.RA } public IEnumerable CrushClasses { get { return info.CrushClasses; } } - public void OnCrush(Actor crusher) { self.InflictDamage(crusher, self.Health, null); } + public void OnCrush(Actor crusher) { self.Kill(crusher); } } } diff --git a/mods/cnc/civilian.yaml b/mods/cnc/civilian.yaml index 99e5e71be4..69d34b7aa4 100644 --- a/mods/cnc/civilian.yaml +++ b/mods/cnc/civilian.yaml @@ -145,6 +145,7 @@ V33: BARB: Inherits: ^Wall Building: + Health: HP: 100 Armor: none Valued: @@ -152,6 +153,7 @@ BARB: WOOD: Inherits: ^Wall Building: + Health: HP: 100 Armor: none Valued: diff --git a/mods/cnc/defaults.yaml b/mods/cnc/defaults.yaml index 0ed0421a21..009b918aee 100644 --- a/mods/cnc/defaults.yaml +++ b/mods/cnc/defaults.yaml @@ -58,6 +58,7 @@ ^Infantry: Category: Infantry Unit: + Health: Armor: none RevealsShroud: Range: 4 @@ -119,10 +120,11 @@ ^CivBuilding: Inherits: ^Building - Building: - Repairable: false + Health: HP: 400 Armor: wood + Building: + Repairable: false Valued: Description: Civilian Building @@ -186,6 +188,7 @@ Unit: ROT: 0 Speed: 0 + Health: HP: 140 Armor: Heavy Husk: @@ -201,11 +204,12 @@ Targetable: TargetTypes: Ground, Water BelowUnits: + Health: + HP: 1000 Building: DamagedSound: xplos.aud DestroyedSound: xplobig4.aud Footprint: ______ ______ ______ ______ Dimensions: 6,4 - HP: 1000 RadarColorFromTerrain: Terrain: Road \ No newline at end of file diff --git a/mods/cnc/infantry.yaml b/mods/cnc/infantry.yaml index f0226387fd..db38369770 100644 --- a/mods/cnc/infantry.yaml +++ b/mods/cnc/infantry.yaml @@ -10,8 +10,9 @@ E1: Selectable: Bounds: 12,17,0,-6 Unit: - HP: 50 Speed: 4 + Health: + HP: 50 AttackBase: PrimaryWeapon: M16 TakeCover: @@ -29,8 +30,9 @@ E2: Selectable: Bounds: 12,17,0,-6 Unit: - HP: 50 Speed: 4 + Health: + HP: 50 AttackBase: PrimaryWeapon: Grenade PrimaryOffset: 0,0,0,-10 @@ -49,8 +51,9 @@ E3: Selectable: Bounds: 12,17,0,-6 Unit: - HP: 45 Speed: 3 + Health: + HP: 45 AttackBase: PrimaryWeapon: Rockets PrimaryOffset: 0,0,0,-10 @@ -70,8 +73,9 @@ E4: Selectable: Bounds: 12,17,0,-6 Unit: - HP: 90 Speed: 4 + Health: + HP: 90 AttackBase: PrimaryWeapon: Flamethrower PrimaryOffset: 0,0,0,-5 @@ -94,8 +98,9 @@ E5: Selectable: Bounds: 12,17,0,-6 Unit: - HP: 90 Speed: 4 + Health: + HP: 90 AttackBase: PrimaryWeapon: Chemspray PrimaryOffset: 0,0,0,-5 @@ -118,8 +123,9 @@ E6: Selectable: Bounds: 12,17,0,-6 Unit: - HP: 25 Speed: 4 + Health: + HP: 25 TakeCover: Passenger: ColorOfCargoPip: Yellow @@ -141,8 +147,9 @@ RMBO: Bounds: 12,17,0,-6 Voice: CommandoVoice Unit: - HP: 200 Speed: 5 + Health: + HP: 200 RevealsShroud: Range: 5 AttackBase: @@ -160,8 +167,9 @@ C1: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -176,8 +184,9 @@ C2: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -192,8 +201,9 @@ C3: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -208,8 +218,9 @@ C4: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -224,8 +235,9 @@ C5: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -240,8 +252,9 @@ C6: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -256,8 +269,9 @@ C7: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -271,8 +285,9 @@ C8: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -287,8 +302,9 @@ C9: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: @@ -303,8 +319,9 @@ C10: Cost: 70 Description: Technician Unit: - HP: 20 Speed: 4 + Health: + HP: 20 RevealsShroud: Range: 2 AttackBase: diff --git a/mods/cnc/structures.yaml b/mods/cnc/structures.yaml index 8c9860d710..200ddea604 100644 --- a/mods/cnc/structures.yaml +++ b/mods/cnc/structures.yaml @@ -8,6 +8,7 @@ FACT: Footprint: xxx xxx xxx Dimensions: 3,3 Capturable: true + Health: HP: 800 Armor: wood RevealsShroud: @@ -31,6 +32,7 @@ NUKE: Footprint: x_ xx Dimensions: 2,2 Capturable: true + Health: HP: 400 Armor: wood RevealsShroud: @@ -52,6 +54,7 @@ PROC.proxy: Footprint: ___xx xxxxx xxx__ xxx__ Dimensions: 5,4 Capturable: true + Health: HP: 900 Armor: wood RevealsShroud: @@ -70,6 +73,7 @@ PROC: Footprint: ___ xxx === Dimensions: 3,3 Capturable: true + Health: HP: 900 Armor: wood RevealsShroud: @@ -112,6 +116,7 @@ SILO: Footprint: xx Dimensions: 2,1 Capturable: true + Health: HP: 300 Armor: wood RevealsShroud: @@ -140,6 +145,7 @@ PYLE: Footprint: xx xx Dimensions: 2,2 Capturable: true + Health: HP: 800 Armor: wood RevealsShroud: @@ -165,6 +171,7 @@ HAND: Footprint: __ xx xx Dimensions: 2,3 Capturable: true + Health: HP: 800 Armor: wood RevealsShroud: @@ -190,6 +197,7 @@ AFLD: Footprint: xxxx xxxx Dimensions: 4,2 Capturable: true + Health: HP: 1000 Armor: heavy RevealsShroud: @@ -216,6 +224,7 @@ WEAP: Footprint: ___ xxx === Dimensions: 3,3 Capturable: true + Health: HP: 400 Armor: light RevealsShroud: @@ -243,6 +252,7 @@ HQ: Footprint: __ xx Dimensions: 2,2 Capturable: true + Health: HP: 1000 Armor: wood RevealsShroud: @@ -267,6 +277,7 @@ NUK2: Footprint: xx xx Dimensions: 2,2 Capturable: true + Health: HP: 600 Armor: wood RevealsShroud: @@ -288,6 +299,7 @@ FIX: Footprint: _x_ xxx _x_ Dimensions: 3,3 Capturable: true + Health: HP: 800 Armor: wood RevealsShroud: @@ -312,6 +324,7 @@ HPAD: Footprint: xx xx Dimensions: 2,2 Capturable: true + Health: HP: 800 Armor: wood RevealsShroud: @@ -343,6 +356,7 @@ EYE: Footprint: __ xx Dimensions: 2,2 Capturable: true + Health: HP: 1000 Armor: wood RevealsShroud: @@ -369,6 +383,7 @@ TMPL: Footprint: ___ xxx xxx Dimensions: 3,3 Capturable: false + Health: HP: 2000 Armor: light RevealsShroud: @@ -393,6 +408,7 @@ OBLI: Power: -150 Footprint: _ x Dimensions: 1,2 + Health: HP: 400 Armor: light RevealsShroud: @@ -419,7 +435,7 @@ CYCL: Cost: 25 Description: Chain Link Barrier LongDesc: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. - Building: + Health: HP: 300 Armor: none RenderBuildingWall: @@ -436,7 +452,7 @@ SBAG: Cost: 25 Description: Sandbag Barrier LongDesc: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. - Building: + Health: HP: 250 Armor: none @@ -451,7 +467,7 @@ BRIK: Cost: 100 Description: Concrete Barrier LongDesc: Stop units and blocks enemy fire. - Building: + Health: HP: 1000 Armor: heavy RenderBuildingWall: @@ -472,6 +488,7 @@ GUN: LongDesc: Anti-Armor base defense.\n Strong vs Tanks\n Weak vs Infantry, Aircraft Building: Power: -20 + Health: HP: 400 Armor: heavy RevealsShroud: @@ -501,6 +518,7 @@ SAM: Power: -20 Footprint: xx Dimensions: 2,1 + Health: HP: 400 Armor: heavy RevealsShroud: @@ -528,6 +546,7 @@ GTWR: LongDesc: Basic defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft Building: Power: -10 + Health: HP: 400 Armor: wood RevealsShroud: @@ -552,6 +571,7 @@ ATWR: LongDesc: Anti-armor defensive structure.\n Strong vs Light Vehicles, Tanks\n Weak vs Infantry Building: Power: -20 + Health: HP: 600 Armor: light RevealsShroud: diff --git a/mods/cnc/system.yaml b/mods/cnc/system.yaml index f00232ef7b..c8a08882e0 100644 --- a/mods/cnc/system.yaml +++ b/mods/cnc/system.yaml @@ -158,6 +158,5 @@ CRATE: SelectionShares: 5 Effect: hide-map Unit: - HP: 1 RenderUnit: BelowUnits: diff --git a/mods/cnc/trees.yaml b/mods/cnc/trees.yaml index e286b027cd..2c69fe3a55 100644 --- a/mods/cnc/trees.yaml +++ b/mods/cnc/trees.yaml @@ -1,5 +1,5 @@ SPLIT2: - Inherits: ^Building + Inherits: ^Tree RenderBuilding: Palette: terrain SeedsResource: @@ -11,7 +11,7 @@ SPLIT2: Terrain: Ore SPLIT3: - Inherits: ^Building + Inherits: ^Tree RenderBuilding: Palette: terrain SeedsResource: diff --git a/mods/cnc/vehicles.yaml b/mods/cnc/vehicles.yaml index ab677488da..0a8ff571ca 100644 --- a/mods/cnc/vehicles.yaml +++ b/mods/cnc/vehicles.yaml @@ -11,9 +11,10 @@ MCV: Selectable: Priority: 3 Unit: + Speed: 6 + Health: HP: 600 Armor: light - Speed: 6 RevealsShroud: Range: 4 TransformsOnDeploy: @@ -43,9 +44,10 @@ HARV: PipColor: Green Capacity: 28 Unit: + Speed: 6 + Health: HP: 600 Armor: light - Speed: 6 RevealsShroud: Range: 4 RenderUnit: @@ -61,10 +63,11 @@ APC: Description: Armored Personnel Carrier LongDesc: Tough infantry transport.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft Unit: - HP: 200 - Armor: heavy ROT: 5 Speed: 15 + Health: + HP: 200 + Armor: heavy RevealsShroud: Range: 5 AttackBase: @@ -90,10 +93,11 @@ ARTY: Description: Artillery LongDesc: Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft Unit: - HP: 75 - Armor: light ROT: 2 Speed: 6 + Health: + HP: 75 + Armor: light RevealsShroud: Range: 6 AttackBase: @@ -113,10 +117,11 @@ FTNK: Description: Flame Tank LongDesc: Heavily armored flame-throwing vehicle.\n Strong vs Infantry, Buildings\n Weak vs Aircraft Unit: - HP: 300 - Armor: heavy ROT: 5 Speed: 9 + Health: + HP: 300 + Armor: heavy RevealsShroud: Range: 4 AttackBase: @@ -137,10 +142,11 @@ BGGY: Description: Nod Buggy LongDesc: Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft Unit: - HP: 140 - Armor: light ROT: 10 Speed: 18 + Health: + HP: 140 + Armor: light RevealsShroud: Range: 5 Turreted: @@ -163,10 +169,11 @@ BIKE: Description: Recon Bike LongDesc: Fast scout vehicle, armed with \nrockets.\n Strong vs Vehicles, Aircraft\n Weak vs Infantry Unit: - HP: 160 - Armor: none ROT: 10 Speed: 20 + Health: + HP: 160 + Armor: none RevealsShroud: Range: 7 AttackBase: @@ -190,10 +197,11 @@ JEEP: Description: Hum-Vee LongDesc: Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft Unit: - HP: 150 - Armor: light ROT: 10 Speed: 15 + Health: + HP: 150 + Armor: light RevealsShroud: Range: 7 Turreted: @@ -216,9 +224,10 @@ LTNK: Description: Light Tank LongDesc: Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft Unit: + Speed: 9 + Health: HP: 300 Armor: Heavy - Speed: 9 RevealsShroud: Range: 4 Turreted: @@ -245,9 +254,10 @@ MTNK: Description: Medium Tank LongDesc: General-Purpose GDI Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft Unit: + Speed: 9 + Health: HP: 400 Armor: heavy - Speed: 9 RevealsShroud: Range: 5 Turreted: @@ -274,9 +284,10 @@ HTNK: Description: Mammoth Tank LongDesc: Heavily armored GDI Tank.\n Strong vs Everything Unit: + Speed: 3 + Health: HP: 600 Armor: heavy - Speed: 3 RevealsShroud: Range: 6 Turreted: @@ -309,9 +320,10 @@ MSAM: Description: Rocket Launcher LongDesc: Long range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft Unit: + Speed: 6 + Health: HP: 120 Armor: light - Speed: 6 RevealsShroud: Range: 6 Turreted: @@ -337,9 +349,10 @@ MLRS: Description: SSM Launcher LongDesc: Long range artillery.\n Strong vs Infantry, Aircraft\n Weak vs Tanks, Aircraft Unit: + Speed: 6 + Health: HP: 120 Armor: light - Speed: 6 RevealsShroud: Range: 10 Turreted: @@ -361,9 +374,10 @@ STNK: Description: Stealth Tank LongDesc: Missile tank that can bend light around \nitself to become invisible\n Strong vs Infantry, Aircraft\n Weak vs Tanks Unit: + Speed: 15 + Health: HP: 110 Armor: light - Speed: 15 RevealsShroud: Range: 4 Cloak: @@ -388,11 +402,12 @@ TRAN: Description: Chinook Transport LongDesc: Fast Infantry Transport Helicopter.\n Unarmed Unit: - HP: 90 - Armor: light InitialFacing: 20 ROT: 5 Speed: 15 + Health: + HP: 90 + Armor: light RevealsShroud: Range: 8 RenderUnitRotor: @@ -415,11 +430,12 @@ HELI: Description: Apache Longbow LongDesc: Helicopter Gunship with AG Missiles.\n Strong vs Buildings, Tanks\n Weak vs Infantry Unit: - HP: 125 - Armor: heavy InitialFacing: 20 ROT: 4 Speed: 20 + Health: + HP: 125 + Armor: heavy RevealsShroud: Range: 8 AttackBase: @@ -443,11 +459,12 @@ ORCA: Description: Orca LongDesc: Helicopter Gunship with AG Missiles.\n Strong vs Buildings, Tanks\n Weak vs Infantry Unit: - HP: 125 - Armor: heavy InitialFacing: 20 ROT: 4 Speed: 20 + Health: + HP: 125 + Armor: heavy RevealsShroud: Range: 8 AttackBase: @@ -463,10 +480,11 @@ C17: LZRange: 1 Inherits: ^Plane Unit: - HP: 25 - Armor: light ROT: 5 Speed: 25 + Health: + HP: 25 + Armor: light Plane: RenderUnit: WithShadow: @@ -480,10 +498,11 @@ C17: A10: Inherits: ^Plane Unit: - HP: 60 - Armor: light ROT: 4 Speed: 25 + Health: + HP: 60 + Armor: light Plane: RenderUnit: WithShadow: