3 using System.Collections;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
14 public virtual string Name {
get;
private set; }
16 private readonly HashSet<Identifier> allowedLocationTypes =
new HashSet<Identifier>();
23 get {
return allowedLocationTypes; }
27 [
Serialize(-1,
IsPropertySaveable.Yes, description:
"Should this type of outpost be forced to the locations at the end of the campaign map? 0 = first end level, 1 = second end level, and so on."),
Editable(MinValueInt = -1, MaxValueInt = 10)]
34 [
Serialize(-1,
IsPropertySaveable.Yes, description:
"The closer to the current level difficulty this value is, the higher the probability of choosing these generation params are. Defaults to -1, which means we use the current difficulty."),
Editable(MinValueInt = 1, MaxValueInt = 50)]
48 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"Should the generator append generic (module flag \"none\") modules to the outpost to reach the total module count."),
Editable]
55 [
Serialize(200.0f,
IsPropertySaveable.Yes, description:
"Minimum length of the hallways between modules. If 0, the generator will place the modules directly against each other assuming it can be done without making any modules overlap."),
Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
104 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"Should the whole outpost render behind submarines? Only set this to true if the submarine is intended to go inside the outpost."),
Editable]
131 [
Serialize(
"",
IsPropertySaveable.Yes, description:
"Identifier of the outpost generation parameters that should be used if this outpost has become critically irradiated."),
Editable]
134 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"By default, sonar only shows the outline of the sub/outpost from the outside. Enable this if you want to see each structure individually."),
Editable]
153 [
Serialize(0,
IsPropertySaveable.Yes, description:
"Can be used to enforce the modules to be placed in a specific order, starting from the docking module (0 = first, 1 = second, etc)."),
Editable]
162 [
Serialize(1.0f,
IsPropertySaveable.Yes, description:
"Probability for this type of module to be included in the outpost."),
Editable(MinValueFloat = 0, MaxValueFloat = 1)]
187 private readonly List<ModuleCount> moduleCounts =
new List<ModuleCount>();
191 get {
return moduleCounts; }
194 private class NpcCollection : IReadOnlyList<HumanPrefab>
206 this.humanPrefab = humanPrefab;
207 this.FactionIdentifier = factionIdentifier;
212 this.setIdentifier = setIdentifier;
213 this.npcIdentifier = npcIdentifier;
214 this.FactionIdentifier = factionIdentifier;
217 public HumanPrefab HumanPrefab
218 => humanPrefab ?? NPCSet.Get(setIdentifier, npcIdentifier);
221 private readonly List<Entry> entries =
new List<Entry>();
223 public void Add(HumanPrefab humanPrefab,
Identifier factionIdentifier)
224 => entries.Add(
new Entry(humanPrefab, factionIdentifier));
228 => entries.Add(
new Entry(setIdentifier, npcIdentifier, factionIdentifier));
230 public IEnumerator<HumanPrefab> GetEnumerator()
232 foreach (var entry
in entries)
234 if (entry ==
null) {
continue; }
235 yield
return entry.HumanPrefab;
239 IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
241 public IEnumerable<HumanPrefab> GetByFaction(IEnumerable<FactionPrefab> factions)
243 foreach (var entry
in entries)
245 if (entry.FactionIdentifier ==
Identifier.Empty || factions.Any(f => f.Identifier == entry.FactionIdentifier))
247 yield
return entry.HumanPrefab;
252 public int Count => entries.Count;
254 public HumanPrefab
this[
int index] => entries[index].HumanPrefab;
257 private readonly ImmutableArray<NpcCollection> humanPrefabCollections;
261 private ImmutableHashSet<Identifier> StoreIdentifiers {
get;
set; }
263 #warning TODO: this shouldn't really accept any ContentFile, issue is that RuinConfigFile and OutpostConfigFile are separate derived classes
279 DebugConsole.ThrowError($
"Error in outpost generation parameters \"{Identifier}\". \"{levelTypeStr}\" is not a valid level type.", contentPackage: element.
ContentPackage);
286 var humanPrefabCollections =
new List<NpcCollection>();
287 foreach (var subElement
in element.Elements())
289 switch (subElement.Name.ToString().ToLowerInvariant())
293 if (moduleCounts.None() && newModuleCount.Probability < 1.0f)
295 DebugConsole.AddWarning(
296 $
"Potential error in outpost generation parameters \"{Identifier}\"." +
297 $
" The first module is set to spawn with a probability of {newModuleCount.Probability}%. The first module must always spawn, so the probability will be ignored.",
299 newModuleCount.Probability = 1.0f;
301 else if (newModuleCount.Probability <= 0.0f)
303 DebugConsole.AddWarning(
304 $
"Potential error in outpost generation parameters \"{Identifier}\"." +
305 $
" Probability of the module {newModuleCount.Identifier} is 0% (the module should never spawn, so there's no reason to include it in the generation parameters.",
308 moduleCounts.Add(newModuleCount);
311 var newCollection =
new NpcCollection();
312 foreach (var npcElement
in subElement.Elements())
318 newCollection.Add(from, npcElement.GetAttributeIdentifier(
"identifier",
Identifier.Empty), faction);
322 newCollection.Add(
new HumanPrefab(npcElement, file, npcSetIdentifier: from), faction);
325 humanPrefabCollections.Add(newCollection);
330 this.humanPrefabCollections = humanPrefabCollections.ToImmutableArray();
335 if (moduleFlag ==
Identifier.Empty || moduleFlag ==
"none") {
return int.MaxValue; }
336 return moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag)?.Count ?? 0;
339 public void SetModuleCount(
Identifier moduleFlag,
int count,
float? probability =
null,
float? minDifficulty =
null,
float? maxDifficulty =
null)
341 if (moduleFlag ==
Identifier.Empty || moduleFlag ==
"none") {
return; }
344 moduleCounts.RemoveAll(m => m.Identifier == moduleFlag);
348 var moduleCount = moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag);
349 if (moduleCount ==
null)
352 if (moduleCount.Probability <= 0.0f)
354 DebugConsole.AddWarning(
355 $
"Potential error in outpost generation parameters \"{Identifier}\"."+
356 $
" Probability of the module {moduleCount.Identifier} is 0 (the module should never spawn, so there's no reason to include it in the generation parameters.",
359 moduleCounts.Add(moduleCount);
361 moduleCount.Count = count;
362 if (probability.HasValue) { moduleCount.Probability = probability.Value; }
363 if (minDifficulty.HasValue) { moduleCount.MinDifficulty = minDifficulty.Value; }
364 if (maxDifficulty.HasValue) { moduleCount.MaxDifficulty = maxDifficulty.Value; }
370 this.allowedLocationTypes.Clear();
371 foreach (
Identifier locationType
in allowedLocationTypes)
373 if (locationType ==
"any") {
continue; }
374 this.allowedLocationTypes.Add(locationType);
380 if (!humanPrefabCollections.Any()) {
return Array.Empty<
HumanPrefab>(); }
382 var collection = humanPrefabCollections.GetRandom(randSync);
384 .GetByFaction(factions)
385 .Where(humanPrefab => !humanPrefab.RequireSpawnPointTag ||
WayPoint.
WayPointList.Any(wp => wp.Submarine == sub && humanPrefab.GetSpawnPointTags().Any(tag => wp.Tags.Contains(tag))))
391 foreach (var collection
in humanPrefabCollections)
393 foreach (var prefab
in collection)
395 if (prefab !=
null && prefab.CampaignInteractionType == interactionType)
406 if (StoreIdentifiers ==
null)
408 var storeIdentifiers =
new HashSet<Identifier>();
409 foreach (var collection
in humanPrefabCollections)
411 foreach (var prefab
in collection)
415 storeIdentifiers.Add(prefab.Identifier);
419 StoreIdentifiers = storeIdentifiers.ToImmutableHashSet();
421 return StoreIdentifiers;
Base class for content file types, which are loaded from filelist.xml via reflection....
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
ContentPackage? ContentPackage
ContentPath? GetAttributeContentPath(string key)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
ModuleCount(Identifier id, int count)
Dictionary< Identifier, SerializableProperty > SerializableProperties
ModuleCount(ContentXElement element)
Identifier RequiredFaction
OutpostGenerationParams(ContentXElement element, ContentFile file)
ContentPath OutpostFilePath
static readonly PrefabCollection< OutpostGenerationParams > OutpostParams
string ReplaceInRadiation
int ForceToEndLocationIndex
bool AlwaysShowStructuresOnSonar
bool CanHaveCampaignInteraction(CampaignMode.InteractionType interactionType)
bool AppendToReachTotalModuleCount
IReadOnlyList< ModuleCount > ModuleCounts
void SetAllowedLocationTypes(IEnumerable< Identifier > allowedLocationTypes)
IReadOnlyList< HumanPrefab > GetHumanPrefabs(IEnumerable< FactionPrefab > factions, Submarine sub, Rand.RandSync randSync)
LevelData.? LevelType LevelType
bool SpawnCrewInsideOutpost
ImmutableHashSet< Identifier > GetStoreIdentifiers()
void SetModuleCount(Identifier moduleFlag, int count, float? probability=null, float? minDifficulty=null, float? maxDifficulty=null)
int GetModuleCount(Identifier moduleFlag)
Dictionary< Identifier, SerializableProperty > SerializableProperties
IEnumerable< Identifier > AllowedLocationTypes
Identifiers of the location types this outpost can appear in. If empty, can appear in all types of lo...
readonly Identifier Identifier
Prefab that has a property serves as a deterministic hash of a prefab's identifier....
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
static List< WayPoint > WayPointList