diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 656ef59c17..1d6a0f8cbb 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -1,15 +1,14 @@ +using System; using System.Collections.Generic; -using OpenRa.FileFormats; -using OpenRa.Game.Graphics; -using OpenRa.TechTree; using System.Drawing; using System.Linq; -using IrrKlang; using IjwFramework.Collections; -using System; using IjwFramework.Types; -using OpenRa.Game.Traits; +using IrrKlang; +using OpenRa.FileFormats; using OpenRa.Game.GameRules; +using OpenRa.Game.Graphics; +using OpenRa.Game.Traits; namespace OpenRa.Game { @@ -44,7 +43,7 @@ namespace OpenRa.Game Rules.LoadRules(mapName); for (int i = 0; i < 8; i++) - players.Add(i, new Player(i, string.Format("Multi{0}", i), Race.Soviet)); + players.Add(i, new Player(i, string.Format("Multi{0}", i), Race.Allies)); localPlayerIndex = localPlayer; @@ -223,7 +222,7 @@ namespace OpenRa.Game public static void BuildUnit(Player player, string name) { - var producerTypes = Rules.UnitInfo[name].BuiltAt; + var producerTypes = Rules.TechTree.UnitBuiltAt( Rules.UnitInfo[ name ] ); var producer = world.Actors .FirstOrDefault(a => a.unitInfo != null && producerTypes.Contains(a.unitInfo.Name) && a.Owner == player); diff --git a/OpenRa.Game/GameRules/FieldLoader.cs b/OpenRa.Game/GameRules/FieldLoader.cs index f187edf9c7..97ef6d72b3 100755 --- a/OpenRa.Game/GameRules/FieldLoader.cs +++ b/OpenRa.Game/GameRules/FieldLoader.cs @@ -29,7 +29,7 @@ namespace OpenRa.Game.GameRules return x; else if (fieldType.IsEnum) - return Enum.Parse(fieldType, x); + return Enum.Parse(fieldType, x, true); else if (fieldType == typeof(bool)) return ParseYesNo(x); diff --git a/OpenRa.Game/GameRules/Rules.cs b/OpenRa.Game/GameRules/Rules.cs index a138cca4c3..0f7d38c7fe 100755 --- a/OpenRa.Game/GameRules/Rules.cs +++ b/OpenRa.Game/GameRules/Rules.cs @@ -16,6 +16,7 @@ namespace OpenRa.Game public static InfoLoader WeaponInfo; public static InfoLoader WarheadInfo; public static InfoLoader ProjectileInfo; + public static TechTree TechTree; public static void LoadRules( string mapFileName ) { @@ -52,6 +53,8 @@ namespace OpenRa.Game ProjectileInfo = new InfoLoader( Pair.New>("Projectile", _ => new ProjectileInfo())); + + TechTree = new TechTree(); } static void LoadCategories( params string[] types ) diff --git a/OpenRa.Game/GameRules/TechTree.cs b/OpenRa.Game/GameRules/TechTree.cs new file mode 100755 index 0000000000..b8c0810917 --- /dev/null +++ b/OpenRa.Game/GameRules/TechTree.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IjwFramework.Collections; + +namespace OpenRa.Game.GameRules +{ + class TechTree + { + readonly Cache> producesIndex = new Cache>( x => new List() ); + + public TechTree() + { + foreach( var b in Rules.Categories[ "Building" ] ) + { + var info = (UnitInfo.BuildingInfo)Rules.UnitInfo[ b ]; + foreach( var p in info.Produces ) + producesIndex[ p ].Add( info ); + } + } + + public Cache> GatherBuildings( Player player ) + { + var ret = new Cache>( x => new List() ); + foreach( var b in Game.world.Actors.Where( x => x.Owner == player && Rules.UnitCategory[ x.unitInfo.Name ] == "Building" ) ) + ret[ b.unitInfo.Name ].Add( b ); + return ret; + } + + public bool CanBuild( UnitInfo unit, Player player, Cache> playerBuildings ) + { + if( unit.TechLevel == -1 ) + return false; + + if( !unit.Owner.Any( x => x == player.Race ) ) + return false; + + foreach( var p in unit.Prerequisite ) + if( playerBuildings[ p ].Count == 0 ) + return false; + + if( producesIndex[ Rules.UnitCategory[ unit.Name ] ].All( x => playerBuildings[ x.Name ].Count == 0 ) ) + return false; + + return true; + } + + public IEnumerable BuildableItems( Player player, params string[] categories ) + { + var playerBuildings = GatherBuildings( player ); + foreach( var unit in categories.SelectMany( x => Rules.Categories[ x ] ).Select( x => Rules.UnitInfo[ x ] ) ) + if( CanBuild( unit, player, playerBuildings ) ) + yield return unit.Name; + } + + public IEnumerable UnitBuiltAt( UnitInfo info ) + { + if( info.BuiltAt.Length != 0 ) + return info.BuiltAt; + else + return producesIndex[ Rules.UnitCategory[ info.Name ] ].Select( x => x.Name ); + } + } +} diff --git a/OpenRa.Game/GameRules/UnitInfo.cs b/OpenRa.Game/GameRules/UnitInfo.cs index d427e09f20..ac944efdd2 100755 --- a/OpenRa.Game/GameRules/UnitInfo.cs +++ b/OpenRa.Game/GameRules/UnitInfo.cs @@ -33,7 +33,7 @@ namespace OpenRa.Game.GameRules 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" }; + public readonly Race[] Owner = { Race.Allies, Race.Soviet }; public readonly int Points = 0; public readonly string[] Prerequisite = { }; public readonly string Primary = null; @@ -81,6 +81,7 @@ namespace OpenRa.Game.GameRules { public readonly int2 Dimensions = new int2( 1, 1 ); public readonly string Footprint = "x"; + public readonly string[] Produces = { }; public readonly bool BaseNormal = true; public readonly int Adjacent = 1; diff --git a/OpenRa.Game/MainWindow.cs b/OpenRa.Game/MainWindow.cs index 8fe3360d2b..5ef3e4bc74 100755 --- a/OpenRa.Game/MainWindow.cs +++ b/OpenRa.Game/MainWindow.cs @@ -2,7 +2,6 @@ using System.Drawing; using System.Windows.Forms; using OpenRa.FileFormats; using OpenRa.Game.Graphics; -using OpenRa.TechTree; using System.Runtime.InteropServices; namespace OpenRa.Game diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index 76bf0fc33e..850c205523 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -74,6 +74,7 @@ + @@ -99,7 +100,6 @@ - @@ -124,7 +124,6 @@ - diff --git a/OpenRa.Game/Player.cs b/OpenRa.Game/Player.cs index 117635cd82..4bc1cac5b0 100644 --- a/OpenRa.Game/Player.cs +++ b/OpenRa.Game/Player.cs @@ -8,13 +8,13 @@ namespace OpenRa.Game public int Palette; public int Kills; public string PlayerName; - public TechTree.TechTree TechTree = new OpenRa.TechTree.TechTree(); + public Race Race; - public Player( int palette, string playerName, OpenRa.TechTree.Race race ) + public Player( int palette, string playerName, Race race ) { this.Palette = palette; this.PlayerName = playerName; - TechTree.CurrentRace = race; + this.Race = race; } public float GetSiloFullness() diff --git a/OpenRa.Game/Race.cs b/OpenRa.Game/Race.cs index 4a44acad28..fc0ae35dbe 100644 --- a/OpenRa.Game/Race.cs +++ b/OpenRa.Game/Race.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Text; -namespace OpenRa.TechTree +namespace OpenRa.Game { [Flags] public enum Race diff --git a/OpenRa.Game/Sidebar.cs b/OpenRa.Game/Sidebar.cs index fc8b6708b8..cd8b443d03 100644 --- a/OpenRa.Game/Sidebar.cs +++ b/OpenRa.Game/Sidebar.cs @@ -4,7 +4,6 @@ using System.Drawing; using System.Windows.Forms; using OpenRa.FileFormats; using OpenRa.Game.Graphics; -using OpenRa.TechTree; using System.Linq; namespace OpenRa.Game @@ -34,7 +33,6 @@ namespace OpenRa.Game public Sidebar( Renderer renderer, Player player ) { this.player = player; - this.player.TechTree.BuildableItemsChanged += PopulateItemList; region = GRegion.Create(Game.viewport, DockStyle.Right, 128, Paint, MouseHandler); Game.viewport.AddRegion( region ); spriteRenderer = new SpriteRenderer(renderer, false); @@ -68,11 +66,11 @@ namespace OpenRa.Game { if (item == null) return; - if (item.techTreeItem.IsStructure) + if (item.IsStructure) Game.controller.orderGenerator = new PlaceBuilding(player, - item.techTreeItem.tag.ToLowerInvariant()); + item.Tag.ToLowerInvariant()); else - Game.controller.AddOrder(Order.BuildUnit(player, item.techTreeItem.tag.ToLowerInvariant())); + Game.controller.AddOrder(Order.BuildUnit(player, item.Tag.ToLowerInvariant())); } void LoadSprites( string group ) @@ -107,17 +105,20 @@ namespace OpenRa.Game items.Clear(); - foreach (Item i in player.TechTree.BuildableItems) + foreach (var i in Rules.TechTree.BuildableItems(player, "Building")) { Sprite sprite; - if (!sprites.TryGetValue(i.tag, out sprite)) continue; + if (!sprites.TryGetValue(i, out sprite)) continue; + items.Add(new SidebarItem(sprite, i, true, buildPos)); + buildPos += spriteHeight; + } - items.Add(new SidebarItem(sprite, i, i.IsStructure ? buildPos : unitPos)); - - if (i.IsStructure) - buildPos += spriteHeight; - else - unitPos += spriteHeight; + foreach( var i in Rules.TechTree.BuildableItems( player, "Vehicle", "Infantry", "Ship", "Plane" ) ) + { + Sprite sprite; + if( !sprites.TryGetValue( i, out sprite ) ) continue; + items.Add( new SidebarItem( sprite, i, false, unitPos ) ); + unitPos += spriteHeight; } foreach( string g in groups ) player.CancelProduction( g ); @@ -125,11 +126,12 @@ namespace OpenRa.Game void Paint() { + PopulateItemList(); foreach( SidebarItem i in items ) { - var group = Rules.UnitCategory[ i.techTreeItem.tag ]; + var group = Rules.UnitCategory[ i.Tag ]; var producing = player.Producing( group ); - if( producing != null && producing.Item == i.techTreeItem.tag ) + if( producing != null && producing.Item == i.Tag ) { clockAnimations[ group ].Tick(); clockRenderer.DrawSprite( clockAnimations[ group ].Image, region.Location.ToFloat2() + i.location, 0 ); @@ -155,30 +157,22 @@ namespace OpenRa.Game void MouseHandler(MouseInput mi) { - if (mi.Button == MouseButtons.Left && mi.Event == MouseInputEvent.Down) + var point = mi.Location.ToFloat2(); + var item = GetItem( point ); + if( item == null ) + return; + + if( mi.Button == MouseButtons.Left && mi.Event == MouseInputEvent.Down ) { - var point = mi.Location.ToFloat2(); - var item = GetItem(point); - if (item != null) - { - string group = Rules.UnitCategory[ item.techTreeItem.tag ]; + string group = Rules.UnitCategory[ item.Tag ]; if (player.Producing(group) == null) { - player.BeginProduction( group, new ProductionItem( item.techTreeItem.tag, 25, 0 ) ); + player.BeginProduction( group, new ProductionItem( item.Tag, 25, 0 ) ); Build(item); } - } } else if( mi.Button == MouseButtons.Right && mi.Event == MouseInputEvent.Down ) - { - var point = mi.Location.ToFloat2(); - var item = GetItem(point); - if( item != null ) - { - string group = Rules.UnitCategory[ item.techTreeItem.tag ]; - player.CancelProduction( group ); - } - } + player.CancelProduction( Rules.UnitCategory[ item.Tag ] ); } } } diff --git a/OpenRa.Game/SidebarItem.cs b/OpenRa.Game/SidebarItem.cs index 9dd4e530bc..784a80df03 100644 --- a/OpenRa.Game/SidebarItem.cs +++ b/OpenRa.Game/SidebarItem.cs @@ -1,19 +1,20 @@ using OpenRa.Game.Graphics; -using OpenRa.TechTree; namespace OpenRa.Game { class SidebarItem { - public readonly Item techTreeItem; public readonly float2 location; + public readonly string Tag; + public readonly bool IsStructure; readonly Sprite sprite; - public SidebarItem(Sprite s, Item item, int y) + public SidebarItem(Sprite s, string tag, bool isStructure, int y) { - this.techTreeItem = item; this.sprite = s; - location = new float2(item.IsStructure ? 0 : 64, y); + this.Tag = tag; + this.IsStructure = isStructure; + location = new float2(isStructure ? 0 : 64, y); } public bool Clicked(float2 p) diff --git a/OpenRa.Game/TechTree/Item.cs b/OpenRa.Game/TechTree/Item.cs deleted file mode 100755 index d95f447ab4..0000000000 --- a/OpenRa.Game/TechTree/Item.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using OpenRa.FileFormats; -using OpenRa.Game.GameRules; -using IjwFramework.Types; -using OpenRa.Game; - -namespace OpenRa.TechTree -{ - public class Item - { - readonly bool isStructure; - - public bool IsStructure { get { return isStructure; } } - - public Item(string tag, UnitInfo unitInfo, bool isStructure) - { - this.tag = tag; - this.friendlyName = unitInfo.Description; - this.isStructure = isStructure; - - owner = ParseOwner(unitInfo.Owner, unitInfo.DoubleOwned); - techLevel = unitInfo.TechLevel; - var pre = ParsePrerequisites(unitInfo.Prerequisite, tag); - alliedPrerequisites = pre.a; - sovietPrerequisites = pre.b; - } - - static Race ParseOwner(string[] owners, bool doubleOwned) - { - if (doubleOwned) - return Race.Allies | Race.Soviet; - - Race race = Race.None; - - foreach (string s in owners) - race |= Enum.Parse(s); - - return race; - } - - static Tuple ParsePrerequisites(string[] prerequisites, string tag) - { - var allied = prerequisites.Select( x => x.ToLowerInvariant() ).ToList(); - var soviet = new List(allied); - - if (allied.Remove("stek")) allied.Add("atek"); - if (soviet.Remove("atek")) soviet.Add("stek"); - if (soviet.Remove("tent")) soviet.Add("barr"); - if (allied.Remove("barr")) allied.Add("tent"); - - if( Rules.UnitCategory[ tag ] == "Infantry" ) - { - if( !allied.Contains( "tent" ) ) allied.Add( "tent" ); - if( !soviet.Contains( "barr" ) ) soviet.Add( "barr" ); - } - - if (tag == "lst") - { - if (!soviet.Contains("spen")) soviet.Add("spen"); - if (!allied.Contains("syrd")) allied.Add("syrd"); - } - - return new Tuple( - allied.ToArray(), soviet.ToArray()); - } - - public readonly string tag, friendlyName; - readonly int techLevel; - readonly Race owner; - readonly string[] alliedPrerequisites, sovietPrerequisites; - - bool ShouldMakeBuildable(IEnumerable buildings, string[] racePrerequisites) - { - if (techLevel < 0) - return false; - - if (racePrerequisites.Length == 0) - return true; - - return racePrerequisites.Except(buildings).Count() == 0; - } - - bool ShouldMakeUnbuildable(IEnumerable buildings, string[] racePrerequisites) - { - if (racePrerequisites.Length == 0) - return false; - - return racePrerequisites.Except(buildings).Count() == racePrerequisites.Length; - } - - void CheckForBoth(IEnumerable buildings) - { - if (CanBuild && (ShouldMakeUnbuildable(buildings, alliedPrerequisites) - && ShouldMakeUnbuildable(buildings, sovietPrerequisites))) - CanBuild = false; - - else if (!CanBuild && (ShouldMakeBuildable(buildings, alliedPrerequisites) - || ShouldMakeBuildable(buildings, sovietPrerequisites))) - CanBuild = true; - } - - public void CheckPrerequisites(IEnumerable buildings, Race currentRace) - { - if (currentRace == Race.None || currentRace == (Race.Allies | Race.Soviet)) - CheckForBoth(buildings); - else - { - string[] racePrerequisites = (currentRace == Race.Allies) ? alliedPrerequisites : sovietPrerequisites; - - if ((CanBuild && ShouldMakeUnbuildable(buildings, racePrerequisites)) || !((owner & currentRace) == currentRace)) - CanBuild = false; - else if (!CanBuild && ShouldMakeBuildable(buildings, racePrerequisites)) - CanBuild = true; - } - } - - public bool CanBuild { get; private set; } - public string Tooltip { get { return string.Format("{0} ({1})\n{2}", friendlyName, tag, owner); } } - } -} diff --git a/OpenRa.Game/TechTree/TechTree.cs b/OpenRa.Game/TechTree/TechTree.cs deleted file mode 100644 index 4682c58f42..0000000000 --- a/OpenRa.Game/TechTree/TechTree.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using OpenRa.FileFormats; -using System.Linq; -using OpenRa.Game; - -namespace OpenRa.TechTree -{ - public class TechTree - { - Dictionary objects = new Dictionary(); - public ICollection built = new List(); - - Race currentRace = Race.None; - - public Race CurrentRace - { - get { return currentRace; } - set - { - currentRace = value; - CheckAll(); - } - } - - public TechTree() - { - LoadRules(); - CheckAll(); - } - - void LoadRules() - { - foreach( var unit in Rules.UnitInfo ) - objects.Add( unit.Key, new Item( unit.Key, unit.Value, Rules.UnitCategory[ unit.Key ] == "Building" ) ); - } - - public bool Build(string key, bool force) - { - if( string.IsNullOrEmpty( key ) ) return false; - key = key.ToLowerInvariant(); - Item b = objects[ key ]; - if (!force && !b.CanBuild) return false; - built.Add(key); - CheckAll(); - return true; - } - - public bool Build(string key) { return Build(key, false); } - - public bool Unbuild(string key) - { - key = key.ToLowerInvariant(); - Item b = objects[key]; - if (!built.Contains(key)) return false; - built.Remove(key); - CheckAll(); - return true; - } - - void CheckAll() - { - foreach (Item unit in objects.Values) - unit.CheckPrerequisites(built, currentRace); - - BuildableItemsChanged(); - } - - public IEnumerable BuildableItems { get { return objects.Values.Where(b => b.CanBuild); } } - public event Action BuildableItemsChanged = () => { }; - } -} diff --git a/OpenRa.Game/Traits/Building.cs b/OpenRa.Game/Traits/Building.cs index 541fa469f8..c0793b55e8 100644 --- a/OpenRa.Game/Traits/Building.cs +++ b/OpenRa.Game/Traits/Building.cs @@ -15,10 +15,8 @@ namespace OpenRa.Game.Traits public void Tick(Actor self) { if (first && self.Owner == Game.LocalPlayer) - { - self.Owner.TechTree.Build(self.unitInfo.Name, true); self.CenterLocation = Game.CellSize * (float2)self.Location + 0.5f * self.SelectedSize; - } + first = false; } } diff --git a/sequences.xml b/sequences.xml index c7445cd24d..bfc25aa657 100644 --- a/sequences.xml +++ b/sequences.xml @@ -1,4 +1,4 @@ - +