Add a bot type identifier.

This commit is contained in:
Paul Chote
2017-05-28 21:04:58 +00:00
parent e88754cd4a
commit b0906e1836
13 changed files with 71 additions and 21 deletions

View File

@@ -106,8 +106,9 @@ namespace OpenRA
Color = client.Color; Color = client.Color;
if (client.Bot != null) if (client.Bot != null)
{ {
var botInfo = world.Map.Rules.Actors["player"].TraitInfos<IBotInfo>().First(b => b.Type == client.Bot);
var botsOfSameType = world.LobbyInfo.Clients.Where(c => c.Bot == client.Bot).ToArray(); 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 else
PlayerName = client.Name; PlayerName = client.Name;
@@ -139,7 +140,7 @@ namespace OpenRA
IsBot = BotType != null; IsBot = BotType != null;
if (IsBot && Game.IsHost) if (IsBot && Game.IsHost)
{ {
var logic = PlayerActor.TraitsImplementing<IBot>().FirstOrDefault(b => b.Info.Name == BotType); var logic = PlayerActor.TraitsImplementing<IBot>().FirstOrDefault(b => b.Info.Type == BotType);
if (logic == null) if (logic == null)
Log.Write("debug", "Invalid bot type: {0}", BotType); Log.Write("debug", "Invalid bot type: {0}", BotType);
else else

View File

@@ -337,7 +337,12 @@ namespace OpenRA.Traits
public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); } public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); }
public interface ICreatePlayers { void CreatePlayers(World w); } 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 public interface IBot
{ {
void Activate(Player p); void Activate(Player p);

View File

@@ -15,9 +15,15 @@ namespace OpenRA.Mods.Common.AI
{ {
public sealed class DummyAIInfo : ITraitInfo, IBotInfo 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"; 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; } } string IBotInfo.Name { get { return Name; } }
public object Create(ActorInitializer init) { return new DummyAI(this); } public object Create(ActorInitializer init) { return new DummyAI(this); }

View File

@@ -58,7 +58,11 @@ namespace OpenRA.Mods.Common.AI
public readonly HashSet<string> Silo = new HashSet<string>(); public readonly HashSet<string> Silo = new HashSet<string>();
} }
[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"; public readonly string Name = "Unnamed Bot";
[Desc("Minimum number of units AI must have before attacking.")] [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?")] [Desc("Should the AI repair its buildings if damaged?")]
public readonly bool ShouldRepairBuildings = true; 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.")] [Desc("What units to the AI should build.", "What % of the total army must be this type of unit.")]
public readonly Dictionary<string, float> UnitsToBuild = null; public readonly Dictionary<string, float> UnitsToBuild = null;
@@ -233,6 +235,10 @@ namespace OpenRA.Mods.Common.AI
return ret; return ret;
} }
string IBotInfo.Type { get { return Type; } }
string IBotInfo.Name { get { return Name; } }
public object Create(ActorInitializer init) { return new HackyAI(this, init); } public object Create(ActorInitializer init) { return new HackyAI(this, init); }
} }

View File

@@ -280,8 +280,6 @@ namespace OpenRA.Mods.Common.Server
return false; return false;
} }
var botType = parts.Skip(2).JoinWith(" ");
// Invalid slot // Invalid slot
if (bot != null && bot.Bot == null) if (bot != null && bot.Bot == null)
{ {
@@ -289,6 +287,16 @@ namespace OpenRA.Mods.Common.Server
return true; return true;
} }
var botType = parts[2];
var botInfo = server.Map.Rules.Actors["player"].TraitInfos<IBotInfo>()
.FirstOrDefault(b => b.Type == botType);
if (botInfo == null)
{
server.SendOrderTo(conn, "Message", "Invalid bot type.");
return true;
}
slot.Closed = false; slot.Closed = false;
if (bot == null) if (bot == null)
{ {
@@ -296,7 +304,7 @@ namespace OpenRA.Mods.Common.Server
bot = new Session.Client() bot = new Session.Client()
{ {
Index = server.ChooseFreePlayerIndex(), Index = server.ChooseFreePlayerIndex(),
Name = botType, Name = botInfo.Name,
Bot = botType, Bot = botType,
Slot = parts[0], Slot = parts[0],
Faction = "Random", Faction = "Random",
@@ -319,7 +327,7 @@ namespace OpenRA.Mods.Common.Server
else else
{ {
// Change the type of the existing bot // Change the type of the existing bot
bot.Name = botType; bot.Name = botInfo.Name;
bot.Bot = botType; bot.Bot = botType;
} }
@@ -366,7 +374,7 @@ namespace OpenRA.Mods.Common.Server
// - Players who now lack a slot are made observers // - Players who now lack a slot are made observers
// - Bots who now lack a slot are dropped // - Bots who now lack a slot are dropped
// - Bots who are not defined in the map rules are dropped // - Bots who are not defined in the map rules are dropped
var botNames = server.Map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name); var botTypes = server.Map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Type);
var slots = server.LobbyInfo.Slots.Keys.ToArray(); var slots = server.LobbyInfo.Slots.Keys.ToArray();
var i = 0; var i = 0;
foreach (var os in oldSlots) foreach (var os in oldSlots)
@@ -380,7 +388,7 @@ namespace OpenRA.Mods.Common.Server
if (c.Slot != null) if (c.Slot != null)
{ {
// Remove Bot from slot if slot forbids bots // 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); server.LobbyInfo.Clients.Remove(c);
S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]); S.SyncClientToPlayerReference(c, server.Map.Players.Players[c.Slot]);
} }

View File

