diff --git a/OpenRA.Game/Actor.cs b/OpenRA.Game/Actor.cs index f9124984dc..14a7ae115f 100755 --- a/OpenRA.Game/Actor.cs +++ b/OpenRA.Game/Actor.cs @@ -176,8 +176,10 @@ namespace OpenRA var oldState = GetDamageState(); /* apply the damage modifiers, if we have any. */ - damage = (int)traits.WithInterface().Aggregate( - (float)damage, (a, t) => t.GetDamageModifier() * a); + var modifier = (float)traits.WithInterface() + .Select(t => t.GetDamageModifier()).Product(); + + damage = (int)(damage * modifier); Health -= damage; if (Health <= 0) @@ -194,8 +196,8 @@ namespace OpenRA if (Health > maxHP) Health = maxHP; - Log.Write("InflictDamage: {0} #{1} -> {2} #{3} raw={4} adj={5} hp={6}", - attacker.Info.Name, attacker.ActorID, Info.Name, ActorID, rawDamage, damage, Health); + Log.Write("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(); diff --git a/OpenRA.Game/Combat.cs b/OpenRA.Game/Combat.cs index 4048b7a51c..b5c04a47b8 100644 --- a/OpenRA.Game/Combat.cs +++ b/OpenRA.Game/Combat.cs @@ -81,8 +81,10 @@ namespace OpenRA args.dest, warhead.Damage, firepowerModifier, maxSpread); foreach (var victim in hitActors) - victim.InflictDamage(args.firedBy, - (int)GetDamageToInflict(victim, args, warhead, firepowerModifier), warhead); + { + var damage = (int)GetDamageToInflict(victim, args, warhead, firepowerModifier); + victim.InflictDamage(args.firedBy, damage, warhead); + } } break; case DamageModel.PerCell: @@ -126,6 +128,20 @@ namespace OpenRA DoImpacts(args); } + static readonly float[] falloff = + { + 1f, 0.3678795f, 0.1353353f, 0.04978707f, + 0.01831564f, 0.006737947f, 0.002478752f, 0.000911882f + }; + + static float GetDamageFalloff(float x) + { + var u = (int)x; + if (u >= falloff.Length - 1) return 0; + var t = x - u; + return (falloff[u] * (1 - t)) + (falloff[u + 1] * t); + } + static float GetDamageToInflict(Actor target, ProjectileArgs args, WarheadInfo warhead, float modifier) { // don't hit air units with splash from ground explosions, etc @@ -133,10 +149,15 @@ namespace OpenRA var selectable = target.Info.Traits.GetOrDefault(); var radius = selectable != null ? selectable.Radius : 0; - var distance = Math.Max(0, (target.CenterLocation - args.dest).Length - radius); - var rawDamage = warhead.Damage * modifier * (float)Math.Exp(-distance / warhead.Spread); - var multiplier = warhead.EffectivenessAgainst(target.Info.Traits.Get().Armor); - return rawDamage * multiplier; + 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); + + Log.Write("GetDamageToInflict {0} #{1} r={2} dist={3} falloff={4} raw={5} mul={6}", + target.Info.Name, target.ActorID, radius, distance, falloff, rawDamage, multiplier); + + return (float)(rawDamage * multiplier); } public static bool WeaponValidForTarget(WeaponInfo weapon, Actor target)