diff --git a/OpenRA.Editor/Form1.cs b/OpenRA.Editor/Form1.cs index b74795f841..990dc4a993 100644 --- a/OpenRA.Editor/Form1.cs +++ b/OpenRA.Editor/Form1.cs @@ -152,9 +152,8 @@ namespace OpenRA.Editor actorPalette.Controls.Add(ibox); tt.SetToolTip(ibox, - "{0}:{1}".F( - info.Name, - info.Category)); + "{0}".F( + info.Name)); actorTemplates.Add(template); } diff --git a/OpenRA.Game/GameRules/ActorInfo.cs b/OpenRA.Game/GameRules/ActorInfo.cs index e8e0d9d74b..7e6fc5547a 100644 --- a/OpenRA.Game/GameRules/ActorInfo.cs +++ b/OpenRA.Game/GameRules/ActorInfo.cs @@ -19,7 +19,6 @@ namespace OpenRA public class ActorInfo { public readonly string Name; - public readonly string Category; public readonly TypeDictionary Traits = new TypeDictionary(); public ActorInfo( string name, MiniYaml node, Dictionary allUnits ) @@ -27,12 +26,8 @@ namespace OpenRA var mergedNode = MergeWithParent( node, allUnits ).Nodes; Name = name; - MiniYaml categoryNode; - if( mergedNode.TryGetValue( "Category", out categoryNode ) ) - Category = categoryNode.Value; - foreach( var t in mergedNode ) - if( t.Key != "Inherits" && t.Key != "Category" && !t.Key.StartsWith("-") ) + if( t.Key != "Inherits" && !t.Key.StartsWith("-") ) Traits.Add( LoadTraitInfo( t.Key.Split('@')[0], t.Value ) ); } diff --git a/OpenRA.Game/GameRules/Rules.cs b/OpenRA.Game/GameRules/Rules.cs index d95ae1cba1..9bba84ca86 100755 --- a/OpenRA.Game/GameRules/Rules.cs +++ b/OpenRA.Game/GameRules/Rules.cs @@ -50,10 +50,5 @@ namespace OpenRA var y = files.Select(a => MiniYaml.FromFile(a)).Aggregate(dict,MiniYaml.Merge); return y.ToDictionary(kv => kv.Key.ToLowerInvariant(), kv => f(kv, y)); } - - public static IEnumerable Categories() - { - return Info.Values.Select( x => x.Category ).Distinct().Where( g => g != null ).ToList(); - } } } diff --git a/OpenRA.Game/GameRules/TechTree.cs b/OpenRA.Game/GameRules/TechTree.cs index 8d5b274130..7e4aa1bd84 100755 --- a/OpenRA.Game/GameRules/TechTree.cs +++ b/OpenRA.Game/GameRules/TechTree.cs @@ -62,7 +62,7 @@ namespace OpenRA.GameRules if( playerBuildings[ p ].Count == 0 ) return false; - if( producesIndex[ info.Category ].All( x => playerBuildings[ x.Name ].Count == 0 ) ) + if( producesIndex[ bi.Queue ].All( x => playerBuildings[ x.Name ].Count == 0 ) ) return false; return true; @@ -83,17 +83,18 @@ namespace OpenRA.GameRules { return Rules.Info.Values .Where( x => x.Name[ 0 ] != '^' ) - .Where( x => categories.Contains( x.Category ) ) - .Where( x => x.Traits.Contains() ); + .Where( x => x.Traits.Contains() ) + .Where( x => categories.Contains(x.Traits.Get().Queue) ); } public IEnumerable UnitBuiltAt( ActorInfo info ) { - var builtAt = info.Traits.Get().BuiltAt; + var bi = info.Traits.Get(); + var builtAt = bi.BuiltAt; if( builtAt.Length != 0 ) return builtAt.Select( x => Rules.Info[ x.ToLowerInvariant() ] ); else - return producesIndex[ info.Category ]; + return producesIndex[ bi.Queue ]; } } } diff --git a/OpenRA.Game/Traits/Buildable.cs b/OpenRA.Game/Traits/Buildable.cs index 67793530e9..00fa356c05 100755 --- a/OpenRA.Game/Traits/Buildable.cs +++ b/OpenRA.Game/Traits/Buildable.cs @@ -33,6 +33,8 @@ namespace OpenRA.Traits public readonly string[] BuiltAt = { }; public readonly string[] Owner = { }; + + public readonly string Queue; // todo: UI fluff; doesn't belong here public readonly int BuildPaletteOrder = 9999; diff --git a/OpenRA.Game/Traits/Player/PlaceBuilding.cs b/OpenRA.Game/Traits/Player/PlaceBuilding.cs index 8512d1fa50..658791b127 100644 --- a/OpenRA.Game/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Game/Traits/Player/PlaceBuilding.cs @@ -85,8 +85,12 @@ namespace OpenRA.Traits // finds a construction yard (or equivalent) and runs its "build" animation. static void PlayBuildAnim( Actor self, ActorInfo unit ) { + var bi = unit.Traits.GetOrDefault(); + if (bi == null) + return; + var producers = self.World.Queries.OwnedBy[ self.Owner ].WithTrait() - .Where( x => x.Actor.Info.Traits.Get().Produces.Contains( unit.Category ) ) + .Where( x => x.Actor.Info.Traits.Get().Produces.Contains( bi.Queue ) ) .ToList(); var producer = producers.Where( x => x.Actor.IsPrimaryBuilding() ).Concat( producers ) .FirstOrDefault(); @@ -98,7 +102,11 @@ namespace OpenRA.Traits static int GetNumBuildables(Player p) { if (p != p.World.LocalPlayer) return 0; // this only matters for local players. - return Rules.TechTree.BuildableItems(p, Rules.Categories().ToArray()).Count(); + + // todo: this will simplify once queues know about what they can build + var queues = p.World.Queries.WithTraitMultiple().Where(a => a.Actor.Owner == p) + .Select(a => a.Trait.Info.Type).Distinct().ToArray(); + return Rules.TechTree.BuildableItems(p, queues).Count(); } } } diff --git a/OpenRA.Game/Traits/Player/ProductionQueue.cs b/OpenRA.Game/Traits/Player/ProductionQueue.cs index d9d5a3d4da..543ec8b0ea 100644 --- a/OpenRA.Game/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Game/Traits/Player/ProductionQueue.cs @@ -41,7 +41,7 @@ namespace OpenRA.Traits return Producing.ElementAtOrDefault(0); } - public IEnumerable AllItems() + public IEnumerable AllQueued() { return Producing; } @@ -64,13 +64,14 @@ namespace OpenRA.Traits case "StartProduction": { var unit = Rules.Info[order.TargetString]; - if (unit.Category != Info.Type) + var bi = unit.Traits.Get(); + if (bi.Queue != Info.Type) return; /* Not built by this queue */ var cost = unit.Traits.Contains() ? unit.Traits.Get().Cost : 0; var time = GetBuildTime(order.TargetString); - if (!Rules.TechTree.BuildableItems(order.Player, unit.Category).Contains(order.TargetString)) + if (!Rules.TechTree.BuildableItems(order.Player, bi.Queue).Contains(order.TargetString)) return; /* you can't build that!! */ bool hasPlayedSound = false; @@ -96,9 +97,6 @@ namespace OpenRA.Traits } case "PauseProduction": { - if (Rules.Info[ order.TargetString ].Category != Info.Type) - return; /* Not built by this queue */ - if( Producing.Count > 0 && Producing[0].Item == order.TargetString ) Producing[0].Paused = ( order.TargetLocation.X != 0 ); break; @@ -127,10 +125,8 @@ namespace OpenRA.Traits } void CancelProduction( string itemName ) - { - var category = Rules.Info[itemName].Category; - - if (category != Info.Type || Producing.Count == 0) + { + if (Producing.Count == 0) return; // Nothing to do here var lastIndex = Producing.FindLastIndex( a => a.Item == itemName ); diff --git a/OpenRA.Mods.RA/HackyAI.cs b/OpenRA.Mods.RA/HackyAI.cs index eeef76dd98..75cdee8fa8 100644 --- a/OpenRA.Mods.RA/HackyAI.cs +++ b/OpenRA.Mods.RA/HackyAI.cs @@ -182,7 +182,7 @@ namespace OpenRA.Mods.RA //don't select harvesters. var newUnits = self.World.Queries.OwnedBy[p] - .Where(a => ((a.Info.Category == "Infantry" || a.Info.Category == "Vehicle") + .Where(a => (a.Info.Traits.Contains() && a.Info != Rules.Info["harv"] && !activeUnits.Contains(a))).ToArray(); @@ -198,7 +198,11 @@ namespace OpenRA.Mods.RA if (unitsHangingAroundTheBase.Count > 5) { Game.Debug("Launch an attack."); + + // Todo: We have a trait which holds player/spawn info (MPStartLocationsInfo) - use it int2[] spawnPoints = Game.world.Map.SpawnPoints.ToArray(); + + // At the start of the game, all you can do is investigate each spawn point // until you learn where some other players are. // this sometimes sends an attack to the bot's own spawn point, @@ -214,8 +218,7 @@ namespace OpenRA.Mods.RA private void SetRallyPointsForNewProductionBuildings(Actor self) { var newProdBuildings = self.World.Queries.OwnedBy[p] - .Where(a => (a.Info.Category == "Building" - && a.TraitOrDefault() != null + .Where(a => (a.TraitOrDefault() != null && !activeProductionBuildings.Contains(a))).ToArray(); foreach (var a in newProdBuildings) diff --git a/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs b/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs index a7f4262b9d..e0df318145 100755 --- a/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs +++ b/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs @@ -58,9 +58,9 @@ namespace OpenRA.Mods.RA.Widgets ready = new Animation("pips"); ready.PlayRepeating("ready"); clock = new Animation("clock"); - + iconSprites = Rules.Info.Values - .Where(u => u.Traits.Contains()) + .Where(u => u.Traits.Contains() && u.Name[0] != '^' ) .ToDictionary( u => u.Name, u => SpriteSheetBuilder.LoadAllSprites(u.Traits.Get().Icon ?? (u.Name + "icon"))[0]); @@ -219,7 +219,7 @@ namespace OpenRA.Mods.RA.Widgets var drawPos = new float2(rect.Location); WidgetUtils.DrawSHP(iconSprites[item.Name], drawPos); - var firstOfThis = queue.AllItems().FirstOrDefault(a => a.Item == item.Name); + var firstOfThis = queue.AllQueued().FirstOrDefault(a => a.Item == item.Name); if (rect.Contains(Viewport.LastMousePos.ToPoint())) tooltipItem = item.Name; @@ -245,7 +245,7 @@ namespace OpenRA.Mods.RA.Widgets overlayBits.Add(Pair.New(ready.Image, overlayPos)); } - var repeats = queue.AllItems().Count(a => a.Item == item.Name); + var repeats = queue.AllQueued().Count(a => a.Item == item.Name); if (repeats > 1 || queue.CurrentItem() != firstOfThis) { var offset = -22; @@ -332,7 +332,7 @@ namespace OpenRA.Mods.RA.Widgets { var unit = Rules.Info[item]; var eva = world.WorldActor.Info.Traits.Get(); - var producing = CurrentQueue.AllItems().FirstOrDefault( a => a.Item == item ); + var producing = CurrentQueue.AllQueued().FirstOrDefault( a => a.Item == item ); if (isLmb) { @@ -411,7 +411,6 @@ namespace OpenRA.Mods.RA.Widgets if (rect.Contains(Viewport.LastMousePos.ToPoint())) { - //var text = CategoryNameRemaps.ContainsKey(groupName) ? CategoryNameRemaps[groupName] : groupName; var text = queue.Info.Type; var sz = Game.Renderer.BoldFont.Measure(text); WidgetUtils.DrawPanelPartial("dialog4", diff --git a/mods/cnc/defaults.yaml b/mods/cnc/defaults.yaml index ffd2bee16d..9a1dacdae9 100644 --- a/mods/cnc/defaults.yaml +++ b/mods/cnc/defaults.yaml @@ -1,5 +1,4 @@ ^Vehicle: - Category: Vehicle AppearsOnRadar: Mobile: Crushes: crate @@ -10,6 +9,8 @@ Voice: VehicleVoice Targetable: TargetTypes: Ground + Buildable: + Queue: Vehicle Repairable: Chronoshiftable: Passenger: @@ -23,7 +24,6 @@ Notification: unitlost.aud ^Tank: - Category: Vehicle AppearsOnRadar: Mobile: Crushes: wall, crate @@ -34,6 +34,8 @@ Voice: VehicleVoice Targetable: TargetTypes: Ground + Buildable: + Queue: Vehicle Repairable: Chronoshiftable: Passenger: @@ -47,7 +49,6 @@ Notification: unitlost.aud ^Helicopter: - Category: Plane AppearsOnRadar: Targetable: TargetTypes: Air @@ -57,6 +58,8 @@ RepairBuildings: hpad RearmBuildings: LandWhenIdle: false + Buildable: + Queue: Plane HiddenUnderFog: GainsExperience: GivesExperience: @@ -65,7 +68,6 @@ Notification: unitlost.aud ^Infantry: - Category: Infantry AppearsOnRadar: Health: Armor: none @@ -81,6 +83,8 @@ Voice: GenericVoice Targetable: TargetTypes: Ground + Buildable: + Queue: Infantry RenderInfantry: AutoTarget: Passenger: @@ -96,6 +100,7 @@ ^CivInfantry: Inherits: ^Infantry + -Buildable: AppearsOnRadar: Selectable: Voice: CivilianMaleVoice @@ -117,7 +122,6 @@ # NotifyAll: true ^Plane: - Category: Plane AppearsOnRadar: Selectable: Voice: GenericVoice @@ -131,7 +135,6 @@ Notification: unitlost.aud ^Ship: - Category: Ship AppearsOnRadar: Mobile: Crushes: crate @@ -149,7 +152,6 @@ Notification: unitlost.aud ^Building: - Category: Building AppearsOnRadar: Selectable: Priority: 3 @@ -163,6 +165,8 @@ SellSounds: cashturn.aud DamagedSound: xplos.aud DestroyedSound: xplobig4.aud + Buildable: + Queue: Building RenderBuilding: DeadBuildingState: EmitInfantryOnSell: @@ -180,6 +184,7 @@ ^CivBuilding: Inherits: ^Building + -Buildable: DeadBuildingState: Zombie: true Health: @@ -202,7 +207,6 @@ OverrideImage: v23 ^Wall: - Category: Building AppearsOnRadar: Building: Dimensions: 1,1 @@ -227,7 +231,6 @@ GivesExperience: ^Tree: - Category: Building Tooltip: Name: Tree RenderBuilding: @@ -239,7 +242,6 @@ RadarColorFromTerrain: Terrain: Tree ^Rock: - Category: Building Tooltip: Name: Rock RenderBuilding: @@ -252,7 +254,6 @@ Terrain: Tree ^Husk: - Category: Vehicle Health: HP: 140 Armor: Heavy @@ -264,7 +265,6 @@ Burns: ^Bridge: - Category: Building Tooltip: Name: Bridge Targetable: diff --git a/mods/cnc/structures.yaml b/mods/cnc/structures.yaml index 8667057581..ca1f176005 100644 --- a/mods/cnc/structures.yaml +++ b/mods/cnc/structures.yaml @@ -465,7 +465,6 @@ TMPL: MissileWeapon: atomic OBLI: - Category: Defense RequiresPower: Inherits: ^Building Valued: @@ -475,6 +474,7 @@ OBLI: Icon:obliicnh Description: Advanced base defense. Requires power\nto operate.\n Strong vs Tanks, Infantry\n Weak vs Aircraft Buildable: + Queue: Defense BuildPaletteOrder: 60 Prerequisites: tmpl Owner: nod @@ -499,7 +499,6 @@ OBLI: -EmitInfantryOnSell: CYCL: - Category: Defense Inherits: ^Wall Valued: Cost: 25 @@ -508,6 +507,7 @@ CYCL: Icon:cyclicnh Description: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. Buildable: + Queue: Defense BuildPaletteOrder: 10 Prerequisites: fact Owner: nod @@ -516,7 +516,6 @@ CYCL: Armor: none SBAG: - Category: Defense Inherits: ^Wall Valued: Cost: 25 @@ -525,6 +524,7 @@ SBAG: Icon:sbagicnh Description: Stops infantry and blocks enemy fire.\nCan be crushed by tanks. Buildable: + Queue: Defense BuildPaletteOrder: 20 Prerequisites: fact Owner: gdi @@ -533,7 +533,6 @@ SBAG: Armor: none BRIK: - Category: Defense Inherits: ^Wall Valued: Cost: 100 @@ -542,6 +541,7 @@ BRIK: Icon:brikicnh Description: Stop units and blocks enemy fire. Buildable: + Queue: Defense BuildPaletteOrder: 30 Prerequisites: fact Owner: gdi,nod @@ -552,7 +552,6 @@ BRIK: CrushClasses: heavywall GUN: - Category: Defense Inherits: ^Building Valued: Cost: 600 @@ -561,6 +560,7 @@ GUN: Icon: gunicnh Description: Anti-Armor base defense.\n Strong vs Tanks\n Weak vs Infantry, Aircraft Buildable: + Queue: Defense BuildPaletteOrder: 40 Prerequisites: hand Owner: nod @@ -583,7 +583,6 @@ GUN: RenderRangeCircle: SAM: - Category: Defense Inherits: ^Building Valued: Cost: 750 @@ -592,6 +591,7 @@ SAM: Icon: samicnh Description: Anti-Air base defense.\n Strong vs Aircraft\n Weak vs Infantry, Tanks Buildable: + Queue: Defense BuildPaletteOrder: 50 Prerequisites: hand Owner: nod @@ -616,7 +616,6 @@ SAM: RenderRangeCircle: GTWR: - Category: Defense Inherits: ^Building Valued: Cost: 500 @@ -625,6 +624,7 @@ GTWR: Icon: gtwricnh Description: Basic defensive structure.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft Buildable: + Queue: Defense BuildPaletteOrder: 50 Prerequisites: pyle Owner: gdi @@ -643,7 +643,6 @@ GTWR: RenderRangeCircle: ATWR: - Category: Defense Inherits: ^Building Valued: Cost: 1000 @@ -652,6 +651,7 @@ ATWR: Icon: atwricnh Description: Anti-armor defensive structure.\n Strong vs Light Vehicles, Tanks\n Weak vs Infantry Buildable: + Queue: Defense BuildPaletteOrder: 60 Prerequisites: hq Owner: gdi