diff --git a/OpenRA.Game/WPos.cs b/OpenRA.Game/WPos.cs index 369aca6bab..0966d6c849 100644 --- a/OpenRA.Game/WPos.cs +++ b/OpenRA.Game/WPos.cs @@ -64,8 +64,8 @@ namespace OpenRA return ret; // Add an additional quadratic variation to height - // Attempts to avoid integer overflow by keeping the intermediate variables reasonably sized - var offset = (int)(((((((long)(b - a).Length * mul) / div) * (div - mul)) / div) * pitch.Tan()) / 1024); + // Uses decimal to avoid integer overflow + var offset = (int)((decimal)(b - a).Length * pitch.Tan() * mul * (div - mul) / (1024 * div * div)); return new WPos(ret.X, ret.Y, ret.Z + offset); } diff --git a/OpenRA.Game/WVec.cs b/OpenRA.Game/WVec.cs index bfce594265..068845bf37 100644 --- a/OpenRA.Game/WVec.cs +++ b/OpenRA.Game/WVec.cs @@ -82,8 +82,8 @@ namespace OpenRA return ret; // Add an additional quadratic variation to height - // Uses fp to avoid integer overflow - var offset = (int)((float)(b - a).Length * pitch.Tan() * mul * (div - mul) / (1024 * div * div)); + // Uses decimal to avoid integer overflow + var offset = (int)((decimal)(b - a).Length * pitch.Tan() * mul * (div - mul) / (1024 * div * div)); return new WVec(ret.X, ret.Y, ret.Z + offset); } diff --git a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs index a312cd4be7..25c0fc6091 100644 --- a/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs +++ b/OpenRA.Mods.Common/Activities/Air/ReturnToBase.cs @@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Activities int CalculateTurnRadius(int speed) { - return (int)(141 * speed / planeInfo.TurnSpeed / (float)Math.PI); + return 45 * speed / planeInfo.TurnSpeed; } } } diff --git a/OpenRA.Mods.Common/Activities/CaptureActor.cs b/OpenRA.Mods.Common/Activities/CaptureActor.cs index 25c80943ff..507b8cb0f7 100644 --- a/OpenRA.Mods.Common/Activities/CaptureActor.cs +++ b/OpenRA.Mods.Common/Activities/CaptureActor.cs @@ -53,7 +53,7 @@ namespace OpenRA.Mods.Common.Activities if (actor.IsDead || capturable.BeingCaptured) return; - var lowEnoughHealth = health.HP <= capturable.Info.CaptureThreshold * health.MaxHP; + var lowEnoughHealth = health.HP <= capturable.Info.CaptureThreshold * health.MaxHP / 100; if (!capturesInfo.Sabotage || lowEnoughHealth || actor.Owner.NonCombatant) { var oldOwner = actor.Owner; @@ -68,7 +68,7 @@ namespace OpenRA.Mods.Common.Activities } else { - var damage = (int)(health.MaxHP * capturesInfo.SabotageHPRemoval); + var damage = health.MaxHP * capturesInfo.SabotageHPRemoval / 100; actor.InflictDamage(self, damage, null); } diff --git a/OpenRA.Mods.Common/Traits/Capturable.cs b/OpenRA.Mods.Common/Traits/Capturable.cs index 3b81a98980..b55962b4d4 100644 --- a/OpenRA.Mods.Common/Traits/Capturable.cs +++ b/OpenRA.Mods.Common/Traits/Capturable.cs @@ -22,7 +22,7 @@ namespace OpenRA.Mods.Common.Traits public readonly bool AllowNeutral = true; public readonly bool AllowEnemies = true; [Desc("Health percentage the target must be at (or below) before it can be captured.")] - public readonly float CaptureThreshold = 0.5f; + public readonly int CaptureThreshold = 50; public readonly bool CancelActivity = false; public object Create(ActorInitializer init) { return new Capturable(this); } diff --git a/OpenRA.Mods.Common/Traits/Captures.cs b/OpenRA.Mods.Common/Traits/Captures.cs index 975dce7895..8c584d5544 100644 --- a/OpenRA.Mods.Common/Traits/Captures.cs +++ b/OpenRA.Mods.Common/Traits/Captures.cs @@ -25,7 +25,7 @@ namespace OpenRA.Mods.Common.Traits [Desc("Unit will do damage to the actor instead of capturing it. Unit is destroyed when sabotaging.")] public readonly bool Sabotage = true; [Desc("Only used if Sabotage=true. Sabotage damage expressed as a percentage of enemy health removed.")] - public readonly float SabotageHPRemoval = 0.5f; + public readonly int SabotageHPRemoval = 50; [VoiceReference] public readonly string Voice = "Action"; @@ -101,7 +101,7 @@ namespace OpenRA.Mods.Common.Traits } var health = target.Trait(); - var lowEnoughHealth = health.HP <= c.CaptureThreshold * health.MaxHP; + var lowEnoughHealth = health.HP <= c.CaptureThreshold * health.MaxHP / 100; cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant ? "enter" : "capture"; @@ -118,7 +118,7 @@ namespace OpenRA.Mods.Common.Traits } var health = target.Info.TraitInfoOrDefault(); - var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP; + var lowEnoughHealth = target.HP <= c.CaptureThreshold * health.HP / 100; cursor = !sabotage || lowEnoughHealth || target.Owner.NonCombatant ? "enter" : "capture"; diff --git a/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs b/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs index d9814a198e..60ab45657a 100644 --- a/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs +++ b/OpenRA.Mods.Common/Traits/EmitInfantryOnSell.cs @@ -19,8 +19,8 @@ namespace OpenRA.Mods.Common.Traits [Desc("Spawn new actors when sold.")] public class EmitInfantryOnSellInfo : ITraitInfo { - public readonly float ValuePercent = 40; - public readonly float MinHpPercent = 30; + public readonly int ValuePercent = 40; + public readonly int MinHpPercent = 30; [ActorReference] [Desc("Be sure to use lowercase. Default value is \"e1\".")] @@ -59,8 +59,12 @@ namespace OpenRA.Mods.Common.Traits var health = self.TraitOrDefault(); var dudesValue = info.ValuePercent * cost; if (health != null) - dudesValue = dudesValue * health.HP / health.MaxHP; - dudesValue /= 100; + { + if (100 * health.HP >= info.MinHpPercent * health.MaxHP) + dudesValue = health.HP * dudesValue / health.MaxHP; + else + dudesValue = 0; + } var eligibleLocations = FootprintUtils.Tiles(self).ToList(); var actorTypes = info.ActorTypes.Select(a => new { Name = a, Cost = self.World.Map.Rules.Actors[a].TraitInfo().Cost }).ToList(); diff --git a/OpenRA.Mods.Common/Traits/SelfHealing.cs b/OpenRA.Mods.Common/Traits/SelfHealing.cs index ca4b2dd6b7..98a782b613 100644 --- a/OpenRA.Mods.Common/Traits/SelfHealing.cs +++ b/OpenRA.Mods.Common/Traits/SelfHealing.cs @@ -18,7 +18,8 @@ namespace OpenRA.Mods.Common.Traits { public readonly int Step = 5; public readonly int Delay = 5; - public readonly float HealIfBelow = .5f; + [Desc("Heal if current health is below this percentage of full health.")] + public readonly int HealIfBelow = 50; public readonly int DamageCooldown = 0; public override object Create(ActorInitializer init) { return new SelfHealing(init.Self, this); } @@ -42,7 +43,7 @@ namespace OpenRA.Mods.Common.Traits if (self.IsDead || IsTraitDisabled) return; - if (health.HP >= Info.HealIfBelow * health.MaxHP) + if (health.HP >= Info.HealIfBelow * health.MaxHP / 100) return; if (damageTicks > 0) diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 2a144f97e1..0d19186713 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -644,9 +644,10 @@ namespace OpenRA.Mods.Common.UtilityCommands node.Key = "ReloadDelay"; } - // Migrated ProductionQueue BuildSpeed to use int percentage instead of float - if (engineVersion < 20160325) + // Got rid of most remaining usages of float in a bid to further reduce desync risk + if (engineVersion < 20160328) { + // Migrated ProductionQueue BuildSpeed to use int percentage instead of float if (node.Key.StartsWith("ProductionQueue") || node.Key.StartsWith("ClassicProductionQueue")) { var buildSpeedNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "BuildSpeed"); @@ -658,11 +659,8 @@ namespace OpenRA.Mods.Common.UtilityCommands buildSpeedNode.Value.Value = newValue.ToString(); } } - } - // Migrated StrategicVictoryConditions RatioRequired to use int percentage instead of float - if (engineVersion < 20160325) - { + // Migrated StrategicVictoryConditions RatioRequired to use int percentage instead of float if (node.Key.StartsWith("StrategicVictoryConditions")) { var ratioNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "RatioRequired"); @@ -674,11 +672,8 @@ namespace OpenRA.Mods.Common.UtilityCommands ratioNode.Value.Value = newValue.ToString(); } } - } - // Migrated Minelayer.MinefieldDepth to use WDist instead of float - if (engineVersion < 20160325) - { + // Migrated Minelayer.MinefieldDepth to use WDist instead of float if (node.Key.StartsWith("Minelayer")) { var depthNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "MinefieldDepth"); @@ -690,6 +685,69 @@ namespace OpenRA.Mods.Common.UtilityCommands depthNode.Value.Value = newValue.ToString(); } } + + // Migrated SelfHealing to use int percentage instead of float + if (node.Key == "SelfHealing") + { + var healIfBelowNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "HealIfBelow"); + if (healIfBelowNode != null) + { + // The HealIfBelow value is now an int percentage, so multiply the float with 100. + var oldValue = FieldLoader.GetValue("HealIfBelow", healIfBelowNode.Value.Value); + var newValue = (int)(oldValue * 100); + healIfBelowNode.Value.Value = newValue.ToString(); + } + } + + // Migrated EmitInfantryOnSell to use int percentage instead of float + if (node.Key == "EmitInfantryOnSell") + { + var valueNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "ValuePercent"); + var minHPNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "MinHpPercent"); + + if (valueNode != null) + { + // The ValuePercent value is now an int percentage, but was previously geared towards + // percentage rather than float and divided by 100 internally so division by 100 is NOT needed. + var oldValue = FieldLoader.GetValue("ValuePercent", valueNode.Value.Value); + var newValue = (int)oldValue; + valueNode.Value.Value = newValue.ToString(); + } + + if (minHPNode != null) + { + // The MinHpPercent value is now an int percentage, but was previously geared towards + // percentage rather than float and divided by 100 internally so division by 100 is NOT needed. + var oldValue = FieldLoader.GetValue("MinHpPercent", minHPNode.Value.Value); + var newValue = (int)oldValue; + minHPNode.Value.Value = newValue.ToString(); + } + } + + // Migrated Captures and Capturable to use int percentage instead of float + if (node.Key == "Captures") + { + var sabotageHPRemNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "SabotageHPRemoval"); + if (sabotageHPRemNode != null) + { + // The SabotageHPRemoval value is now an int percentage, so multiply the float with 100. + var oldValue = FieldLoader.GetValue("SabotageHPRemoval", sabotageHPRemNode.Value.Value); + var newValue = (int)(oldValue * 100); + sabotageHPRemNode.Value.Value = newValue.ToString(); + } + } + + if (node.Key == "Capturable") + { + var captThreshNode = node.Value.Nodes.FirstOrDefault(x => x.Key == "CaptureThreshold"); + if (captThreshNode != null) + { + // The CaptureThreshold value is now an int percentage, so multiply the float with 100. + var oldValue = FieldLoader.GetValue("CaptureThreshold", captThreshNode.Value.Value); + var newValue = (int)(oldValue * 100); + captThreshNode.Value.Value = newValue.ToString(); + } + } } UpgradeActorRules(engineVersion, ref node.Value.Nodes, node, depth + 1); diff --git a/mods/cnc/maps/gdi06/rules.yaml b/mods/cnc/maps/gdi06/rules.yaml index e0c9cafecb..d19de9dfd1 100644 --- a/mods/cnc/maps/gdi06/rules.yaml +++ b/mods/cnc/maps/gdi06/rules.yaml @@ -70,7 +70,7 @@ RMBO.easy: HP: 300 SelfHealing: Delay: 10 - HealIfBelow: 50% + HealIfBelow: 50 DamageCooldown: 200 RenderSprites: Image: RMBO diff --git a/mods/cnc/maps/nod03a/rules.yaml b/mods/cnc/maps/nod03a/rules.yaml index 4c20331bb0..4ca994bf35 100644 --- a/mods/cnc/maps/nod03a/rules.yaml +++ b/mods/cnc/maps/nod03a/rules.yaml @@ -89,4 +89,4 @@ MISS: Tooltip: Name: Prison Capturable: - CaptureThreshold: 1 + CaptureThreshold: 100 diff --git a/mods/cnc/maps/nod03b/rules.yaml b/mods/cnc/maps/nod03b/rules.yaml index 5bb60d3644..29d3bd212a 100644 --- a/mods/cnc/maps/nod03b/rules.yaml +++ b/mods/cnc/maps/nod03b/rules.yaml @@ -89,4 +89,4 @@ MISS: Tooltip: Name: Prison Capturable: - CaptureThreshold: 1 + CaptureThreshold: 100 diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index b2bb37611d..5b4595863d 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -688,7 +688,7 @@ Capturable: Type: husk AllowAllies: yes - CaptureThreshold: 1.0 + CaptureThreshold: 100 TransformOnCapture: ForceHealthPercentage: 25 Tooltip: diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index 76d4f81bb4..04984c3f61 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -411,7 +411,7 @@ HTNK: AutoTarget: SelfHealing: Delay: 10 - HealIfBelow: 50% + HealIfBelow: 50 DamageCooldown: 200 SpawnActorOnDeath: Actor: HTNK.Husk diff --git a/mods/d2k/rules/aircraft.yaml b/mods/d2k/rules/aircraft.yaml index aa41a11d0b..d0d73bdfee 100644 --- a/mods/d2k/rules/aircraft.yaml +++ b/mods/d2k/rules/aircraft.yaml @@ -39,7 +39,7 @@ carryall.reinforce: SelfHealing: Step: 5 Delay: 3 - HealIfBelow: 50% + HealIfBelow: 50 carryall: Inherits: carryall.reinforce diff --git a/mods/d2k/rules/defaults.yaml b/mods/d2k/rules/defaults.yaml index 8db6bc0a06..f45670e956 100644 --- a/mods/d2k/rules/defaults.yaml +++ b/mods/d2k/rules/defaults.yaml @@ -254,7 +254,7 @@ Adjacent: 3 GivesBuildableArea: Capturable: - CaptureThreshold: 1.0 + CaptureThreshold: 100 SoundOnDamageTransition: DamagedSounds: EXPLSML1.WAV DestroyedSounds: EXPLHG1.WAV diff --git a/mods/d2k/rules/vehicles.yaml b/mods/d2k/rules/vehicles.yaml index c5a82a4c15..760dbf3857 100644 --- a/mods/d2k/rules/vehicles.yaml +++ b/mods/d2k/rules/vehicles.yaml @@ -44,7 +44,7 @@ mcv: SelfHealing: Step: 5 Delay: 3 - HealIfBelow: 50% + HealIfBelow: 50 harvester: Inherits: ^Vehicle @@ -95,7 +95,7 @@ harvester: SelfHealing: Step: 5 Delay: 3 - HealIfBelow: 50% + HealIfBelow: 50 trike: Inherits: ^Vehicle @@ -336,7 +336,7 @@ devastator: SelfHealing: Step: 5 Delay: 3 - HealIfBelow: 50% + HealIfBelow: 50 raider: Inherits: ^Vehicle diff --git a/mods/ra/maps/allies-03a/rules.yaml b/mods/ra/maps/allies-03a/rules.yaml index 8e2a2a0f3b..c35f69b523 100644 --- a/mods/ra/maps/allies-03a/rules.yaml +++ b/mods/ra/maps/allies-03a/rules.yaml @@ -52,7 +52,7 @@ World: ^Building: Capturable: - CaptureThreshold: 0.25 + CaptureThreshold: 25 Tooltip: GenericVisibility: Enemy ShowOwnerRow: false diff --git a/mods/ra/maps/allies-03b/rules.yaml b/mods/ra/maps/allies-03b/rules.yaml index 23e17388c3..e65f231e5d 100644 --- a/mods/ra/maps/allies-03b/rules.yaml +++ b/mods/ra/maps/allies-03b/rules.yaml @@ -52,7 +52,7 @@ World: ^Building: Capturable: - CaptureThreshold: 0.25 + CaptureThreshold: 25 Tooltip: GenericVisibility: Enemy ShowOwnerRow: false diff --git a/mods/ra/maps/bomber-john/rules.yaml b/mods/ra/maps/bomber-john/rules.yaml index c7834f7bb0..2043a106df 100644 --- a/mods/ra/maps/bomber-john/rules.yaml +++ b/mods/ra/maps/bomber-john/rules.yaml @@ -160,7 +160,7 @@ MINVV: SelfHealing: Step: -1 Delay: 1 - HealIfBelow: 101% + HealIfBelow: 101 DamageCooldown: 0 Explodes: Weapon: CrateNuke diff --git a/mods/ra/maps/fort-lonestar/rules.yaml b/mods/ra/maps/fort-lonestar/rules.yaml index c4c48f89a7..c2717eb3ef 100644 --- a/mods/ra/maps/fort-lonestar/rules.yaml +++ b/mods/ra/maps/fort-lonestar/rules.yaml @@ -307,7 +307,7 @@ V2RL: SelfHealing: Step: 2 Delay: 1 - HealIfBelow: 40% + HealIfBelow: 40 BADR.Bomber: Health: diff --git a/mods/ra/maps/intervention/rules.yaml b/mods/ra/maps/intervention/rules.yaml index 7d9dce836b..e917f4ea89 100644 --- a/mods/ra/maps/intervention/rules.yaml +++ b/mods/ra/maps/intervention/rules.yaml @@ -42,7 +42,7 @@ MISS: AllowAllies: False AllowNeutral: False AllowEnemies: True - CaptureThreshold: 1.0 + CaptureThreshold: 100 E6.MOD: Inherits: E6 diff --git a/mods/ra/maps/monster-tank-madness/rules.yaml b/mods/ra/maps/monster-tank-madness/rules.yaml index 2fd5c2b1fc..2f091e1cae 100644 --- a/mods/ra/maps/monster-tank-madness/rules.yaml +++ b/mods/ra/maps/monster-tank-madness/rules.yaml @@ -160,7 +160,7 @@ PBOX: SelfHealing: Step: 1 Delay: 1 - HealIfBelow: 100% + HealIfBelow: 100 DamageCooldown: 150 Selectable: Bounds: 44,38,0,-4 diff --git a/mods/ra/rules/defaults.yaml b/mods/ra/rules/defaults.yaml index 290498a08d..b8e787b123 100644 --- a/mods/ra/rules/defaults.yaml +++ b/mods/ra/rules/defaults.yaml @@ -112,7 +112,7 @@ AllowUnsuitableCell: false Capturable: Type: vehicle - CaptureThreshold: 1 + CaptureThreshold: 100 CancelActivity: True CaptureNotification: Notification: UnitStolen @@ -620,7 +620,7 @@ Capturable: Type: husk AllowAllies: true - CaptureThreshold: 1.0 + CaptureThreshold: 100 TransformOnCapture: ForceHealthPercentage: 25 DisabledOverlay: diff --git a/mods/ra/rules/vehicles.yaml b/mods/ra/rules/vehicles.yaml index 80278c6aa3..88e4685976 100644 --- a/mods/ra/rules/vehicles.yaml +++ b/mods/ra/rules/vehicles.yaml @@ -192,7 +192,7 @@ V2RL: SelfHealing: Step: 1 Delay: 3 - HealIfBelow: 50% + HealIfBelow: 50 DamageCooldown: 150 SelectionDecorations: VisualBounds: 44,38,0,-4 diff --git a/mods/ts/rules/civilian-vehicles.yaml b/mods/ts/rules/civilian-vehicles.yaml index 2df93e87e7..c918bf7478 100644 --- a/mods/ts/rules/civilian-vehicles.yaml +++ b/mods/ts/rules/civilian-vehicles.yaml @@ -28,7 +28,7 @@ AutoTarget: SelfHealing: Delay: 10 - HealIfBelow: 50% + HealIfBelow: 50 DamageCooldown: 200 WithVoxelTurret: WithVoxelBarrel: diff --git a/mods/ts/rules/defaults.yaml b/mods/ts/rules/defaults.yaml index 8a964c00e9..eafcacf11c 100644 --- a/mods/ts/rules/defaults.yaml +++ b/mods/ts/rules/defaults.yaml @@ -418,7 +418,7 @@ ActorLostNotification: Capturable: Type: Vehicle - CaptureThreshold: 1 + CaptureThreshold: 100 CancelActivity: True Guard: Voice: Move diff --git a/mods/ts/rules/nod-vehicles.yaml b/mods/ts/rules/nod-vehicles.yaml index 35b35e49b9..90cf5ca203 100644 --- a/mods/ts/rules/nod-vehicles.yaml +++ b/mods/ts/rules/nod-vehicles.yaml @@ -246,7 +246,7 @@ WEED: HP: 600 SelfHealing: Delay: 10 - HealIfBelow: 50% + HealIfBelow: 50 DamageCooldown: 200 Armor: Type: Heavy diff --git a/mods/ts/rules/shared-vehicles.yaml b/mods/ts/rules/shared-vehicles.yaml index b4d8151f66..d1e192c882 100644 --- a/mods/ts/rules/shared-vehicles.yaml +++ b/mods/ts/rules/shared-vehicles.yaml @@ -76,7 +76,7 @@ HARV: HP: 1000 SelfHealing: Delay: 10 - HealIfBelow: 50% + HealIfBelow: 50 DamageCooldown: 200 Armor: Type: Heavy