-
Notifications
You must be signed in to change notification settings - Fork 2.6k
/
SpawnStartingUnits.cs
132 lines (108 loc) · 4.6 KB
/
SpawnStartingUnits.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[TraitLocation(SystemActors.World)]
[Desc("Spawn base actor at the spawnpoint and support units in an annulus around the base actor. Both are defined at MPStartUnits. Attach this to the world actor.")]
public class SpawnStartingUnitsInfo : TraitInfo, Requires<StartingUnitsInfo>, NotBefore<LocomotorInfo>, ILobbyOptions
{
public readonly string StartingUnitsClass = "none";
[TranslationReference]
[Desc("Descriptive label for the starting units option in the lobby.")]
public readonly string DropdownLabel = "dropdown-starting-units.label";
[TranslationReference]
[Desc("Tooltip description for the starting units option in the lobby.")]
public readonly string DropdownDescription = "dropdown-starting-units.description";
[Desc("Prevent the starting units option from being changed in the lobby.")]
public readonly bool DropdownLocked = false;
[Desc("Whether to display the starting units option in the lobby.")]
public readonly bool DropdownVisible = true;
[Desc("Display order for the starting units option in the lobby.")]
public readonly int DropdownDisplayOrder = 0;
IEnumerable<LobbyOption> ILobbyOptions.LobbyOptions(MapPreview map)
{
var startingUnits = new Dictionary<string, string>();
// Duplicate classes are defined for different race variants
foreach (var t in map.WorldActorInfo.TraitInfos<StartingUnitsInfo>())
startingUnits[t.Class] = map.GetLocalisedString(t.ClassName);
if (startingUnits.Count > 0)
yield return new LobbyOption(map, "startingunits", DropdownLabel, DropdownDescription, DropdownVisible, DropdownDisplayOrder,
startingUnits, StartingUnitsClass, DropdownLocked);
}
public override object Create(ActorInitializer init) { return new SpawnStartingUnits(this); }
}
public class SpawnStartingUnits : IWorldLoaded
{
readonly SpawnStartingUnitsInfo info;
public SpawnStartingUnits(SpawnStartingUnitsInfo info)
{
this.info = info;
}
public void WorldLoaded(World world, WorldRenderer wr)
{
foreach (var p in world.Players)
if (p.Playable)
SpawnUnitsForPlayer(world, p);
}
void SpawnUnitsForPlayer(World w, Player p)
{
var spawnClass = p.PlayerReference.StartingUnitsClass ?? w.LobbyInfo.GlobalSettings
.OptionOrDefault("startingunits", info.StartingUnitsClass);
var unitGroup = w.Map.Rules.Actors[SystemActors.World].TraitInfos<StartingUnitsInfo>()
.Where(g => g.Class == spawnClass && g.Factions != null && g.Factions.Contains(p.Faction.InternalName))
.RandomOrDefault(w.SharedRandom);
if (unitGroup == null)
throw new InvalidOperationException($"No starting units defined for faction {p.Faction.InternalName} with class {spawnClass}");
if (unitGroup.BaseActor != null)
{
var facing = unitGroup.BaseActorFacing ?? new WAngle(w.SharedRandom.Next(1024));
w.CreateActor(unitGroup.BaseActor.ToLowerInvariant(), new TypeDictionary
{
new LocationInit(p.HomeLocation + unitGroup.BaseActorOffset),
new OwnerInit(p),
new SkipMakeAnimsInit(),
new FacingInit(facing),
new SpawnedByMapInit(),
});
}
if (unitGroup.SupportActors.Length == 0)
return;
var supportSpawnCells = w.Map.FindTilesInAnnulus(p.HomeLocation, unitGroup.InnerSupportRadius + 1, unitGroup.OuterSupportRadius);
foreach (var s in unitGroup.SupportActors)
{
var actorRules = w.Map.Rules.Actors[s.ToLowerInvariant()];
var ip = actorRules.TraitInfo<IPositionableInfo>();
var validCell = supportSpawnCells.Shuffle(w.SharedRandom).FirstOrDefault(c => ip.CanEnterCell(w, null, c));
if (validCell == CPos.Zero)
{
Log.Write("debug", $"No cells available to spawn starting unit {s} for player {p}");
continue;
}
var subCell = ip.SharesCell ? w.ActorMap.FreeSubCell(validCell) : 0;
var facing = unitGroup.SupportActorsFacing ?? new WAngle(w.SharedRandom.Next(1024));
w.CreateActor(s.ToLowerInvariant(), new TypeDictionary
{
new OwnerInit(p),
new LocationInit(validCell),
new SubCellInit(subCell),
new FacingInit(facing),
new SpawnedByMapInit(),
});
}
}
}
}