Unstable 0.17.0.0

This commit is contained in:
Markus Isberg
2022-02-26 02:43:01 +09:00
parent a83f375681
commit 3974067915
913 changed files with 32472 additions and 32364 deletions
@@ -1,93 +1,37 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
internal class NPCSet : IDisposable
internal class NPCSet : Prefab
{
private static List<NPCSet>? Sets { get; set; }
public readonly static PrefabCollection<NPCSet> Sets = new PrefabCollection<NPCSet>();
private string Identifier { get; }
private readonly List<HumanPrefab> Humans = new List<HumanPrefab>();
private readonly ImmutableArray<HumanPrefab> Humans;
private bool Disposed { get; set; }
private NPCSet(XElement element, string filePath)
public NPCSet(ContentXElement element, NPCSetsFile file) : base(file, element.GetAttributeIdentifier("identifier", ""))
{
Identifier = element.GetAttributeString("identifier", string.Empty);
foreach (XElement npcElement in element.Elements())
{
Humans.Add(new HumanPrefab(npcElement, filePath));
}
Humans = element.Elements().Select(npcElement => new HumanPrefab(npcElement, file)).ToImmutableArray();
}
public static HumanPrefab? Get(string identifier, string npcidentifier)
public static HumanPrefab? Get(Identifier setIdentifier, Identifier npcidentifier)
{
HumanPrefab prefab = Sets.Where(set => set.Identifier == identifier).SelectMany(npcSet => npcSet.Humans.Where(npcSetHuman => npcSetHuman.Identifier == npcidentifier)).FirstOrDefault();
HumanPrefab? prefab = Sets.Where(set => set.Identifier == setIdentifier).SelectMany(npcSet => npcSet.Humans.Where(npcSetHuman => npcSetHuman.Identifier == npcidentifier)).FirstOrDefault();
if (prefab == null)
{
DebugConsole.ThrowError($"Could not find human prefab \"{npcidentifier}\" from \"{identifier}\".");
DebugConsole.ThrowError($"Could not find human prefab \"{npcidentifier}\" from \"{setIdentifier}\".");
return null;
}
return new HumanPrefab(prefab.Element, prefab.FilePath);
}
public static void LoadSets()
{
Sets?.ForEach(set => set.Dispose());
Sets = new List<NPCSet>();
IEnumerable<ContentFile> files = GameMain.Instance.GetFilesOfType(ContentType.NPCSets);
foreach (ContentFile file in files)
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
XElement? rootElement = doc?.Root;
if (doc == null || rootElement == null) { continue; }
if (doc.Root.IsOverride())
{
Sets.Clear();
DebugConsole.NewMessage($"Overriding all NPC sets with '{file.Path}'", Color.Yellow);
}
foreach (XElement element in rootElement.Elements())
{
bool isOverride = element.IsOverride();
XElement sourceElement = isOverride ? element.FirstElement() : element;
string elementName = sourceElement.Name.ToString().ToLowerInvariant();
string identifier = sourceElement.GetAttributeString("identifier", null);
if (string.IsNullOrWhiteSpace(identifier))
{
DebugConsole.ThrowError($"No identifier defined for the NPC set config '{elementName}' in file '{file.Path}'");
continue;
}
var existingParams = Sets.Find(set => set.Identifier == identifier);
if (existingParams != null)
{
if (isOverride)
{
DebugConsole.NewMessage($"Overriding NPC set config '{identifier}' using the file '{file.Path}'", Color.Yellow);
Sets.Remove(existingParams);
}
else
{
DebugConsole.ThrowError($"Duplicate NPC set config: '{identifier}' defined in {elementName} of '{file.Path}'");
continue;
}
}
Sets.Add(new NPCSet(element, file.Path));
}
}
return prefab;
}
private void Dispose(bool disposing)
@@ -103,7 +47,7 @@ namespace Barotrauma
Disposed = true;
}
public void Dispose()
public override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
@@ -1,164 +1,209 @@
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
class OutpostGenerationParams : ISerializableEntity
class OutpostGenerationParams : PrefabWithUintIdentifier, ISerializableEntity
{
public static List<OutpostGenerationParams> Params { get; private set; }
public readonly static PrefabCollection<OutpostGenerationParams> OutpostParams = new PrefabCollection<OutpostGenerationParams>();
public virtual string Name { get; private set; }
public string Identifier { get; private set; }
private readonly List<string> allowedLocationTypes = new List<string>();
private readonly HashSet<Identifier> allowedLocationTypes = new HashSet<Identifier>();
/// <summary>
/// Identifiers of the location types this outpost can appear in. If empty, can appear in all types of locations.
/// </summary>
public IEnumerable<string> AllowedLocationTypes
public IEnumerable<Identifier> AllowedLocationTypes
{
get { return allowedLocationTypes; }
}
[Serialize(10, isSaveable: true), Editable(MinValueInt = 1, MaxValueInt = 50)]
[Serialize(10, IsPropertySaveable.Yes), Editable(MinValueInt = 1, MaxValueInt = 50)]
public int TotalModuleCount
{
get;
set;
}
[Serialize(200.0f, isSaveable: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
[Serialize(200.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
public float MinHallwayLength
{
get;
set;
}
[Serialize(false, isSaveable: true), Editable]
[Serialize(false, IsPropertySaveable.Yes), Editable]
public bool AlwaysDestructible
{
get;
set;
}
[Serialize(false, isSaveable: true), Editable]
[Serialize(false, IsPropertySaveable.Yes), Editable]
public bool AlwaysRewireable
{
get;
set;
}
[Serialize(false, isSaveable: true), Editable]
[Serialize(false, IsPropertySaveable.Yes), Editable]
public bool AllowStealing
{
get;
set;
}
[Serialize(true, isSaveable: true), Editable]
[Serialize(true, IsPropertySaveable.Yes), Editable]
public bool SpawnCrewInsideOutpost
{
get;
set;
}
[Serialize(true, isSaveable: true), Editable]
[Serialize(true, IsPropertySaveable.Yes), Editable]
public bool LockUnusedDoors
{
get;
set;
}
[Serialize(true, isSaveable: true), Editable]
[Serialize(true, IsPropertySaveable.Yes), Editable]
public bool RemoveUnusedGaps
{
get;
set;
}
[Serialize(0.0f, isSaveable: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
public float MinWaterPercentage
{
get;
set;
}
[Serialize(0.0f, isSaveable: true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
[Serialize(0.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
public float MaxWaterPercentage
{
get;
set;
}
[Serialize("", isSaveable: true), Editable]
[Serialize("", IsPropertySaveable.Yes), Editable]
public string ReplaceInRadiation { get; set; }
private readonly Dictionary<string, int> moduleCounts = new Dictionary<string, int>();
private readonly Dictionary<Identifier, int> moduleCounts = new Dictionary<Identifier, int>();
public IEnumerable<KeyValuePair<string, int>> ModuleCounts
public IReadOnlyDictionary<Identifier, int> ModuleCounts
{
get { return moduleCounts; }
}
private readonly List<List<HumanPrefab>> humanPrefabLists = new List<List<HumanPrefab>>();
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
protected OutpostGenerationParams(XElement element, string filePath)
private class NpcCollection : IReadOnlyList<HumanPrefab>
{
Identifier = element.GetAttributeString("identifier", "");
Name = element.GetAttributeString("name", Identifier);
allowedLocationTypes = element.GetAttributeStringArray("allowedlocationtypes", Array.Empty<string>()).ToList();
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
private class Entry
{
private readonly HumanPrefab humanPrefab = null;
private readonly Identifier setIdentifier = Identifier.Empty;
private readonly Identifier npcIdentifier = Identifier.Empty;
public Entry(HumanPrefab humanPrefab)
{
this.humanPrefab = humanPrefab;
}
if (element == null) { return; }
foreach (XElement subElement in element.Elements())
public Entry(Identifier setIdentifier, Identifier npcIdentifier)
{
this.setIdentifier = setIdentifier;
this.npcIdentifier = npcIdentifier;
}
public HumanPrefab HumanPrefab
=> humanPrefab ?? NPCSet.Get(setIdentifier, npcIdentifier);
}
private readonly List<Entry> entries = new List<Entry>();
public void Add(HumanPrefab humanPrefab)
=> entries.Add(new Entry(humanPrefab));
public void Add(Identifier setIdentifier, Identifier npcIdentifier)
=> entries.Add(new Entry(setIdentifier, npcIdentifier));
public IEnumerator<HumanPrefab> GetEnumerator()
{
foreach (var entry in entries)
{
yield return entry.HumanPrefab;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count => entries.Count;
public HumanPrefab this[int index] => entries[index].HumanPrefab;
}
private readonly ImmutableArray<IReadOnlyList<HumanPrefab>> humanPrefabCollections;
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
#warning TODO: this shouldn't really accept any ContentFile, issue is that RuinConfigFile and OutpostConfigFile are separate derived classes
public OutpostGenerationParams(ContentXElement element, ContentFile file) : base(file, element.GetAttributeIdentifier("identifier", ""))
{
Name = element.GetAttributeString("name", Identifier.Value);
allowedLocationTypes = element.GetAttributeIdentifierArray("allowedlocationtypes", Array.Empty<Identifier>()).ToHashSet();
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
var humanPrefabCollections = new List<IReadOnlyList<HumanPrefab>>();
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "modulecount":
string moduleFlag = (subElement.GetAttributeString("flag", null) ?? subElement.GetAttributeString("moduletype", "")).ToLowerInvariant();
Identifier moduleFlag = subElement.GetAttributeIdentifier("flag", subElement.GetAttributeIdentifier("moduletype", ""));
moduleCounts[moduleFlag] = subElement.GetAttributeInt("count", 0);
break;
case "npcs":
humanPrefabLists.Add(new List<HumanPrefab>());
foreach (XElement npcElement in subElement.Elements())
var newCollection = new NpcCollection();
foreach (var npcElement in subElement.Elements())
{
string from = npcElement.GetAttributeString("from", string.Empty);
// ReSharper disable once ConvertIfStatementToConditionalTernaryExpression
if (!string.IsNullOrWhiteSpace(from))
Identifier from = npcElement.GetAttributeIdentifier("from", Identifier.Empty);
if (from != Identifier.Empty)
{
HumanPrefab prefab = NPCSet.Get(from, npcElement.GetAttributeString("identifier", string.Empty));
if (prefab != null)
{
humanPrefabLists.Last().Add(prefab);
}
newCollection.Add(from, npcElement.GetAttributeIdentifier("identifier", Identifier.Empty));
}
else
{
humanPrefabLists.Last().Add(new HumanPrefab(npcElement, filePath));
newCollection.Add(new HumanPrefab(npcElement, file));
}
}
humanPrefabCollections.Add(newCollection);
break;
}
}
this.humanPrefabCollections = humanPrefabCollections.ToImmutableArray();
}
public int GetModuleCount(string moduleFlag)
public int GetModuleCount(Identifier moduleFlag)
{
if (string.IsNullOrEmpty(moduleFlag) || moduleFlag == "none") { return int.MaxValue; }
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return int.MaxValue; }
return moduleCounts.ContainsKey(moduleFlag) ? moduleCounts[moduleFlag] : 0;
}
public void SetModuleCount(string moduleFlag, int count)
public void SetModuleCount(Identifier moduleFlag, int count)
{
if (string.IsNullOrEmpty(moduleFlag) || moduleFlag == "none") { return; }
if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return; }
if (count <= 0)
{
moduleCounts.Remove(moduleFlag);
@@ -169,66 +214,22 @@ namespace Barotrauma
}
}
public void SetAllowedLocationTypes(IEnumerable<string> allowedLocationTypes)
public void SetAllowedLocationTypes(IEnumerable<Identifier> allowedLocationTypes)
{
this.allowedLocationTypes.Clear();
foreach (string locationType in allowedLocationTypes)
foreach (Identifier locationType in allowedLocationTypes)
{
if (locationType.Equals("any", StringComparison.OrdinalIgnoreCase)) { continue; }
if (locationType == "any") { continue; }
this.allowedLocationTypes.Add(locationType);
}
}
public IEnumerable<HumanPrefab> GetHumanPrefabs(Rand.RandSync randSync)
public IReadOnlyList<HumanPrefab> GetHumanPrefabs(Rand.RandSync randSync)
{
if (humanPrefabLists == null || !humanPrefabLists.Any()) { return Enumerable.Empty<HumanPrefab>(); }
return humanPrefabLists.GetRandom(randSync);
if (!humanPrefabCollections.Any()) { return Array.Empty<HumanPrefab>(); }
return humanPrefabCollections.GetRandom(randSync);
}
public static void LoadPresets()
{
Params = new List<OutpostGenerationParams>();
var files = GameMain.Instance.GetFilesOfType(ContentType.OutpostConfig);
foreach (ContentFile file in files)
{
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
if (doc?.Root == null) { continue; }
var mainElement = doc.Root;
if (doc.Root.IsOverride())
{
Params.Clear();
DebugConsole.NewMessage($"Overriding all outpost generation parameters with '{file.Path}'", Color.Yellow);
}
foreach (XElement element in mainElement.Elements())
{
bool isOverride = element.IsOverride();
XElement sourceElement = isOverride ? element.FirstElement() : element;
string elementName = sourceElement.Name.ToString().ToLowerInvariant();
string identifier = sourceElement.GetAttributeString("identifier", null);
if (string.IsNullOrWhiteSpace(identifier))
{
DebugConsole.ThrowError($"No identifier defined for the outpost config '{elementName}' in file '{file.Path}'");
continue;
}
var existingParams = Params.Find(p => p.Identifier == identifier);
if (existingParams != null)
{
if (isOverride)
{
DebugConsole.NewMessage($"Overriding outpost config '{identifier}' using the file '{file.Path}'", Color.Yellow);
Params.Remove(existingParams);
}
else
{
DebugConsole.ThrowError($"Duplicate outpost config: '{identifier}' defined in {elementName} of '{file.Path}'");
continue;
}
}
Params.Add(new OutpostGenerationParams(element, file.Path));
}
}
}
public override void Dispose() { }
}
}
@@ -3,7 +3,9 @@ using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
namespace Barotrauma
{
@@ -26,7 +28,7 @@ namespace Barotrauma
public OutpostModuleInfo.GapPosition UsedGapPositions = 0;
public readonly HashSet<string> FulfilledModuleTypes = new HashSet<string>();
public readonly HashSet<Identifier> FulfilledModuleTypes = new HashSet<Identifier>();
public Vector2 Offset;
@@ -67,10 +69,18 @@ namespace Barotrauma
private static Submarine Generate(OutpostGenerationParams generationParams, LocationType locationType, Location location, bool onlyEntrance = false)
{
var outpostModuleFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.OutpostModule);
var outpostModuleFiles = ContentPackageManager.EnabledPackages.All
.SelectMany(p => p.GetFiles<OutpostModuleFile>())
.OrderBy(f => f.UintIdentifier).ToArray();
var uintIdDupes = outpostModuleFiles.Where(f1 =>
outpostModuleFiles.Any(f2 => f1 != f2 && f1.UintIdentifier == f2.UintIdentifier)).ToArray();
if (uintIdDupes.Any())
{
throw new Exception($"OutpostModuleFile UintIdentifier duplicates found: {uintIdDupes.Select(f => f.Path)}");
}
if (location != null)
{
if (location.IsCriticallyRadiated() && OutpostGenerationParams.Params.FirstOrDefault(p => p.Identifier.Equals(generationParams.ReplaceInRadiation, StringComparison.OrdinalIgnoreCase)) is { } newParams)
if (location.IsCriticallyRadiated() && OutpostGenerationParams.OutpostParams.FirstOrDefault(p => p.Identifier == generationParams.ReplaceInRadiation) is { } newParams)
{
generationParams = newParams;
}
@@ -80,21 +90,21 @@ namespace Barotrauma
//load the infos of the outpost module files
List<SubmarineInfo> outpostModules = new List<SubmarineInfo>();
foreach (ContentFile outpostModuleFile in outpostModuleFiles)
foreach (var outpostModuleFile in outpostModuleFiles)
{
var subInfo = new SubmarineInfo(outpostModuleFile.Path);
var subInfo = new SubmarineInfo(outpostModuleFile.Path.Value);
if (subInfo.OutpostModuleInfo != null)
{
if (generationParams is RuinGeneration.RuinGenerationParams)
{
//if the module doesn't have the ruin flag or any other flag used in the generation params, don't use it in ruins
if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin") &&
if (!subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin".ToIdentifier()) &&
!generationParams.ModuleCounts.Any(m => subInfo.OutpostModuleInfo.ModuleFlags.Contains(m.Key)))
{
continue;
}
}
else if (subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin"))
else if (subInfo.OutpostModuleInfo.ModuleFlags.Contains("ruin".ToIdentifier()))
{
continue;
}
@@ -131,14 +141,23 @@ namespace Barotrauma
selectedModules.Clear();
//select which module types the outpost should consist of
var pendingModuleFlags = onlyEntrance ? new List<string>() { generationParams.ModuleCounts.First().Key } : SelectModules(outpostModules, generationParams);
foreach (string flag in pendingModuleFlags.Distinct().ToList())
List<Identifier> pendingModuleFlags;
using (var md5 = MD5.Create())
{
if (flag.Equals("none", StringComparison.OrdinalIgnoreCase)) { continue; }
#warning TODO: cursed
pendingModuleFlags = onlyEntrance
? generationParams.ModuleCounts
.Keys.OrderBy(k => ToolBox.IdentifierToUint32Hash(k, md5))
.First().ToEnumerable().ToList()
: SelectModules(outpostModules, generationParams);
}
foreach (Identifier flag in pendingModuleFlags)
{
if (flag == "none") { continue; }
int pendingCount = pendingModuleFlags.Count(f => f == flag);
int availableModuleCount =
outpostModules
.Where(m => m.OutpostModuleInfo.ModuleFlags.Any(f => f.Equals(flag, StringComparison.OrdinalIgnoreCase)))
.Where(m => m.OutpostModuleInfo.ModuleFlags.Any(f => f == flag))
.Select(m => m.OutpostModuleInfo.MaxCount)
.DefaultIfEmpty(0)
.Sum();
@@ -154,7 +173,7 @@ namespace Barotrauma
}
//the first module is spawned separately, remove it from the list of pending modules
string initialModuleFlag = pendingModuleFlags.FirstOrDefault() ?? "airlock";
Identifier initialModuleFlag = pendingModuleFlags.FirstOrDefault().IfEmpty("airlock".ToIdentifier());
pendingModuleFlags.Remove(initialModuleFlag);
var initialModule = GetRandomModule(outpostModules, initialModuleFlag, locationType);
@@ -162,9 +181,9 @@ namespace Barotrauma
{
throw new Exception("Failed to generate an outpost (no airlock modules found).");
}
foreach (string initialFlag in initialModule.OutpostModuleInfo.ModuleFlags)
foreach (Identifier initialFlag in initialModule.OutpostModuleInfo.ModuleFlags)
{
if (pendingModuleFlags.Contains("initialFlag")) { pendingModuleFlags.Remove(initialFlag); }
if (pendingModuleFlags.Contains("initialFlag".ToIdentifier())) { pendingModuleFlags.Remove(initialFlag); }
}
if (remainingTries == 1)
@@ -176,7 +195,7 @@ namespace Barotrauma
selectedModules.Add(new PlacedModule(initialModule, null, OutpostModuleInfo.GapPosition.None));
selectedModules.Last().FulfilledModuleTypes.Add(initialModuleFlag);
AppendToModule(selectedModules.Last(), outpostModules.ToList(), pendingModuleFlags, selectedModules, locationType, allowExtendBelowInitialModule: generationParams is RuinGeneration.RuinGenerationParams);
if (pendingModuleFlags.Any(flag => !flag.Equals("none", StringComparison.OrdinalIgnoreCase)))
if (pendingModuleFlags.Any(flag => flag != "none"))
{
remainingTries--;
if (remainingTries <= 0)
@@ -196,7 +215,7 @@ namespace Barotrauma
sub.Info.OutpostGenerationParams = generationParams;
if (!generationFailed)
{
foreach (Hull hull in Hull.hullList)
foreach (Hull hull in Hull.HullList)
{
if (hull.Submarine != sub) { continue; }
if (string.IsNullOrEmpty(hull.RoomName) ||
@@ -223,12 +242,14 @@ namespace Barotrauma
DebugConsole.NewMessage("Failed to generate an outpost without overlapping modules. Trying to use a pre-built outpost instead...");
var outpostFiles = ContentPackage.GetFilesOfType(GameMain.Config.AllEnabledPackages, ContentType.Outpost);
var outpostFiles = ContentPackageManager.EnabledPackages.All
.SelectMany(p => p.GetFiles<OutpostFile>())
.OrderBy(f => f.UintIdentifier).ToArray();
if (!outpostFiles.Any())
{
throw new Exception("Failed to generate an outpost. Could not generate an outpost from the available outpost modules and there are no pre-built outposts available.");
}
var prebuiltOutpostInfo = new SubmarineInfo(outpostFiles.GetRandom(Rand.RandSync.Server).Path)
var prebuiltOutpostInfo = new SubmarineInfo(outpostFiles.GetRandom(Rand.RandSync.ServerAndClient).Path.Value)
{
Type = SubmarineType.Outpost
};
@@ -386,7 +407,7 @@ namespace Barotrauma
}
else
{
hull.WaterVolume = hull.Volume * Rand.Range(generationParams.MinWaterPercentage, generationParams.MaxWaterPercentage, Rand.RandSync.Server) * 0.01f;
hull.WaterVolume = hull.Volume * Rand.Range(generationParams.MinWaterPercentage, generationParams.MaxWaterPercentage, Rand.RandSync.ServerAndClient) * 0.01f;
}
}
}
@@ -400,13 +421,13 @@ namespace Barotrauma
/// <summary>
/// Select the number and types of the modules to use in the outpost
/// </summary>
private static List<string> SelectModules(IEnumerable<SubmarineInfo> modules, OutpostGenerationParams generationParams)
private static List<Identifier> SelectModules(IEnumerable<SubmarineInfo> modules, OutpostGenerationParams generationParams)
{
int totalModuleCount = generationParams.TotalModuleCount;
var pendingModuleFlags = new List<string>();
var pendingModuleFlags = new List<Identifier>();
bool availableModulesFound = true;
string initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Key;
Identifier initialModuleFlag = generationParams.ModuleCounts.FirstOrDefault().Key;
pendingModuleFlags.Add(initialModuleFlag);
while (pendingModuleFlags.Count < totalModuleCount && availableModulesFound)
{
@@ -426,13 +447,17 @@ namespace Barotrauma
pendingModuleFlags.Add(moduleFlag.Key);
}
}
pendingModuleFlags.Shuffle(Rand.RandSync.Server);
using (MD5 md5 = MD5.Create())
{
pendingModuleFlags.Sort((i1, i2) => (int)ToolBox.StringToUInt32Hash(i1.Value.ToLowerInvariant(), md5) - (int)ToolBox.StringToUInt32Hash(i2.Value.ToLowerInvariant(), md5));
}
pendingModuleFlags.Shuffle(Rand.RandSync.ServerAndClient);
while (pendingModuleFlags.Count < totalModuleCount)
{
//don't place "none" modules at the end because
// a. "filler rooms" at the end of a hallway are pointless
// b. placing the unnecessary filler rooms first give more options for the placement of the more important modules
pendingModuleFlags.Insert(Rand.Int(pendingModuleFlags.Count - 1, Rand.RandSync.Server), "none");
pendingModuleFlags.Insert(Rand.Int(pendingModuleFlags.Count - 1, Rand.RandSync.ServerAndClient), "none".ToIdentifier());
}
//make sure the initial module is inserted first
@@ -452,7 +477,7 @@ namespace Barotrauma
/// <param name="selectedModules">The modules we've already selected to be used in the outpost.</param>
private static bool AppendToModule(PlacedModule currentModule,
List<SubmarineInfo> availableModules,
List<string> pendingModuleFlags,
List<Identifier> pendingModuleFlags,
List<PlacedModule> selectedModules,
LocationType locationType,
bool retry = true,
@@ -461,7 +486,7 @@ namespace Barotrauma
if (pendingModuleFlags.Count == 0) { return true; }
List<PlacedModule> placedModules = new List<PlacedModule>();
foreach (OutpostModuleInfo.GapPosition gapPosition in GapPositions().Randomize(Rand.RandSync.Server))
foreach (OutpostModuleInfo.GapPosition gapPosition in GapPositions.Randomize(Rand.RandSync.ServerAndClient))
{
if (currentModule.UsedGapPositions.HasFlag(gapPosition)) { continue; }
if (!allowExtendBelowInitialModule)
@@ -526,15 +551,15 @@ namespace Barotrauma
PlacedModule currentModule,
OutpostModuleInfo.GapPosition gapPosition,
List<SubmarineInfo> availableModules,
List<string> pendingModuleFlags,
List<Identifier> pendingModuleFlags,
List<PlacedModule> selectedModules,
LocationType locationType)
{
if (pendingModuleFlags.Count == 0) { return null; }
string flagToPlace = "none";
Identifier flagToPlace = "none".ToIdentifier();
SubmarineInfo nextModule = null;
foreach (string moduleFlag in pendingModuleFlags)
foreach (Identifier moduleFlag in pendingModuleFlags)
{
flagToPlace = moduleFlag;
nextModule = GetRandomModule(currentModule?.Info?.OutpostModuleInfo, availableModules, flagToPlace, gapPosition, locationType);
@@ -547,7 +572,7 @@ namespace Barotrauma
{
Offset = currentModule.Offset + GetMoveDir(gapPosition),
};
foreach (string moduleFlag in nextModule.OutpostModuleInfo.ModuleFlags)
foreach (Identifier moduleFlag in nextModule.OutpostModuleInfo.ModuleFlags)
{
if (!pendingModuleFlags.Contains(moduleFlag)) { continue; }
if (moduleFlag != "none" || flagToPlace == "none")
@@ -711,19 +736,19 @@ namespace Barotrauma
return solutionFound;
}
private static SubmarineInfo GetRandomModule(IEnumerable<SubmarineInfo> modules, string moduleFlag, LocationType locationType)
private static SubmarineInfo GetRandomModule(IEnumerable<SubmarineInfo> modules, Identifier moduleFlag, LocationType locationType)
{
IEnumerable<SubmarineInfo> availableModules = null;
if (string.IsNullOrEmpty(moduleFlag) || moduleFlag.Equals("none"))
if (moduleFlag.IsEmpty || moduleFlag == "none")
{
availableModules = modules.Where(m => !m.OutpostModuleInfo.ModuleFlags.Any() || m.OutpostModuleInfo.ModuleFlags.Contains("none"));
availableModules = modules.Where(m => !m.OutpostModuleInfo.ModuleFlags.Any() || m.OutpostModuleInfo.ModuleFlags.Contains("none".ToIdentifier()));
}
else
{
availableModules = modules.Where(m => m.OutpostModuleInfo.ModuleFlags.Contains(moduleFlag));
if (moduleFlag != "hallwayhorizontal" && moduleFlag != "hallwayvertical")
{
availableModules = availableModules.Where(m => !m.OutpostModuleInfo.ModuleFlags.Contains("hallwayhorizontal") && !m.OutpostModuleInfo.ModuleFlags.Contains("hallwayvertical"));
availableModules = availableModules.Where(m => !m.OutpostModuleInfo.ModuleFlags.Contains("hallwayhorizontal".ToIdentifier()) && !m.OutpostModuleInfo.ModuleFlags.Contains("hallwayvertical".ToIdentifier()));
}
}
@@ -731,7 +756,7 @@ namespace Barotrauma
//try to search for modules made specifically for this location type first
var modulesSuitableForLocationType =
availableModules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier.ToLowerInvariant()));
availableModules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier));
//if not found, search for modules suitable for any location type
if (!modulesSuitableForLocationType.Any())
@@ -742,21 +767,21 @@ namespace Barotrauma
if (!modulesSuitableForLocationType.Any())
{
DebugConsole.NewMessage($"Could not find a suitable module for the location type {locationType}. Module flag: {moduleFlag}.", Color.Orange);
return ToolBox.SelectWeightedRandom(availableModules.ToList(), availableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.Server);
return ToolBox.SelectWeightedRandom(availableModules.ToList(), availableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
}
else
{
return ToolBox.SelectWeightedRandom(modulesSuitableForLocationType.ToList(), modulesSuitableForLocationType.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.Server);
return ToolBox.SelectWeightedRandom(modulesSuitableForLocationType.ToList(), modulesSuitableForLocationType.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
}
}
private static SubmarineInfo GetRandomModule(OutpostModuleInfo prevModule, IEnumerable<SubmarineInfo> modules, string moduleFlag, OutpostModuleInfo.GapPosition gapPosition, LocationType locationType)
private static SubmarineInfo GetRandomModule(OutpostModuleInfo prevModule, IEnumerable<SubmarineInfo> modules, Identifier moduleFlag, OutpostModuleInfo.GapPosition gapPosition, LocationType locationType)
{
IEnumerable<SubmarineInfo> availableModules = null;
if (string.IsNullOrEmpty(moduleFlag) || moduleFlag.Equals("none"))
if (moduleFlag.IsEmpty || moduleFlag.Equals("none"))
{
availableModules = modules
.Where(m => !m.OutpostModuleInfo.ModuleFlags.Any() || (m.OutpostModuleInfo.ModuleFlags.Count() == 1 && m.OutpostModuleInfo.ModuleFlags.Contains("none")) && m.OutpostModuleInfo.GapPositions.HasFlag(gapPosition));
.Where(m => !m.OutpostModuleInfo.ModuleFlags.Any() || (m.OutpostModuleInfo.ModuleFlags.Count() == 1 && m.OutpostModuleInfo.ModuleFlags.Contains("none".ToIdentifier())) && m.OutpostModuleInfo.GapPositions.HasFlag(gapPosition));
}
else
{
@@ -772,7 +797,7 @@ namespace Barotrauma
//try to search for modules made specifically for this location type first
var modulesSuitableForLocationType =
availableModules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier.ToLowerInvariant()));
availableModules.Where(m => m.OutpostModuleInfo.AllowedLocationTypes.Contains(locationType.Identifier));
//if not found, search for modules suitable for any location type
if (!modulesSuitableForLocationType.Any())
@@ -783,11 +808,11 @@ namespace Barotrauma
if (!modulesSuitableForLocationType.Any())
{
DebugConsole.NewMessage($"Could not find a suitable module for the location type {locationType}. Module flag: {moduleFlag}.", Color.Orange);
return ToolBox.SelectWeightedRandom(availableModules.ToList(), availableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.Server);
return ToolBox.SelectWeightedRandom(availableModules.ToList(), availableModules.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
}
else
{
return ToolBox.SelectWeightedRandom(modulesSuitableForLocationType.ToList(), modulesSuitableForLocationType.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.Server);
return ToolBox.SelectWeightedRandom(modulesSuitableForLocationType.ToList(), modulesSuitableForLocationType.Select(m => m.OutpostModuleInfo.Commonness).ToList(), Rand.RandSync.ServerAndClient);
}
}
@@ -807,13 +832,13 @@ namespace Barotrauma
}
}
private static IEnumerable<OutpostModuleInfo.GapPosition> GapPositions()
private readonly static OutpostModuleInfo.GapPosition[] GapPositions = new[]
{
yield return OutpostModuleInfo.GapPosition.Right;
yield return OutpostModuleInfo.GapPosition.Left;
yield return OutpostModuleInfo.GapPosition.Top;
yield return OutpostModuleInfo.GapPosition.Bottom;
}
OutpostModuleInfo.GapPosition.Right,
OutpostModuleInfo.GapPosition.Left,
OutpostModuleInfo.GapPosition.Top,
OutpostModuleInfo.GapPosition.Bottom
};
private static OutpostModuleInfo.GapPosition GetOpposingGapPosition(OutpostModuleInfo.GapPosition thisGapPosition)
{
@@ -824,7 +849,7 @@ namespace Barotrauma
OutpostModuleInfo.GapPosition.Bottom => OutpostModuleInfo.GapPosition.Top,
OutpostModuleInfo.GapPosition.Top => OutpostModuleInfo.GapPosition.Bottom,
OutpostModuleInfo.GapPosition.None => OutpostModuleInfo.GapPosition.None,
_ => throw new InvalidOperationException()
_ => throw new ArgumentException()
};
}
@@ -837,7 +862,7 @@ namespace Barotrauma
OutpostModuleInfo.GapPosition.Bottom => Vector2.UnitY,
OutpostModuleInfo.GapPosition.Top => -Vector2.UnitY,
OutpostModuleInfo.GapPosition.None => Vector2.Zero,
_ => throw new InvalidOperationException()
_ => throw new ArgumentException()
};
}
@@ -885,7 +910,7 @@ namespace Barotrauma
private static bool CanAttachTo(OutpostModuleInfo from, OutpostModuleInfo to)
{
if (!from.AllowAttachToModules.Any() || from.AllowAttachToModules.All(s => s.Equals("any", StringComparison.OrdinalIgnoreCase))) { return true; }
if (!from.AllowAttachToModules.Any() || from.AllowAttachToModules.All(s => s == "any")) { return true; }
return from.AllowAttachToModules.Any(s => to.ModuleFlags.Contains(s));
}
@@ -915,7 +940,7 @@ namespace Barotrauma
{
for (int i = 0; i < thisJunctionBox.Connections.Count && i < previousJunctionBox.Connections.Count; i++)
{
var wirePrefab = MapEntityPrefab.Find(name: null, identifier: thisJunctionBox.Connections[i].IsPower ? "redwire" : "bluewire") as ItemPrefab;
var wirePrefab = MapEntityPrefab.FindByIdentifier((thisJunctionBox.Connections[i].IsPower ? "redwire" : "bluewire").ToIdentifier()) as ItemPrefab;
var wire = new Item(wirePrefab, thisJunctionBox.Item.Position, sub).GetComponent<Wire>();
if (!thisJunctionBox.Connections[i].TryAddLink(wire))
@@ -1021,9 +1046,9 @@ namespace Barotrauma
{
suitableModules = availableModules.Where(m =>
!m.OutpostModuleInfo.AllowAttachToModules.Any() ||
m.OutpostModuleInfo.AllowAttachToModules.All(s => s.Equals("any", StringComparison.OrdinalIgnoreCase)));
m.OutpostModuleInfo.AllowAttachToModules.All(s => s == "any"));
}
var hallwayInfo = GetRandomModule(suitableModules, isHorizontal ? "hallwayhorizontal" : "hallwayvertical", locationType);
var hallwayInfo = GetRandomModule(suitableModules, (isHorizontal ? "hallwayhorizontal" : "hallwayvertical").ToIdentifier(), locationType);
if (hallwayInfo == null)
{
DebugConsole.ThrowError($"Generating hallways between outpost modules failed. No {(isHorizontal ? "horizontal" : "vertical")} hallway modules suitable for use between the modules \"{module.Info.DisplayName}\" and \"{module.PreviousModule.Info.DisplayName}\".");
@@ -1442,50 +1467,47 @@ namespace Barotrauma
if (outpost?.Info?.OutpostGenerationParams == null) { return; }
List<HumanPrefab> killedCharacters = new List<HumanPrefab>();
Dictionary<HumanPrefab, CharacterInfo> selectedCharacters = new Dictionary<HumanPrefab, CharacterInfo>();
foreach (HumanPrefab humanPrefab in outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.Server))
List<(HumanPrefab HumanPrefab, CharacterInfo CharacterInfo)> selectedCharacters
= new List<(HumanPrefab HumanPrefab, CharacterInfo CharacterInfo)>();
foreach (HumanPrefab humanPrefab in outpost.Info.OutpostGenerationParams.GetHumanPrefabs(Rand.RandSync.ServerAndClient))
{
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server);
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.ServerAndClient), randSync: Rand.RandSync.ServerAndClient);
if (location != null && location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
{
killedCharacters.Add(humanPrefab);
continue;
}
selectedCharacters.Add(humanPrefab, characterInfo);
selectedCharacters.Add((humanPrefab, characterInfo));
}
//replace killed characters with new ones
foreach (HumanPrefab killedCharacter in killedCharacters)
{
int tries = 0;
while (tries < 100)
for (int tries = 0; tries < 100; tries++)
{
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: killedCharacter.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server);
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: killedCharacter.GetJobPrefab(Rand.RandSync.ServerAndClient), randSync: Rand.RandSync.ServerAndClient);
if (!location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
{
selectedCharacters.Add(killedCharacter, characterInfo);
selectedCharacters.Add((killedCharacter, characterInfo));
break;
}
}
}
foreach (var selectedCharacter in selectedCharacters)
foreach ((var humanPrefab, var characterInfo) in selectedCharacters)
{
HumanPrefab humanPrefab = selectedCharacter.Key;
CharacterInfo characterInfo = selectedCharacter.Value;
Rand.SetSyncedSeed(ToolBox.StringToInt(characterInfo.Name));
ISpatialEntity gotoTarget = SpawnAction.GetSpawnPos(SpawnAction.SpawnLocationType.Outpost, SpawnType.Human, humanPrefab.GetModuleFlags(), humanPrefab.GetSpawnPointTags());
if (gotoTarget == null)
{
gotoTarget = outpost.GetHulls(true).GetRandom(Rand.RandSync.Server);
gotoTarget = outpost.GetHulls(true).GetRandom(Rand.RandSync.ServerAndClient);
}
characterInfo.TeamID = CharacterTeamType.FriendlyNPC;
var npc = Character.Create(CharacterPrefab.HumanConfigFile, SpawnAction.OffsetSpawnPos(gotoTarget.WorldPosition, 100.0f), ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true);
var npc = Character.Create(CharacterPrefab.HumanSpeciesName, SpawnAction.OffsetSpawnPos(gotoTarget.WorldPosition, 100.0f), ToolBox.RandomSeed(8), characterInfo, hasAi: true, createNetworkEvent: true);
npc.AnimController.FindHull(gotoTarget.WorldPosition, setSubmarine: true);
npc.TeamID = CharacterTeamType.FriendlyNPC;
npc.Prefab = humanPrefab;
npc.HumanPrefab = humanPrefab;
if (!outpost.Info.OutpostNPCs.ContainsKey(humanPrefab.Identifier))
{
outpost.Info.OutpostNPCs.Add(humanPrefab.Identifier, new List<Character>());
@@ -1499,7 +1521,7 @@ namespace Barotrauma
{
npc.AddStaticHealthMultiplier(humanPrefab.HealthMultiplier);
}
humanPrefab.GiveItems(npc, outpost, Rand.RandSync.Server);
humanPrefab.GiveItems(npc, outpost, Rand.RandSync.ServerAndClient);
foreach (Item item in npc.Inventory.FindAllItems(it => it != null, recursive: true))
{
item.AllowStealing = outpost.Info.OutpostGenerationParams.AllowStealing;
@@ -18,46 +18,46 @@ namespace Barotrauma
Bottom = 8
}
private readonly HashSet<string> moduleFlags = new HashSet<string>();
public IEnumerable<string> ModuleFlags
private readonly HashSet<Identifier> moduleFlags = new HashSet<Identifier>();
public IEnumerable<Identifier> ModuleFlags
{
get { return moduleFlags; }
}
private readonly HashSet<string> allowAttachToModules = new HashSet<string>();
public IEnumerable<string> AllowAttachToModules
private readonly HashSet<Identifier> allowAttachToModules = new HashSet<Identifier>();
public IEnumerable<Identifier> AllowAttachToModules
{
get { return allowAttachToModules; }
}
private readonly HashSet<string> allowedLocationTypes = new HashSet<string>();
public IEnumerable<string> AllowedLocationTypes
private readonly HashSet<Identifier> allowedLocationTypes = new HashSet<Identifier>();
public IEnumerable<Identifier> AllowedLocationTypes
{
get { return allowedLocationTypes; }
}
[Serialize(100, isSaveable: true, description: "How many instances of this module can be used in one outpost."), Editable]
[Serialize(100, IsPropertySaveable.Yes, description: "How many instances of this module can be used in one outpost."), Editable]
public int MaxCount { get; set; }
[Serialize(10.0f, isSaveable: true, description: "How likely it is for the module to get picked when selecting from a set of modules during the outpost generation."), Editable]
[Serialize(10.0f, IsPropertySaveable.Yes, description: "How likely it is for the module to get picked when selecting from a set of modules during the outpost generation."), Editable]
public float Commonness { get; set; }
[Serialize(GapPosition.None, isSaveable: true, description: "Which sides of the module have gaps on them (i.e. from which sides the module can be attached to other modules). Center = no gaps available.")]
[Serialize(GapPosition.None, IsPropertySaveable.Yes, description: "Which sides of the module have gaps on them (i.e. from which sides the module can be attached to other modules). Center = no gaps available.")]
public GapPosition GapPositions { get; set; }
public string Name { get; private set; }
public Dictionary<string, SerializableProperty> SerializableProperties { get; private set; }
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
public OutpostModuleInfo(SubmarineInfo submarineInfo, XElement element)
{
Name = $"OutpostModuleInfo ({submarineInfo.Name})";
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
SetFlags(
element.GetAttributeStringArray("flags", null, convertToLowerInvariant: true) ??
element.GetAttributeStringArray("moduletypes", new string[0], convertToLowerInvariant: true));
SetAllowAttachTo(element.GetAttributeStringArray("allowattachto", new string[0], convertToLowerInvariant: true));
allowedLocationTypes = new HashSet<string>(element.GetAttributeStringArray("allowedlocationtypes", new string[0], convertToLowerInvariant: true));
element.GetAttributeIdentifierArray("flags", null) ??
element.GetAttributeIdentifierArray("moduletypes", Array.Empty<Identifier>()));
SetAllowAttachTo(element.GetAttributeIdentifierArray("allowattachto", Array.Empty<Identifier>()));
allowedLocationTypes = new HashSet<Identifier>(element.GetAttributeIdentifierArray("allowedlocationtypes", Array.Empty<Identifier>()));
}
public OutpostModuleInfo(SubmarineInfo submarineInfo)
@@ -68,12 +68,12 @@ namespace Barotrauma
public OutpostModuleInfo(OutpostModuleInfo original)
{
Name = original.Name;
moduleFlags = new HashSet<string>(original.moduleFlags);
allowAttachToModules = new HashSet<string>(original.allowAttachToModules);
allowedLocationTypes = new HashSet<string>(original.allowedLocationTypes);
SerializableProperties = new Dictionary<string, SerializableProperty>();
moduleFlags = new HashSet<Identifier>(original.moduleFlags);
allowAttachToModules = new HashSet<Identifier>(original.allowAttachToModules);
allowedLocationTypes = new HashSet<Identifier>(original.allowedLocationTypes);
SerializableProperties = new Dictionary<Identifier, SerializableProperty>();
GapPositions = original.GapPositions;
foreach (KeyValuePair<string, SerializableProperty> kvp in original.SerializableProperties)
foreach (KeyValuePair<Identifier, SerializableProperty> kvp in original.SerializableProperties)
{
SerializableProperties.Add(kvp.Key, kvp.Value);
if (SerializableProperty.GetSupportedTypeName(kvp.Value.PropertyType) != null)
@@ -83,51 +83,51 @@ namespace Barotrauma
}
}
public void SetFlags(IEnumerable<string> newFlags)
public void SetFlags(IEnumerable<Identifier> newFlags)
{
moduleFlags.Clear();
if (newFlags.Contains("hallwayhorizontal"))
if (newFlags.Contains("hallwayhorizontal".ToIdentifier()))
{
moduleFlags.Add("hallwayhorizontal");
if (newFlags.Contains("ruin")) { moduleFlags.Add("ruin"); }
moduleFlags.Add("hallwayhorizontal".ToIdentifier());
if (newFlags.Contains("ruin".ToIdentifier())) { moduleFlags.Add("ruin".ToIdentifier()); }
return;
}
if (newFlags.Contains("hallwayvertical"))
if (newFlags.Contains("hallwayvertical".ToIdentifier()))
{
moduleFlags.Add("hallwayvertical");
if (newFlags.Contains("ruin")) { moduleFlags.Add("ruin"); }
moduleFlags.Add("hallwayvertical".ToIdentifier());
if (newFlags.Contains("ruin".ToIdentifier())) { moduleFlags.Add("ruin".ToIdentifier()); }
return;
}
if (!newFlags.Any())
{
moduleFlags.Add("none");
moduleFlags.Add("none".ToIdentifier());
}
foreach (string flag in newFlags)
foreach (Identifier flag in newFlags)
{
if (flag == "none" && newFlags.Count() > 1) { continue; }
moduleFlags.Add(flag.ToLowerInvariant());
moduleFlags.Add(flag);
}
}
public void SetAllowAttachTo(IEnumerable<string> allowAttachTo)
public void SetAllowAttachTo(IEnumerable<Identifier> allowAttachTo)
{
allowAttachToModules.Clear();
if (!allowAttachTo.Any())
{
allowAttachToModules.Add("any");
allowAttachToModules.Add("any".ToIdentifier());
}
foreach (string flag in allowAttachTo)
foreach (Identifier flag in allowAttachTo)
{
if (flag == "any" && allowAttachTo.Count() > 1) { continue; }
allowAttachToModules.Add(flag);
}
}
public void SetAllowedLocationTypes(IEnumerable<string> allowedLocationTypes)
public void SetAllowedLocationTypes(IEnumerable<Identifier> allowedLocationTypes)
{
this.allowedLocationTypes.Clear();
foreach (string locationType in allowedLocationTypes)
foreach (Identifier locationType in allowedLocationTypes)
{
if (locationType.Equals("any", StringComparison.OrdinalIgnoreCase)) { continue; }
if (locationType == "any") { continue; }
this.allowedLocationTypes.Add(locationType);
}
}