@@ -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); UpgradeActorRules(modData, engineVersion, ref node.Value.Nodes, node, depth + 1);
} }

View File

@@ -199,7 +199,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
slotsButton.OnMouseDown = _ => slotsButton.OnMouseDown = _ =>
{ {
var botNames = map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name); var botTypes = map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Type);
var options = new Dictionary<string, IEnumerable<DropDownOption>>(); var options = new Dictionary<string, IEnumerable<DropDownOption>>();
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); 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) 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); var c = orderManager.LobbyInfo.ClientInSlot(slot.Key);
if (slot.Value.AllowBots == true && (c == null || c.Bot != null)) 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))); 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) if (addBotOnMapLoad)
{ {
var slot = orderManager.LobbyInfo.FirstEmptyBotSlot(); var slot = orderManager.LobbyInfo.FirstEmptyBotSlot();
var bot = currentMap.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name).FirstOrDefault(); var bot = currentMap.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Type).FirstOrDefault();
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
if (slot != null && bot != null) if (slot != null && bot != null)
orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot, botController.Index, bot))); orderManager.IssueOrder(Order.Command("slot_bot {0} {1} {2}".F(slot, botController.Index, bot)));

View File

@@ -50,13 +50,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
var bots = new List<SlotDropDownOption>(); var bots = new List<SlotDropDownOption>();
if (slot.AllowBots) if (slot.AllowBots)
{ {
foreach (var b in map.Rules.Actors["player"].TraitInfos<IBotInfo>().Select(t => t.Name)) foreach (var b in map.Rules.Actors["player"].TraitInfos<IBotInfo>())
{ {
var bot = b;
var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin); var botController = orderManager.LobbyInfo.Clients.FirstOrDefault(c => c.IsAdmin);
bots.Add(new SlotDropDownOption(bot, bots.Add(new SlotDropDownOption(b.Name,
"slot_bot {0} {1} {2}".F(slot.PlayerReference, botController.Index, bot), "slot_bot {0} {1} {2}".F(slot.PlayerReference, botController.Index, b.Type),
() => client != null && client.Bot == bot)); () => client != null && client.Bot == b.Type));
} }
} }

View File

@@ -1,6 +1,7 @@
Player: Player:
HackyAI@Cabal: HackyAI@Cabal:
Name: Cabal Name: Cabal
Type: Cabal
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact
@@ -122,6 +123,7 @@ Player:
CheckRadius: 7c0 CheckRadius: 7c0
HackyAI@Watson: HackyAI@Watson:
Name: Watson Name: Watson
Type: Watson
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact
@@ -243,6 +245,7 @@ Player:
CheckRadius: 7c0 CheckRadius: 7c0
HackyAI@HAL9001: HackyAI@HAL9001:
Name: HAL 9001 Name: HAL 9001
Type: HAL 9001
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact

View File

@@ -1,6 +1,7 @@
Player: Player:
HackyAI@Omnius: HackyAI@Omnius:
Name: Omnius Name: Omnius
Type: Omnius
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingQueues: Building, Upgrade BuildingQueues: Building, Upgrade
UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft
@@ -119,6 +120,7 @@ Player:
Against: Ally Against: Ally
HackyAI@Vidius: HackyAI@Vidius:
Name: Vidious Name: Vidious
Type: Vidious
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingQueues: Building, Upgrade BuildingQueues: Building, Upgrade
UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft
@@ -239,6 +241,7 @@ Player:
Against: Ally Against: Ally
HackyAI@Gladius: HackyAI@Gladius:
Name: Gladius Name: Gladius
Type: Gladius
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingQueues: Building, Upgrade BuildingQueues: Building, Upgrade
UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft UnitQueues: Infantry, Vehicle, Armor, Starport, Aircraft

View File

@@ -100,6 +100,7 @@ Player:
-HackyAI@TurtleAI: -HackyAI@TurtleAI:
DummyAI@LonestarAI: DummyAI@LonestarAI:
Name: Lonestar AI Name: Lonestar AI
Type: Lonestar AI
^Infantry: ^Infantry:
Inherits@IC: ^IronCurtainable Inherits@IC: ^IronCurtainable

View File

@@ -1,6 +1,7 @@
Player: Player:
HackyAI@RushAI: HackyAI@RushAI:
Name: Rush AI Name: Rush AI
Type: Rush AI
MinimumExcessPower: 40 MinimumExcessPower: 40
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact
@@ -114,6 +115,7 @@ Player:
CheckRadius: 7c0 CheckRadius: 7c0
HackyAI@NormalAI: HackyAI@NormalAI:
Name: Normal AI Name: Normal AI
Type: Normal AI
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact
@@ -245,6 +247,7 @@ Player:
CheckRadius: 7c0 CheckRadius: 7c0
HackyAI@TurtleAI: HackyAI@TurtleAI:
Name: Turtle AI Name: Turtle AI
Type: Turtle AI
MinimumExcessPower: 100 MinimumExcessPower: 100
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact
@@ -376,6 +379,7 @@ Player:
CheckRadius: 7c0 CheckRadius: 7c0
HackyAI@NavalAI: HackyAI@NavalAI:
Name: Naval AI Name: Naval AI
Type: Naval AI
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: fact ConstructionYard: fact
Refinery: proc Refinery: proc

View File

@@ -1,6 +1,7 @@
Player: Player:
HackyAI@TestAI: HackyAI@TestAI:
Name: Test AI Name: Test AI
Type: Test AI
MinimumExcessPower: 60 MinimumExcessPower: 60
BuildingCommonNames: BuildingCommonNames:
ConstructionYard: gacnst ConstructionYard: gacnst