diff --git a/OpenRA.Mods.Common/AI/SupportPowerDecision.cs b/OpenRA.Mods.Common/AI/SupportPowerDecision.cs index b8f6d0ad6f..89c94d6a36 100644 --- a/OpenRA.Mods.Common/AI/SupportPowerDecision.cs +++ b/OpenRA.Mods.Common/AI/SupportPowerDecision.cs @@ -160,7 +160,12 @@ namespace OpenRA.Mods.Common.AI case DecisionMetric.Health: var health = a.TraitOrDefault(); - return (health != null) ? (health.HP / health.MaxHP) * Attractiveness : 0; + + if (health == null) + return 0; + + // Cast to long to avoid overflow when multiplying by the health + return (int)((long)health.HP * Attractiveness / health.MaxHP); default: return Attractiveness; diff --git a/OpenRA.Mods.Common/Activities/Sell.cs b/OpenRA.Mods.Common/Activities/Sell.cs index c33eb812a5..49d2b1f84b 100644 --- a/OpenRA.Mods.Common/Activities/Sell.cs +++ b/OpenRA.Mods.Common/Activities/Sell.cs @@ -34,9 +34,12 @@ namespace OpenRA.Mods.Common.Activities public override Activity Tick(Actor self) { - var cost = self.GetSellValue(); + var sellValue = self.GetSellValue(); - var refund = (cost * sellableInfo.RefundPercent * (health == null ? 1 : health.HP)) / (100 * (health == null ? 1 : health.MaxHP)); + // Cast to long to avoid overflow when multiplying by the health + var hp = health != null ? (long)health.HP : 1L; + var maxHP = health != null ? (long)health.MaxHP : 1L; + var refund = (int)((sellValue * sellableInfo.RefundPercent * hp) / (100 * maxHP)); playerResources.GiveCash(refund); foreach (var ns in self.TraitsImplementing()) diff --git a/OpenRA.Mods.Common/Activities/Transform.cs b/OpenRA.Mods.Common/Activities/Transform.cs index 324bc1dfaa..5ff8c97767 100644 --- a/OpenRA.Mods.Common/Activities/Transform.cs +++ b/OpenRA.Mods.Common/Activities/Transform.cs @@ -122,7 +122,8 @@ namespace OpenRA.Mods.Common.Activities var health = self.TraitOrDefault(); if (health != null) { - var newHP = ForceHealthPercentage > 0 ? ForceHealthPercentage : (health.HP * 100) / health.MaxHP; + // Cast to long to avoid overflow when multiplying by the health + var newHP = ForceHealthPercentage > 0 ? ForceHealthPercentage : (int)(health.HP * 100L / health.MaxHP); init.Add(new HealthInit(newHP)); } diff --git a/OpenRA.Mods.Common/Traits/Captures.cs b/OpenRA.Mods.Common/Traits/Captures.cs index 4a1def3d7f..155388c15e 100644 --- a/OpenRA.Mods.Common/Traits/Captures.cs +++ b/OpenRA.Mods.Common/Traits/Captures.cs @@ -109,7 +109,9 @@ namespace OpenRA.Mods.Common.Traits } var health = target.Trait(); - var lowEnoughHealth = health.HP <= c.Info.CaptureThreshold * health.MaxHP / 100; + + // Cast to long to avoid overflow when multiplying by the health + var lowEnoughHealth = health.HP <= (int)(c.Info.CaptureThreshold * (long)health.MaxHP / 100); cursor = !capturesInfo.Sabotage || lowEnoughHealth || target.Owner.NonCombatant ? capturesInfo.EnterCursor : capturesInfo.SabotageCursor; @@ -129,7 +131,9 @@ namespace OpenRA.Mods.Common.Traits } var health = target.Info.TraitInfoOrDefault(); - var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP / 100; + + // Cast to long to avoid overflow when multiplying by the health + var lowEnoughHealth = target.HP <= (int)(c.CaptureThreshold * (long)health.HP / 100); cursor = !capturesInfo.Sabotage || lowEnoughHealth || target.Owner.NonCombatant ? capturesInfo.EnterCursor : capturesInfo.SabotageCursor; diff --git a/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs b/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs index 65ec04e343..5dd228b84a 100644 --- a/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs +++ b/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs @@ -60,8 +60,9 @@ namespace OpenRA.Mods.Common.Traits var dudesValue = info.ValuePercent * cost / 100; if (health != null) { - if (100 * health.HP >= info.MinHpPercent * health.MaxHP) - dudesValue = health.HP * dudesValue / health.MaxHP; + // Cast to long to avoid overflow when multiplying by the health + if (100L * health.HP >= info.MinHpPercent * (long)health.MaxHP) + dudesValue = (int)((long)health.HP * dudesValue / health.MaxHP); else dudesValue = 0; } diff --git a/OpenRA.Mods.Common/Traits/Explodes.cs b/OpenRA.Mods.Common/Traits/Explodes.cs index 13647337c2..33167818f1 100644 --- a/OpenRA.Mods.Common/Traits/Explodes.cs +++ b/OpenRA.Mods.Common/Traits/Explodes.cs @@ -134,7 +134,8 @@ namespace OpenRA.Mods.Common.Traits if (Info.DamageThreshold == 0) return; - if (health.HP * 100 < Info.DamageThreshold * health.MaxHP) + // Cast to long to avoid overflow when multiplying by the health + if (health.HP * 100L < Info.DamageThreshold * (long)health.MaxHP) self.World.AddFrameEndTask(w => self.Kill(e.Attacker)); } } diff --git a/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs b/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs index 768f55453d..be8ec4e78e 100644 --- a/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs +++ b/OpenRA.Mods.Common/Traits/Power/ScalePowerWithHealth.cs @@ -32,7 +32,8 @@ namespace OpenRA.Mods.Common.Traits int IPowerModifier.GetPowerModifier() { - return 100 * health.HP / health.MaxHP; + // Cast to long to avoid overflow when multiplying by the health + return (int)(100L * health.HP / health.MaxHP); } void INotifyDamage.Damaged(Actor self, AttackInfo e) { power.UpdateActor(self); } diff --git a/OpenRA.Mods.Common/Traits/SelfHealing.cs b/OpenRA.Mods.Common/Traits/SelfHealing.cs index 3d85ef2473..19bb2905ed 100644 --- a/OpenRA.Mods.Common/Traits/SelfHealing.cs +++ b/OpenRA.Mods.Common/Traits/SelfHealing.cs @@ -55,7 +55,8 @@ namespace OpenRA.Mods.Common.Traits if (self.IsDead || IsTraitDisabled) return; - if (health.HP >= Info.HealIfBelow * health.MaxHP / 100) + // Cast to long to avoid overflow when multiplying by the health + if (health.HP >= Info.HealIfBelow * (long)health.MaxHP / 100) return; if (damageTicks > 0) @@ -67,7 +68,9 @@ namespace OpenRA.Mods.Common.Traits if (--ticks <= 0) { ticks = Info.Delay; - self.InflictDamage(self, new Damage(-(Info.Step + Info.PercentageStep * health.MaxHP / 100), Info.DamageTypes)); + + // Cast to long to avoid overflow when multiplying by the health + self.InflictDamage(self, new Damage((int)(-(Info.Step + Info.PercentageStep * (long)health.MaxHP / 100)), Info.DamageTypes)); } } diff --git a/OpenRA.Mods.Common/Traits/Sellable.cs b/OpenRA.Mods.Common/Traits/Sellable.cs index 78a7fd4fc1..dfd1703178 100644 --- a/OpenRA.Mods.Common/Traits/Sellable.cs +++ b/OpenRA.Mods.Common/Traits/Sellable.cs @@ -94,14 +94,14 @@ namespace OpenRA.Mods.Common.Traits { get { - var sellValue = self.GetSellValue() * info.RefundPercent / 100; - if (health.Value != null) - { - sellValue *= health.Value.HP; - sellValue /= health.Value.MaxHP; - } + var sellValue = self.GetSellValue(); - return "Refund: $" + sellValue; + // Cast to long to avoid overflow when multiplying by the health + var hp = health != null ? (long)health.Value.HP : 1L; + var maxHP = health != null ? (long)health.Value.MaxHP : 1L; + var refund = (int)((sellValue * info.RefundPercent * hp) / (100 * maxHP)); + + return "Refund: $" + refund; } } }