From b0906e1836ef499ac9c2adec547724ef82520a5f Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 28 May 2017 21:04:58 +0000 Subject: [PATCH] Add a bot type identifier. --- OpenRA.Game/Player.cs | 5 +++-- OpenRA.Game/Traits/TraitsInterfaces.cs | 7 ++++++- OpenRA.Mods.Common/AI/DummyAI.cs | 8 +++++++- OpenRA.Mods.Common/AI/HackyAI.cs | 12 ++++++++--- .../ServerTraits/LobbyCommands.cs | 20 +++++++++++++------ .../UtilityCommands/UpgradeRules.cs | 13 ++++++++++++ .../Widgets/Logic/Lobby/LobbyLogic.cs | 6 +++--- .../Widgets/Logic/Lobby/LobbyUtils.cs | 9 ++++----- mods/cnc/rules/ai.yaml | 3 +++ mods/d2k/rules/ai.yaml | 3 +++ mods/ra/maps/fort-lonestar/rules.yaml | 1 + mods/ra/rules/ai.yaml | 4 ++++ mods/ts/rules/ai.yaml | 1 + 13 files changed, 71 insertions(+), 21 deletions(-) diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs index 4df143e0b8..b54c54f542 100644 --- a/OpenRA.Game/Player.cs +++ b/OpenRA.Game/Player.cs @@ -106,8 +106,9 @@ namespace OpenRA Color = client.Color; if (client.Bot != null) { + var botInfo = world.Map.Rules.Actors["player"].TraitInfos().First(b => b.Type == client.Bot); var botsOfSameType = world.LobbyInfo.Clients.Where(c => c.Bot == client.Bot).ToArray(); - PlayerName = botsOfSameType.Length == 1 ? client.Bot : "{0} {1}".F(client.Bot, botsOfSameType.IndexOf(client) + 1); + PlayerName = botsOfSameType.Length == 1 ? botInfo.Name : "{0} {1}".F(botInfo.Name, botsOfSameType.IndexOf(client) + 1); } else PlayerName = client.Name; @@ -139,7 +140,7 @@ namespace OpenRA IsBot = BotType != null; if (IsBot && Game.IsHost) { - var logic = PlayerActor.TraitsImplementing().FirstOrDefault(b => b.Info.Name == BotType); + var logic = PlayerActor.TraitsImplementing().FirstOrDefault(b => b.Info.Type == BotType); if (logic == null) Log.Write("debug", "Invalid bot type: {0}", BotType); else diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index bb5a4efff4..234c49bbcf 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -337,7 +337,12 @@ namespace OpenRA.Traits public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); } public interface ICreatePlayers { void CreatePlayers(World w); } - public interface IBotInfo : ITraitInfoInterface { string Name { get; } } + public interface IBotInfo : ITraitInfoInterface + { + string Type { get; } + string Name { get; } + } + public interface IBot { void Activate(Player p); diff --git a/OpenRA.Mods.Common/AI/DummyAI.cs b/OpenRA.Mods.Common/AI/DummyAI.cs index e64c81d589..e04630ce44 100644 --- a/OpenRA.Mods.Common/AI/DummyAI.cs +++ b/OpenRA.Mods.Common/AI/DummyAI.cs @@ -15,9 +15,15 @@ namespace OpenRA.Mods.Common.AI { public sealed class DummyAIInfo : ITraitInfo, IBotInfo { - [Desc("Ingame name this bot uses.")] + [Desc("Human-readable name this bot uses.")] public readonly string Name = "Unnamed Bot"; + [FieldLoader.Require] + [Desc("Internal id for this bot.")] + public readonly string Type = null; + + string IBotInfo.Type { get { return Type; } } + string IBotInfo.Name { get { return Name; } } public object Create(ActorInitializer init) { return new DummyAI(this); } diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 105ae23f62..380e780ae4 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -58,7 +58,11 @@ namespace OpenRA.Mods.Common.AI public readonly HashSet Silo = new HashSet(); } - [Desc("Ingame name this bot uses.")] + [FieldLoader.Require] + [Desc("Internal id for this bot.")] + public readonly string Type = null; + + [Desc("Human-readable name this bot uses.")] public readonly string Name = "Unnamed Bot"; [Desc("Minimum number of units AI must have before attacking.")] @@ -162,8 +166,6 @@ namespace OpenRA.Mods.Common.AI [Desc("Should the AI repair its buildings if damaged?")] public readonly bool ShouldRepairBuildings = true; - string IBotInfo.Name { get { return Name; } } - [Desc("What units to the AI should build.", "What % of the total army must be this type of unit.")] public readonly Dictionary UnitsToBuild = null; @@ -233,6 +235,10 @@ namespace OpenRA.Mods.Common.AI return ret; } + string IBotInfo.Type { get { return Type; } } + + string IBotInfo.Name { get { return Name; } } + public object Create(ActorInitializer init) { return new HackyAI(this, init); } } diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs index 016258ab58..900d913d8f 100644 --- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs +++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs @@ -280,8 +280,6 @@ namespace OpenRA.Mods.Common.Server return false; } - var botType = parts.Skip(2).JoinWith(" "); - // Invalid slot if (bot != null && bot.Bot == null) { @@ -289,6 +287,16 @@ namespace OpenRA.Mods.Common.Server return true; } + var botType = parts[2]; + var botInfo = server.Map.Rules.Actors["player"].TraitInfos() + .FirstOrDefault(b => b.Type == botType); + + if (botInfo == null) + { + server.SendOrderTo(conn, "Message", "Invalid bot type."); + return true; + } + slot.Closed = false; if (bot == null) { @@ -296,7 +304,7 @@ namespace OpenRA.Mods.Common.Server bot = new Session.Client() { Index = server.ChooseFreePlayerIndex(), - Name = botType, + Name = botInfo.Name, Bot = botType, Slot = parts[0], Faction = "Random", @@ -319,7 +327,7 @@ namespace OpenRA.Mods.Common.Server else { // Change the type of the existing bot - bot.Name = botType; + bot.Name = botInfo.Name; bot.Bot = botType; } @@ -366,7 +374,7 @@ namespace OpenRA.Mods.Common.Server // - Players who now lack a slot are made observers // - Bots who now lack a slot are dropped // - Bots who are not defined in the map rules are dropped - var botNames = server.Map.Rules.Actors["player"].TraitInfos().Select(t => t.Name); + var botTypes = server.Map.Rules.Actors["player"].TraitInfos().Select(t => t.Type); var slots = server.LobbyInfo.Slots.Keys.ToArray(); var i = 0; foreach (var os in oldSlots) @@ -380,7 +388,7 @@ namespace OpenRA.Mods.Common.Server if (c.Slot != null) { // Remove Bot from slot if slot forbids bots - if (c.Bot != null && (!server.Map.Players.Players[c.Slot].AllowBots || !botNames.Contains(c.Bot))) + if (c.Bot != null && (!server.Map.Players.Players[c.Slot].AllowBots || !botTypes.Contains(c.Bot))) server.LobbyInfo.Clients.Remove(c); S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]); } diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 85ba225631..20e94c11ae 100644 --- a/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -808,6 +808,19 @@ namespace OpenRA.Mods.Common.UtilityCommands } } + // Bots must now specify an internal type as well as their display name + if (engineVersion < 20170707) + { + if (node.Key.StartsWith("HackyAI", StringComparison.Ordinal) || node.Key.StartsWith("DummyAI", StringComparison.Ordinal)) + { + var nameNode = node.Value.Nodes.FirstOrDefault(n => n.Key == "Name"); + + // Just duplicate the name to avoid incompatibility with maps + if (nameNode != null) + node.Value.Nodes.Add(new MiniYamlNode("Type", nameNode.Value.Value)); + } + } + UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index c2bccd7d6e..772cc54b70 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -199,7 +199,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic slotsButton.OnMouseDown = _ => { - var botNames = map.Rules.Actors["player"].TraitInfos().Select(t => t.Name); + var botTypes = map.Rules.Actors["player"].TraitInfos().Select(t => t.Type); var options = new Dictionary>(); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); @@ -215,7 +215,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic { foreach (var slot in orderManager.LobbyInfo.Slots) { - var bot = botNames.Random(Game.CosmeticRandom); + var bot = botTypes.Random(Game.CosmeticRandom); var c = orderManager.LobbyInfo.ClientInSlot(slot.Key); if (slot.Value.AllowBots == true && (c == null || c.Bot != null)) orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot.Key, botController.Index, bot))); @@ -621,7 +621,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (addBotOnMapLoad) { var slot = orderManager.LobbyInfo.FirstEmptyBotSlot(); - var bot = currentMap.Rules.Actors["player"].TraitInfos().Select(t => t.Name).FirstOrDefault(); + var bot = currentMap.Rules.Actors["player"].TraitInfos().Select(t => t.Type).FirstOrDefault(); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); if (slot != null && bot != null) orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot, botController.Index, bot))); diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs index 660c28141d..99969cb3ed 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs @@ -50,13 +50,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic var bots = new List(); if (slot.AllowBots) { - foreach (var b in map.Rules.Actors["player"].TraitInfos().Select(t => t.Name)) + foreach (var b in map.Rules.Actors["player"].TraitInfos()) { - var bot = b; var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); - bots.Add(new SlotDropDownOption(bot, - "slot_bot {0} {1} {2}".F(slot.PlayerReference, botController.Index, bot), - () => client != null && client.Bot == bot)); + bots.Add(new SlotDropDownOption(b.Name, + "slot_bot {0} {1} {2}".F(slot.PlayerReference, botController.Index, b.Type), + () => client != null && client.Bot == b.Type)); } } diff --git a/mods/cnc/rules/ai.yaml b/mods/cnc/rules/ai.yaml index 3683c1325b..573f22373b 100644 --- a/mods/cnc/rules/ai.yaml +++ b/mods/cnc/rules/ai.yaml @@ -1,6 +1,7 @@ Player: HackyAI@Cabal: Name: Cabal + Type: Cabal MinimumExcessPower: 60 BuildingCommonNames: ConstructionYard: fact @@ -122,6 +123,7 @@ Player: CheckRadius: 7c0 HackyAI@Watson: Name: Watson + Type: Watson MinimumExcessPower: 60 BuildingCommonNames: ConstructionYard: fact @@ -243,6 +245,7 @@ Player: CheckRadius: 7c0 HackyAI@HAL9001: Name: HAL 9001 + Type: HAL 9001 MinimumExcessPower: 60 BuildingCommonNames: ConstructionYard: fact diff --git a/mods/d2k/rules/ai.yaml b/mods/d2k/rules/ai.yaml index 8e44b9d9e4..f85d8e43f8 100644 --- a/mods/d2k/rules/ai.yaml +++ b/mods/d2k/rules/ai.yaml @@ -1,6 +1,7 @@ Player: HackyAI@Omnius: Name: Omnius + Type: Omnius MinimumExcessPower: 60 BuildingQueues: Building, Upgrade UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft @@ -119,6 +120,7 @@ Player: Against: Ally HackyAI@Vidius: Name: Vidious + Type: Vidious MinimumExcessPower: 60 BuildingQueues: Building, Upgrade UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft @@ -239,6 +241,7 @@ Player: Against: Ally HackyAI@Gladius: Name: Gladius + Type: Gladius MinimumExcessPower: 60 BuildingQueues: Building, Upgrade UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft diff --git a/mods/ra/maps/fort-lonestar/rules.yaml b/mods/ra/maps/fort-lonestar/rules.yaml index 994c6207f3..418a3c5621 100644 --- a/mods/ra/maps/fort-lonestar/rules.yaml +++ b/mods/ra/maps/fort-lonestar/rules.yaml @@ -100,6 +100,7 @@ Player: -HackyAI@TurtleAI: DummyAI@LonestarAI: Name: Lonestar AI + Type: Lonestar AI ^Infantry: Inherits@IC: ^IronCurtainable diff --git a/mods/ra/rules/ai.yaml b/mods/ra/rules/ai.yaml index a97cd63088..b089826a93 100644 --- a/mods/ra/rules/ai.yaml +++ b/mods/ra/rules/ai.yaml @@ -1,6 +1,7 @@ Player: HackyAI@RushAI: Name: Rush AI + Type: Rush AI MinimumExcessPower: 40 BuildingCommonNames: ConstructionYard: fact @@ -114,6 +115,7 @@ Player: CheckRadius: 7c0 HackyAI@NormalAI: Name: Normal AI + Type: Normal AI MinimumExcessPower: 60 BuildingCommonNames: ConstructionYard: fact @@ -245,6 +247,7 @@ Player: CheckRadius: 7c0 HackyAI@TurtleAI: Name: Turtle AI + Type: Turtle AI MinimumExcessPower: 100 BuildingCommonNames: ConstructionYard: fact @@ -376,6 +379,7 @@ Player: CheckRadius: 7c0 HackyAI@NavalAI: Name: Naval AI + Type: Naval AI BuildingCommonNames: ConstructionYard: fact Refinery: proc diff --git a/mods/ts/rules/ai.yaml b/mods/ts/rules/ai.yaml index 1fed6a75a6..3a7930fd4e 100644 --- a/mods/ts/rules/ai.yaml +++ b/mods/ts/rules/ai.yaml @@ -1,6 +1,7 @@ Player: HackyAI@TestAI: Name: Test AI + Type: Test AI MinimumExcessPower: 60 BuildingCommonNames: ConstructionYard: gacnst