diff --git a/OpenRa.DataStructures/float2.cs b/OpenRa.DataStructures/float2.cs index 9544b94eb4..7377c9ea59 100644 --- a/OpenRa.DataStructures/float2.cs +++ b/OpenRa.DataStructures/float2.cs @@ -86,5 +86,8 @@ namespace OpenRa public static float2 Max(float2 a, float2 b) { return new float2(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); } public static float2 Min(float2 a, float2 b) { return new float2(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); } + + public float LengthSquared { get { return X * X + Y * Y; } } + public float Length { get { return (float)Math.Sqrt(LengthSquared); } } } } diff --git a/OpenRa.Game/Actor.cs b/OpenRa.Game/Actor.cs index b66edc8dca..84d88bead6 100755 --- a/OpenRa.Game/Actor.cs +++ b/OpenRa.Game/Actor.cs @@ -77,6 +77,27 @@ namespace OpenRa.Game var loc = CenterLocation - 0.5f * size; return new RectangleF(loc.X, loc.Y, size.X, size.Y); } + } + + public void InflictDamage(Actor attacker, Bullet inflictor, int damage) + { + /* todo: auto-retaliate, etc */ + /* todo: death sequence for infantry based on inflictor */ + /* todo: start smoking if < conditionYellow and took damage, and not already smoking + + if (Health <= 0) return; /* overkill! */ + + Health -= damage; + if (Health <= 0) + { + Health = 0; + if (attacker.Owner != null) + attacker.Owner.Kills++; + + Game.world.AddFrameEndTask(w => w.Remove(this)); + + /* todo: explosion */ + } } } } diff --git a/OpenRa.Game/Bullet.cs b/OpenRa.Game/Bullet.cs index 275c804d75..f31c018eda 100644 --- a/OpenRa.Game/Bullet.cs +++ b/OpenRa.Game/Bullet.cs @@ -59,6 +59,12 @@ namespace OpenRa.Game { Game.world.AddFrameEndTask(w => w.Remove(this)); Game.world.AddFrameEndTask(w => w.Add(new Explosion(Dest))); + + var maxSpread = GetMaximumSpread(); + var hitActors = Game.FindUnitsInCircle(Dest, GetMaximumSpread()); + + foreach (var victim in hitActors) + victim.InflictDamage(FiredBy, this, (int)GetDamageToInflict(victim)); } } @@ -69,6 +75,21 @@ namespace OpenRa.Game Src.ToFloat2(), Dest.ToFloat2(), (float)t / TotalTime()) - 0.5f * anim.Image.size); + } + + float GetMaximumSpread() + { + return (int)(Warhead.Spread * Math.Log(Weapon.Damage, 2)); + } + + float GetDamageToInflict(Actor target) + { + /* todo: some things can't be damaged AT ALL by certain weapons! */ + var distance = (target.CenterLocation - Dest).Length; + var rawDamage = Weapon.Damage * (float)Math.Exp(-distance / Warhead.Spread); + var multiplier = Warhead.EffectivenessAgainst(target.unitInfo.Armor); + + return rawDamage * multiplier; } } } diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 5f51d1f4b4..774edf2bc6 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -142,6 +142,12 @@ namespace OpenRa.Game return world.Actors .Where(x => x.Bounds.IntersectsWith(rect)); + } + + public static IEnumerable FindUnitsInCircle(float2 a, float r) + { + return FindUnits(a - new float2(r, r), a + new float2(r, r)) + .Where(x => (x.CenterLocation - a).LengthSquared < r * r); } public static IEnumerable SelectUnitsInBox(float2 a, float2 b) diff --git a/OpenRa.Game/GameRules/FieldLoader.cs b/OpenRa.Game/GameRules/FieldLoader.cs index 361d13999e..7529abe387 100755 --- a/OpenRa.Game/GameRules/FieldLoader.cs +++ b/OpenRa.Game/GameRules/FieldLoader.cs @@ -22,34 +22,34 @@ namespace OpenRa.Game.GameRules if( fieldType == typeof( int ) ) return int.Parse( x ); - else if( fieldType == typeof( float ) ) - return float.Parse( x ); + else if (fieldType == typeof(float)) + return float.Parse(x.Replace("%","")) * (x.Contains( '%' ) ? 0.01f : 1f); - else if( fieldType == typeof( string ) ) + else if (fieldType == typeof(string)) return x;//.ToLowerInvariant(); - else if( fieldType.IsEnum ) - return Enum.Parse( fieldType, x ); + else if (fieldType.IsEnum) + return Enum.Parse(fieldType, x); - else if( fieldType == typeof( bool ) ) - return ParseYesNo( x ); + else if (fieldType == typeof(bool)) + return ParseYesNo(x); - else if( fieldType.IsArray ) + else if (fieldType.IsArray) { - var parts = x.Split( new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries ); + var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - var ret = Array.CreateInstance( fieldType.GetElementType(), parts.Length ); - for( int i = 0 ; i < parts.Length ; i++ ) - ret.SetValue( GetValue( fieldType.GetElementType(), parts[ i ].Trim() ), i ); + var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length); + for (int i = 0; i < parts.Length; i++) + ret.SetValue(GetValue(fieldType.GetElementType(), parts[i].Trim()), i); return ret; } - else if( fieldType == typeof( int2 ) ) + else if (fieldType == typeof(int2)) { - var parts = x.Split( new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries ); - return new int2( int.Parse( parts[ 0 ] ), int.Parse( parts[ 1 ] ) ); + var parts = x.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + return new int2(int.Parse(parts[0]), int.Parse(parts[1])); } else - throw new InvalidOperationException( "FieldLoader: don't know how to load field of type " + fieldType.ToString() ); + throw new InvalidOperationException("FieldLoader: don't know how to load field of type " + fieldType.ToString()); } static bool ParseYesNo( string p ) diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs index 0527f6e510..31727d5d1a 100755 --- a/OpenRa.Game/GameRules/UnitInfo.cs +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -7,17 +7,17 @@ using IjwFramework.Types; namespace OpenRa.Game.GameRules { + public enum ArmorType + { + none = 0, + wood = 1, + light = 2, + heavy = 3, + concrete = 4, + } + public class UnitInfo { - public enum ArmorType - { - none = 0, - wood = 1, - light = 2, - heavy = 3, - concrete = 4, - } - public readonly string Name; public readonly string Description = ""; diff --git a/OpenRa.Game/GameRules/WarheadInfo.cs b/OpenRa.Game/GameRules/WarheadInfo.cs index b8d6f52bc7..6b783cadaf 100644 --- a/OpenRa.Game/GameRules/WarheadInfo.cs +++ b/OpenRa.Game/GameRules/WarheadInfo.cs @@ -10,11 +10,13 @@ namespace OpenRa.Game.GameRules class WarheadInfo { public readonly int Spread = 1; - public readonly string Verses = "100%,100%,100%,100%,100%"; + public readonly float[] Verses = { 1, 1, 1, 1, 1 }; public readonly bool Wall = false; public readonly bool Wood = false; public readonly bool Ore = false; public readonly int Explosion = 0; public readonly int InfDeath = 0; + + public float EffectivenessAgainst(ArmorType at) { return Verses[ (int)at ]; } } } diff --git a/OpenRa.Game/MainWindow.cs b/OpenRa.Game/MainWindow.cs index c8af2d9002..8a63e725e5 100755 --- a/OpenRa.Game/MainWindow.cs +++ b/OpenRa.Game/MainWindow.cs @@ -57,7 +57,7 @@ namespace OpenRa.Game Game.world.Add( new Actor( "mcv", new int2( 5, 5 ), Game.players[ 3 ]) ); Game.world.Add( new Actor( "mcv", new int2( 7, 5 ), Game.players[ 2 ] ) ); Game.world.Add( new Actor( "mcv", new int2( 9, 5 ), Game.players[ 0 ] ) ); - var jeep = new Actor( "jeep", new int2( 9, 7 ), Game.players[ 1 ] ); + var jeep = new Actor( "jeep", new int2( 9, 15 ), Game.players[ 1 ] ); Game.world.Add( jeep ); var tank = new Actor( "3tnk", new int2( 12, 7 ), Game.players[ 1 ] ); Game.world.Add( tank ); @@ -77,10 +77,7 @@ namespace OpenRa.Game while (Created && Visible) { Game.Tick(); - - // rude hack Game.viewport.cursor = Game.controller.ChooseCursor(); - Application.DoEvents(); } } diff --git a/OpenRa.Game/Player.cs b/OpenRa.Game/Player.cs index bd3357e48e..bd92e08ed4 100644 --- a/OpenRa.Game/Player.cs +++ b/OpenRa.Game/Player.cs @@ -4,6 +4,7 @@ namespace OpenRa.Game class Player { public int Palette; + public int Kills; public string PlayerName; public TechTree.TechTree TechTree = new OpenRa.TechTree.TechTree();