diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 0c80a90406..ed795c77cf 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -30,7 +30,9 @@ namespace OpenRa.Game public Game(string mapName, Renderer renderer, int2 clientSize) { - for (int i = 0; i < 8; i++) + Rules.LoadRules(); + + for( int i = 0 ; i < 8 ; i++ ) players.Add(i, new Player(i, string.Format("Multi{0}", i), OpenRa.TechTree.Race.Soviet)); map = new Map(new IniFile(FileSystem.Open(mapName))); diff --git a/OpenRa.Game/GameRules/Rules.cs b/OpenRa.Game/GameRules/Rules.cs new file mode 100755 index 0000000000..e0b3aef668 --- /dev/null +++ b/OpenRa.Game/GameRules/Rules.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRa.FileFormats; +using OpenRa.Game.GameRules; + +namespace OpenRa.Game +{ + static class Rules + { + public static UnitInfo UnitInfo; + + // TODO: load rules from the map, where appropriate. + public static void LoadRules() + { + UnitInfo = new UnitInfo( new IniFile( FileSystem.Open( "rules.ini" ) ) ); + } + } +} diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs new file mode 100755 index 0000000000..7a4c2ded55 --- /dev/null +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using OpenRa.FileFormats; +using OpenRa.Game.Graphics; + +namespace OpenRa.Game.GameRules +{ + public class UnitInfo + { + static bool ParseYesNo( string p ) + { + p = p.ToLowerInvariant(); + if( p == "yes" ) return true; + if( p == "true" ) return true; + if( p == "no" ) return false; + if( p == "false" ) return false; + throw new InvalidOperationException(); + } + + readonly Dictionary unitInfos = new Dictionary(); + + public UnitInfo( IniFile rules ) + { + foreach( var s in Util.ReadAllLines( FileSystem.Open( "buildings.txt" ) ) ) + { + var unitName = s.Split( ',' )[ 0 ]; + unitInfos.Add( unitName.ToLowerInvariant(), new BuildingInfo( unitName, rules.GetSection( unitName ) ) ); + } + foreach( var s in Util.ReadAllLines( FileSystem.Open( "infantry.txt" ) ) ) + { + var unitName = s.Split( ',' )[ 0 ]; + unitInfos.Add( unitName.ToLowerInvariant(), new InfantryInfo( unitName, rules.GetSection( unitName ) ) ); + } + foreach( var s in Util.ReadAllLines( FileSystem.Open( "vehicles.txt" ) ) ) + { + var unitName = s.Split( ',' )[ 0 ]; + unitInfos.Add( unitName.ToLowerInvariant(), new VehicleInfo( unitName, rules.GetSection( unitName ) ) ); + } + } + + public BaseInfo Get( string unitName ) + { + return unitInfos[ unitName.ToLowerInvariant() ]; + } + + public enum ArmorType + { + none = 0, + wood = 1, + light = 2, + heavy = 3, + concrete = 4, + } + + public class BaseInfo + { + public readonly string Name; + + public readonly int Ammo = -1; + public readonly ArmorType Armor = ArmorType.none; + public readonly bool DoubleOwned = false; + public readonly bool Cloakable = false; + public readonly int Cost = 0; + public readonly bool Crewed = false; + public readonly bool Explodes = false; + public readonly int GuardRange = -1; // -1 = use weapon's range + public readonly string Image = null; // sprite-set to use when rendering + public readonly bool Invisible = false; + public readonly string Owner = "allies,soviet"; // TODO: make this an enum + public readonly int Points = 0; + public readonly string Prerequisite = ""; + public readonly string Primary = null; + public readonly string Secondary = null; + public readonly int ROT = 0; + public readonly int Reload = 0; + public readonly bool SelfHealing = false; + public readonly bool Sensors = false; // no idea what this does + public readonly int Sight = 1; + public readonly int Strength = 1; + public readonly int TechLevel = -1; + + public BaseInfo( string name, IniSection ini ) + { + Name = name; + + foreach( var x in ini ) + { + var field = this.GetType().GetField( x.Key ); + if( field.FieldType == typeof( int ) ) + field.SetValue( this, int.Parse( x.Value ) ); + + else if( field.FieldType == typeof( float ) ) + field.SetValue( this, float.Parse( x.Value ) ); + + else if( field.FieldType == typeof( string ) ) + field.SetValue( this, x.Value ); + + else if( field.FieldType == typeof( ArmorType ) ) + field.SetValue( this, Enum.Parse( typeof( ArmorType ), x.Value ) ); + + else if( field.FieldType == typeof( bool ) ) + field.SetValue( this, ParseYesNo( x.Value ) ); + + else + do { } while( false ); + } + } + } + + public class MobileInfo : BaseInfo + { + public readonly int Passengers = 0; + public readonly int Speed = 0; + + public MobileInfo( string name, IniSection ini ) + : base( name, ini ) + { + } + } + + public class InfantryInfo : MobileInfo + { + + public readonly bool C4 = false; + public readonly bool FraidyCat = false; + public readonly bool Infiltrate = false; + public readonly bool IsCanine = false; + + public InfantryInfo( string name, IniSection ini ) + : base( name, ini ) + { + } + } + + public class VehicleInfo : MobileInfo + { + public readonly bool Crushable = false; + public readonly bool Tracked = false; + public readonly bool NoMovingFire = false; + + public VehicleInfo( string name, IniSection ini ) + : base( name, ini ) + { + } + } + + public class BuildingInfo : BaseInfo + { + public readonly bool BaseNormal = true; + public readonly int Adjacent = 1; + public readonly bool Bib = false; + public readonly bool Capturable = false; + public readonly int Power = 0; + public readonly bool Powered = false; + public readonly bool Repairable = true; + public readonly int Storage = 0; + public readonly bool Unsellable = false; + public readonly bool WaterBound = false; + + public BuildingInfo( string name, IniSection ini ) + : base( name, ini ) + { + } + } + + /* + * Example: HARV + * Unit (can move, etc) + * PlayerOwned + * Selectable + * CanHarvest + * + * Example: PROC (refinery) + * Building (can't move) + * AcceptsOre (harvester returns here) + * + * Example: 3TNK (soviet heavy tank) + * Unit + * Turret (can aim in different direction to movement) + * + * Example: GUN (allied base defense turret) + * Building + * Turret + * + * some traits can be determined by fields in rules.ini + * and some can't : + * Gap-generator's ability + * Nuke, chrone, curtain, (super-weapons) + * Aircraft-landable + * Selectable (bomber/spyplane can't be selected, for example) + * AppearsFriendly (spy) + * IsInfantry (can be build in TENT/BARR, 5-in-a-square) + * IsVehicle + * Squashable (sandbags, infantry) + * Special rendering for war factory + */ + } +} diff --git a/OpenRa.Game/Graphics/Util.cs b/OpenRa.Game/Graphics/Util.cs index 2d76597447..4f232ec79e 100644 --- a/OpenRa.Game/Graphics/Util.cs +++ b/OpenRa.Game/Graphics/Util.cs @@ -23,8 +23,12 @@ namespace OpenRa.Game.Graphics { List result = new List(); using (StreamReader reader = new StreamReader(s)) - while (!reader.EndOfStream) - result.Add(reader.ReadLine()); + while(!reader.EndOfStream) + { + var line = reader.ReadLine(); + if( !string.IsNullOrEmpty( line ) && line[0] != '#' ) + result.Add( line ); + } return result.ToArray(); } diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index 62bc5de560..15c3770857 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -72,6 +72,8 @@ + + @@ -83,7 +85,6 @@ - @@ -115,7 +116,6 @@ - diff --git a/OpenRa.Game/Rules.cs b/OpenRa.Game/Rules.cs deleted file mode 100644 index 6699cc5e91..0000000000 --- a/OpenRa.Game/Rules.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using OpenRa.FileFormats; -using OpenRa.Game.Graphics; - -namespace OpenRa.Game -{ - static class Rules - { - static readonly Dictionary unitInfos = new Dictionary(); - - static Rules() - { - var rulesIni = SharedResources.Rules; - - foreach (string line in Util.ReadAllLines(FileSystem.Open("units.txt"))) - { - string unit = line.Substring( 0, line.IndexOf( ',' ) ).ToUpperInvariant(); - IniSection section = rulesIni.GetSection( unit ); - if (section == null) - { - Log.Write("rules.ini doesnt contain entry for unit \"{0}\"", unit); - continue; - } - unitInfos.Add( unit, new UnitInfo( unit, section ) ); - } - } - - public static UnitInfo UnitInfo( string name ) - { - return unitInfos[ name.ToUpperInvariant() ]; - } - } -} diff --git a/OpenRa.Game/Sidebar.cs b/OpenRa.Game/Sidebar.cs index ba5f843f30..7832494c4a 100644 --- a/OpenRa.Game/Sidebar.cs +++ b/OpenRa.Game/Sidebar.cs @@ -45,7 +45,8 @@ namespace OpenRa.Game clockRenderer = new SpriteRenderer(renderer, true); LoadSprites("buildings.txt"); - LoadSprites("units.txt"); + LoadSprites("vehicles.txt"); + LoadSprites("infantry.txt"); foreach (string s in groups) { @@ -159,6 +160,16 @@ namespace OpenRa.Game } } } + else if( mi.Button == MouseButtons.Right && mi.Event == MouseInputEvent.Down ) + { + var point = new float2(mi.Location.X, mi.Location.Y); + var item = GetItem(point); + if( item != null ) + { + string group = itemGroups[ item.techTreeItem.tag ]; + selectedItems[ group ] = null; + } + } } } diff --git a/OpenRa.Game/TechTree/Item.cs b/OpenRa.Game/TechTree/Item.cs index 30c31799b8..77c139fb12 100644 --- a/OpenRa.Game/TechTree/Item.cs +++ b/OpenRa.Game/TechTree/Item.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using OpenRa.FileFormats; +using OpenRa.Game.GameRules; namespace OpenRa.TechTree { @@ -10,31 +11,26 @@ namespace OpenRa.TechTree public bool IsStructure { get { return isStructure; } } - public Item(string tag, string friendlyName, IniSection section, bool isStructure) + public Item(string tag, string friendlyName, UnitInfo.BaseInfo unitInfo, bool isStructure) { this.tag = tag; this.friendlyName = friendlyName; this.isStructure = isStructure; - owner = ParseOwner(section); - techLevel = ParseTechLevel(section); - Tuple pre = ParsePrerequisites(section, tag); + owner = ParseOwner(unitInfo.Owner, unitInfo.DoubleOwned); + techLevel = unitInfo.TechLevel; + Tuple pre = ParsePrerequisites(unitInfo.Prerequisite, tag); alliedPrerequisites = pre.a; sovietPrerequisites = pre.b; } - static int ParseTechLevel(IniSection section) + static Race ParseOwner(string owners, bool doubleOwned) { - return int.Parse(section.GetValue("TechLevel", "-1")); - } - - static Race ParseOwner(IniSection section) - { - if (section.GetValue("DoubleOwned", "No") == "Yes") + if (doubleOwned) return Race.Allies | Race.Soviet; Race race = Race.None; - string[] frags = section.GetValue("Owner", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + string[] frags = owners.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string s in frags) race |= (Race)Enum.Parse(typeof(Race), s, true); @@ -42,9 +38,9 @@ namespace OpenRa.TechTree return race; } - static Tuple ParsePrerequisites(IniSection section, string tag) + static Tuple ParsePrerequisites(string prerequisites, string tag) { - List allied = new List(section.GetValue("Prerequisite", "").ToUpper().Split( + List allied = new List(prerequisites.ToUpper().Split( new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)); List soviet = new List(allied); diff --git a/OpenRa.Game/TechTree/TechTree.cs b/OpenRa.Game/TechTree/TechTree.cs index 36ea778f44..756f5003f3 100644 --- a/OpenRa.Game/TechTree/TechTree.cs +++ b/OpenRa.Game/TechTree/TechTree.cs @@ -55,14 +55,15 @@ namespace OpenRa.TechTree void LoadRules() { - IEnumerable> definitions = Concat( + IEnumerable> definitions = Concat( Concat( Lines("buildings.txt", true), - Lines("units.txt", false)); + Lines("vehicles.txt", false) ), + Lines("infantry.txt", false) ); var rules = SharedResources.Rules; foreach (Tuple p in definitions) - objects.Add(p.a, new Item(p.a, p.b, rules.GetSection(p.a), p.c)); + objects.Add(p.a, new Item(p.a, p.b, Rules.UnitInfo.Get(p.a), p.c)); } public bool Build(string key, bool force) @@ -102,7 +103,8 @@ namespace OpenRa.TechTree changed = true; } - if (changed) BuildableItemsChanged(); + //if (changed) + BuildableItemsChanged(); } public IEnumerable BuildableItems diff --git a/OpenRa.Game/Unit.cs b/OpenRa.Game/Unit.cs index f0396cb56b..276a2bf27c 100644 --- a/OpenRa.Game/Unit.cs +++ b/OpenRa.Game/Unit.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; - -using OpenRa.Game.Graphics; using IjwFramework.Types; +using OpenRa.Game.GameRules; +using OpenRa.Game.Graphics; namespace OpenRa.Game { @@ -20,7 +20,7 @@ namespace OpenRa.Game public int moveFraction, moveFractionTotal; readonly float2 renderOffset; - public readonly UnitInfo unitInfo; + public readonly UnitInfo.MobileInfo unitInfo; public Unit( string name, int2 cell, Player owner, Game game ) : base( game, name, cell ) @@ -28,7 +28,7 @@ namespace OpenRa.Game fromCell = toCell = cell; this.owner = owner; - this.unitInfo = Rules.UnitInfo( name ); + this.unitInfo = (UnitInfo.MobileInfo)Rules.UnitInfo.Get( name ); animation.PlayFetchIndex( "idle", () => facing ); renderOffset = animation.Center; @@ -90,7 +90,11 @@ namespace OpenRa.Game bool SupportsMission( SupportedMissions mission ) { - return mission == ( unitInfo.supportedMissions & mission ); + if( mission == SupportedMissions.Deploy ) + return this.unitInfo.Name == "MCV"; + if( mission == SupportedMissions.Harvest ) + return this.unitInfo.Name == "HARV"; + return false; } public Order Order( Game game, int2 xy ) diff --git a/OpenRa.Game/UnitInfo.cs b/OpenRa.Game/UnitInfo.cs deleted file mode 100644 index a08271a3e4..0000000000 --- a/OpenRa.Game/UnitInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using OpenRa.FileFormats; - -namespace OpenRa.Game -{ - class UnitInfo - { - public readonly int Speed; - public readonly SupportedMissions supportedMissions; - - public UnitInfo(string unitName, IniSection ini) - { - Speed = int.Parse(ini.GetValue("Speed", "0")); - - supportedMissions = SupportedMissions.Stop; - if (unitName == "MCV") - supportedMissions |= SupportedMissions.Deploy; - if (unitName == "HARV") - supportedMissions |= SupportedMissions.Harvest; - } - } -} diff --git a/infantry.txt b/infantry.txt new file mode 100755 index 0000000000..6790a23918 --- /dev/null +++ b/infantry.txt @@ -0,0 +1,17 @@ +# +# The infantry that we (currently) support. +# + +DOG,Attack Dog,infantry +E1,Rifle Infantry,infantry +E2,Grenadier,infantry +E3,Rocket Soldier,infantry +E4,Flamethrower,infantry +E6,Engineer,infantry +SPY,Spy,infantry +THF,Thief,infantry +E7,Tanya,infantry +MEDI,Medic,infantry + +# TODO: civilians, special (campaign) units + diff --git a/units.txt b/vehicles.txt old mode 100644 new mode 100755 similarity index 68% rename from units.txt rename to vehicles.txt index 9db8fb027e..53e51ee45e --- a/units.txt +++ b/vehicles.txt @@ -1,3 +1,7 @@ +# +# The vehicles that we (currently) support. +# + V2RL,V2 Rocket,vehicle 1TNK,Light Tank,vehicle 3TNK,Heavy Tank,vehicle @@ -11,23 +15,19 @@ MCV,Mobile Construction Vehicle,vehicle JEEP,Ranger,vehicle APC,Armored Personnel Carrier,vehicle MNLY,Minelayer,vehicle + +# TODO: move ships and planes into their own files + SS,Submarine,boat DD,Destroyer,boat CA,Cruiser,boat LST,Transport,boat PT,Gunboat,boat -DOG,Attack Dog,infantry -E1,Rifle Infantry,infantry -E2,Grenadier,infantry -E3,Rocket Soldier,infantry -E4,Flamethrower,infantry -E6,Engineer,infantry -SPY,Spy,infantry -THF,Thief,infantry -E7,Tanya,infantry -MEDI,Medic,infantry + MIG,Mig Attack Plane,plane YAK,Yak Attack Plane,plane TRAN,Transport Helicopter,plane HELI,Longbow,plane HIND,Hind,plane +# TODO: U2 (spyplane), BADR (paratrooper/paradrop plane) +