diff --git a/OpenRA.FileFormats/Map/PlayerReference.cs b/OpenRA.FileFormats/Map/PlayerReference.cs index 5107a913ae..55771b553a 100644 --- a/OpenRA.FileFormats/Map/PlayerReference.cs +++ b/OpenRA.FileFormats/Map/PlayerReference.cs @@ -22,6 +22,7 @@ namespace OpenRA.FileFormats public bool Playable = false; public string Bot = null; public bool DefaultStartingUnits = false; + public string StartingUnitsClass; public bool AllowBots = true; public bool Required = false; diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 1ba16a8da4..d6e52d77ad 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -92,6 +92,7 @@ namespace OpenRA.Network public bool Dedicated; public string Difficulty; public bool Crates = true; + public string StartingUnitsClass = "default"; public bool AllowVersionMismatch; } diff --git a/OpenRA.Mods.RA/MPStartUnits.cs b/OpenRA.Mods.RA/MPStartUnits.cs new file mode 100644 index 0000000000..dc571d74d1 --- /dev/null +++ b/OpenRA.Mods.RA/MPStartUnits.cs @@ -0,0 +1,32 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using OpenRA.FileFormats; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + public class MPStartUnitsInfo : TraitInfo + { + public readonly string Class = "default"; + public readonly string[] Races = { }; + + public readonly string BaseActor = null; + public readonly string[] SupportActors = { }; + + [Desc("Inner radius for spawning support actors")] + public readonly int InnerSupportRadius = 2; + + [Desc("Outer radius for spawning support actors")] + public readonly int OuterSupportRadius = 4; + } + + public class MPStartUnits { } +} diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 249cf6de26..17ab4884c0 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -458,6 +458,7 @@ + diff --git a/OpenRA.Mods.RA/SpawnMPUnits.cs b/OpenRA.Mods.RA/SpawnMPUnits.cs index 061779499c..afb31521a5 100644 --- a/OpenRA.Mods.RA/SpawnMPUnits.cs +++ b/OpenRA.Mods.RA/SpawnMPUnits.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -8,45 +8,74 @@ */ #endregion +using System; +using System.Linq; + using OpenRA.FileFormats; +using OpenRA.Mods.RA.Move; using OpenRA.Traits; namespace OpenRA.Mods.RA { - class SpawnMPUnitsInfo : ITraitInfo, Requires + public class SpawnMPUnitsInfo : TraitInfo, Requires, Requires { } + + public class SpawnMPUnits : IWorldLoaded { - public readonly string InitialUnit = "mcv"; - public readonly string Faction = null; - - public object Create (ActorInitializer init) { return new SpawnMPUnits(this); } - } - - class SpawnMPUnits : IWorldLoaded - { - SpawnMPUnitsInfo info; - - public SpawnMPUnits(SpawnMPUnitsInfo info) { this.info = info; } - public void WorldLoaded(World world) { foreach (var s in world.WorldActor.Trait().Start) - SpawnUnitsForPlayer(s.Key, s.Value); + SpawnUnitsForPlayer(world, s.Key, s.Value); } - void SpawnUnitsForPlayer(Player p, CPos sp) + void SpawnUnitsForPlayer(World w, Player p, CPos sp) { + // They don't want a MCV - the map provides something else for them if (!p.PlayerReference.DefaultStartingUnits) - return; /* they don't want an mcv, the map provides something else for them */ - - /* support different starting units for each faction */ - if (info.Faction != null && p.Country.Race != info.Faction) return; - p.World.CreateActor(info.InitialUnit, new TypeDictionary + var spawnClass = p.PlayerReference.StartingUnitsClass ?? w.LobbyInfo.GlobalSettings.StartingUnitsClass; + var unitGroup = Rules.Info["world"].Traits.WithInterface() + .Where(g => g.Class == spawnClass && g.Races != null && g.Races.Contains(p.Country.Race)) + .RandomOrDefault(w.SharedRandom); + + if (unitGroup == null) + throw new InvalidOperationException("No starting units defined for country {0} with class {1}".F(p.Country.Race, spawnClass)); + + // Spawn base actor at the spawnpoint + if (unitGroup.BaseActor != null) { - new LocationInit( sp ), - new OwnerInit( p ), - }); + w.CreateActor(unitGroup.BaseActor.ToLowerInvariant(), new TypeDictionary + { + new LocationInit(sp), + new OwnerInit(p), + }); + } + + if (!unitGroup.SupportActors.Any()) + return; + + // Spawn support units in an annulus around the base actor + var supportSpawnCells = w.FindTilesInCircle(sp, unitGroup.OuterSupportRadius) + .Except(w.FindTilesInCircle(sp, unitGroup.InnerSupportRadius)); + + foreach (var s in unitGroup.SupportActors) + { + var mi = Rules.Info[s.ToLowerInvariant()].Traits.Get(); + var validCells = supportSpawnCells.Where(c => mi.CanEnterCell(w, c)); + if (!validCells.Any()) + throw new InvalidOperationException("No cells available to spawn starting unit {0}".F(s)); + + var cell = validCells.Random(w.SharedRandom); + var subCell = w.ActorMap.FreeSubCell(cell).Value; + + w.CreateActor(s.ToLowerInvariant(), new TypeDictionary + { + new OwnerInit(p), + new LocationInit(cell), + new SubCellInit(subCell), + new FacingInit(w.SharedRandom.Next(256)) + }); + } } } } diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index 1f2920bce5..5d1133fe79 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -307,9 +307,12 @@ World: Depths:5,5,5,5,5,5 DebugOverlay: SpawnMapActors: - CreateMPPlayers: - SpawnMPUnits: MPStartLocations: + CreateMPPlayers: + MPStartUnits: + Races: gdi, nod + BaseActor: mcv + SpawnMPUnits: SpatialBins: BinSize: 4 CrateSpawner: diff --git a/mods/d2k/rules/system.yaml b/mods/d2k/rules/system.yaml index 79b5c49606..639a37d593 100644 --- a/mods/d2k/rules/system.yaml +++ b/mods/d2k/rules/system.yaml @@ -380,15 +380,16 @@ World: SpawnMapActors: CreateMPPlayers: MPStartLocations: - SpawnMPUnits@atreides: - InitialUnit: mcva - Faction: atreides - SpawnMPUnits@harkonnen: - InitialUnit: mcvh - Faction: harkonnen - SpawnMPUnits@ordos: - InitialUnit: mcvo - Faction: ordos + MPStartUnits@atreides: + Races: atreides + BaseActor: mcva + MPStartUnits@harkonnen: + Races: harkonnen + BaseActor: mcvh + MPStartUnits@ordos: + Races: ordos + BaseActor: mcvo + SpawnMPUnits: SpatialBins: BinSize: 4 PathFinder: diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 4953c644ec..af115dc9e3 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -639,6 +639,9 @@ World: DebugOverlay: SpawnMapActors: CreateMPPlayers: + MPStartUnits: + Races: soviet, allies + BaseActor: mcv MPStartLocations: SpawnMPUnits: SpatialBins: