diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index cb656f8400..7a428769b2 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -367,6 +367,12 @@ namespace OpenRA.Traits [RequireExplicitImplementation] public interface ICreatePlayers { void CreatePlayers(World w); } + [RequireExplicitImplementation] + public interface ICreatePlayersInfo : ITraitInfoInterface + { + void CreateServerPlayers(MapPreview map, Session lobbyInfo, List players); + } + public interface IBotInfo : ITraitInfoInterface { string Type { get; } diff --git a/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs b/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs index 5fa939ae1c..7f4fc45100 100644 --- a/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs +++ b/OpenRA.Mods.Common/Traits/World/CreateMPPlayers.cs @@ -18,7 +18,54 @@ using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Attach this to the world actor.")] - public class CreateMPPlayersInfo : TraitInfo { } + public class CreateMPPlayersInfo : TraitInfo, ICreatePlayersInfo + { + /// + /// Returns a list of GameInformation.Players that matches the indexing of ICreatePlayers.CreatePlayers. + /// Non-playable players appear as null in the list. + /// + void ICreatePlayersInfo.CreateServerPlayers(MapPreview map, Session lobbyInfo, List players) + { + // Create the unplayable map players -- neutral, shellmap, scripted, etc. + foreach (var p in map.Players.Players.Where(p => !p.Value.Playable)) + players.Add(null); + + // Create the regular playable players. + var factions = map.Rules.Actors["world"].TraitInfos().ToArray(); + var bots = map.Rules.Actors["player"].TraitInfos().ToArray(); + + foreach (var kv in lobbyInfo.Slots) + { + var client = lobbyInfo.ClientInSlot(kv.Key); + if (client == null) + continue; + + var clientFaction = factions.First(f => client.Faction == f.InternalName); + + // TODO: Resolve random SpawnPoint and Faction to real values + var player = new GameInformation.Player + { + ClientIndex = client.Index, + Name = Player.ResolvePlayerName(client, lobbyInfo.Clients, bots), + IsHuman = client.Bot == null, + IsBot = client.Bot != null, + FactionName = clientFaction.Name, + FactionId = clientFaction.InternalName, + Color = client.Color, + Team = client.Team, + SpawnPoint = client.SpawnPoint, + IsRandomFaction = clientFaction.RandomFactionMembers.Any(), + IsRandomSpawnPoint = client.SpawnPoint == 0, + Fingerprint = client.Fingerprint, + }; + + players.Add(player); + } + + // Create a player that is allied with everyone for shared observer shroud. + players.Add(null); + } + } public class CreateMPPlayers : ICreatePlayers { diff --git a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs index 640f71936f..476e03679b 100644 --- a/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs +++ b/OpenRA.Mods.Common/Traits/World/EditorActorLayer.cs @@ -14,17 +14,23 @@ using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits.Render; +using OpenRA.Network; using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Traits { [Desc("Required for the map editor to work. Attach this to the world actor.")] - public class EditorActorLayerInfo : TraitInfo + public class EditorActorLayerInfo : TraitInfo, ICreatePlayersInfo { [Desc("Size of partition bins (world pixels)")] public readonly int BinSize = 250; + void ICreatePlayersInfo.CreateServerPlayers(MapPreview map, Session lobbyInfo, List players) + { + throw new NotImplementedException("EditorActorLayer must not be defined on the world actor"); + } + public override object Create(ActorInitializer init) { return new EditorActorLayer(init.Self, this); } }