Client LuaCsForBarotrauma
OutpostGenerationParams.cs
2 using System;
3 using System.Collections;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
6 using System.Linq;
7 
8 namespace Barotrauma
9 {
11  {
13 
14  public virtual string Name { get; private set; }
15 
16  private readonly HashSet<Identifier> allowedLocationTypes = new HashSet<Identifier>();
17 
21  public IEnumerable<Identifier> AllowedLocationTypes
22  {
23  get { return allowedLocationTypes; }
24  }
25 
26 
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)]
29  {
30  get;
31  set;
32  }
33 
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)]
36  {
37  get;
38  set;
39  }
40 
41  [Serialize(10, IsPropertySaveable.Yes, description: "Total number of modules in the outpost."), Editable(MinValueInt = 1, MaxValueInt = 50)]
42  public int TotalModuleCount
43  {
44  get;
45  set;
46  }
47 
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]
50  {
51  get;
52  set;
53  }
54 
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)]
56  public float MinHallwayLength
57  {
58  get;
59  set;
60  }
61 
62  [Serialize(false, IsPropertySaveable.Yes, description: "Should this outpost always be destructible, regardless if damaging outposts is allowed by the server?"), Editable]
63  public bool AlwaysDestructible
64  {
65  get;
66  set;
67  }
68 
69  [Serialize(false, IsPropertySaveable.Yes, description: "Should this outpost always be rewireable, regardless if rewiring is allowed by the server?"), Editable]
70  public bool AlwaysRewireable
71  {
72  get;
73  set;
74  }
75 
76  [Serialize(false, IsPropertySaveable.Yes, description: "Should stealing from this outpost be always allowed?"), Editable]
77  public bool AllowStealing
78  {
79  get;
80  set;
81  }
82 
83  [Serialize(true, IsPropertySaveable.Yes, description: "Should the crew spawn inside the outpost (if not, they'll spawn in the submarine)."), Editable]
85  {
86  get;
87  set;
88  }
89 
90  [Serialize(true, IsPropertySaveable.Yes, description: "Should doors at the edges of an outpost module that didn't get connected to another module be locked?"), Editable]
91  public bool LockUnusedDoors
92  {
93  get;
94  set;
95  }
96 
97  [Serialize(true, IsPropertySaveable.Yes, description: "Should gaps at the edges of an outpost module that didn't get connected to another module be removed?"), Editable]
98  public bool RemoveUnusedGaps
99  {
100  get;
101  set;
102  }
103 
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]
105  public bool DrawBehindSubs
106  {
107  get;
108  set;
109  }
110 
111  [Serialize(0.0f, IsPropertySaveable.Yes, description: "Minimum amount of water in the hulls of the outpost."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
112  public float MinWaterPercentage
113  {
114  get;
115  set;
116  }
117 
118  [Serialize(0.0f, IsPropertySaveable.Yes, description: "Maximum amount of water in the hulls of the outpost."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f)]
119  public float MaxWaterPercentage
120  {
121  get;
122  set;
123  }
124 
126  {
127  get;
128  set;
129  }
130 
131  [Serialize("", IsPropertySaveable.Yes, description: "Identifier of the outpost generation parameters that should be used if this outpost has become critically irradiated."), Editable]
132  public string ReplaceInRadiation { get; set; }
133 
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]
136  {
137  get;
138  set;
139  }
140 
141  public ContentPath OutpostFilePath { get; set; }
142 
143  public class ModuleCount
144  {
146  public int Count;
147  public int Order;
148 
150 
151  public ModuleCount(ContentXElement element)
152  {
153  Identifier = element.GetAttributeIdentifier("flag", element.GetAttributeIdentifier("moduletype", ""));
154  Count = element.GetAttributeInt("count", 0);
155  Order = element.GetAttributeInt("order", 0);
156  RequiredFaction = element.GetAttributeIdentifier("requiredfaction", Identifier.Empty);
157  }
158 
159  public ModuleCount(Identifier id, int count)
160  {
161  Identifier = id;
162  Count = count;
163  RequiredFaction = Identifier.Empty;
164  }
165  }
166 
167  private readonly List<ModuleCount> moduleCounts = new List<ModuleCount>();
168 
169  public IReadOnlyList<ModuleCount> ModuleCounts
170  {
171  get { return moduleCounts; }
172  }
173 
174  private class NpcCollection : IReadOnlyList<HumanPrefab>
175  {
176  private class Entry
177  {
178  private readonly HumanPrefab humanPrefab = null;
179  private readonly Identifier setIdentifier = Identifier.Empty;
180  private readonly Identifier npcIdentifier = Identifier.Empty;
181 
182  public readonly Identifier FactionIdentifier = Identifier.Empty;
183 
184  public Entry(HumanPrefab humanPrefab, Identifier factionIdentifier)
185  {
186  this.humanPrefab = humanPrefab;
187  this.FactionIdentifier = factionIdentifier;
188  }
189 
190  public Entry(Identifier setIdentifier, Identifier npcIdentifier, Identifier factionIdentifier)
191  {
192  this.setIdentifier = setIdentifier;
193  this.npcIdentifier = npcIdentifier;
194  this.FactionIdentifier = factionIdentifier;
195  }
196 
197  public HumanPrefab HumanPrefab
198  => humanPrefab ?? NPCSet.Get(setIdentifier, npcIdentifier);
199  }
200 
201  private readonly List<Entry> entries = new List<Entry>();
202 
203  public void Add(HumanPrefab humanPrefab, Identifier factionIdentifier)
204  => entries.Add(new Entry(humanPrefab, factionIdentifier));
205 
206 
207  public void Add(Identifier setIdentifier, Identifier npcIdentifier, Identifier factionIdentifier)
208  => entries.Add(new Entry(setIdentifier, npcIdentifier, factionIdentifier));
209 
210  public IEnumerator<HumanPrefab> GetEnumerator()
211  {
212  foreach (var entry in entries)
213  {
214  if (entry == null) { continue; }
215  yield return entry.HumanPrefab;
216  }
217  }
218 
219  IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
220 
221  public IEnumerable<HumanPrefab> GetByFaction(IEnumerable<FactionPrefab> factions)
222  {
223  foreach (var entry in entries)
224  {
225  if (entry.FactionIdentifier == Identifier.Empty || factions.Any(f => f.Identifier == entry.FactionIdentifier))
226  {
227  yield return entry.HumanPrefab;
228  }
229  }
230  }
231 
232  public int Count => entries.Count;
233 
234  public HumanPrefab this[int index] => entries[index].HumanPrefab;
235  }
236 
237  private readonly ImmutableArray<NpcCollection> humanPrefabCollections;
238 
239  public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
240 
241  private ImmutableHashSet<Identifier> StoreIdentifiers { get; set; }
242 
243  #warning TODO: this shouldn't really accept any ContentFile, issue is that RuinConfigFile and OutpostConfigFile are separate derived classes
244  public OutpostGenerationParams(ContentXElement element, ContentFile file) : base(file, element.GetAttributeIdentifier("identifier", ""))
245  {
246  Name = element.GetAttributeString("name", Identifier.Value);
247  allowedLocationTypes = element.GetAttributeIdentifierArray("allowedlocationtypes", Array.Empty<Identifier>()).ToHashSet();
249 
250  if (element.GetAttribute("leveltype") != null)
251  {
252  string levelTypeStr = element.GetAttributeString("leveltype", "");
253  if (Enum.TryParse(levelTypeStr, out LevelData.LevelType parsedLevelType))
254  {
255  LevelType = parsedLevelType;
256  }
257  else
258  {
259  DebugConsole.ThrowError($"Error in outpost generation parameters \"{Identifier}\". \"{levelTypeStr}\" is not a valid level type.", contentPackage: element.ContentPackage);
260  }
261  }
262 
264 
265  var humanPrefabCollections = new List<NpcCollection>();
266  foreach (var subElement in element.Elements())
267  {
268  switch (subElement.Name.ToString().ToLowerInvariant())
269  {
270  case "modulecount":
271  moduleCounts.Add(new ModuleCount(subElement));
272  break;
273  case "npcs":
274  var newCollection = new NpcCollection();
275  foreach (var npcElement in subElement.Elements())
276  {
277  Identifier from = npcElement.GetAttributeIdentifier("from", Identifier.Empty);
278  Identifier faction = npcElement.GetAttributeIdentifier("faction", Identifier.Empty);
279  if (from != Identifier.Empty)
280  {
281  newCollection.Add(from, npcElement.GetAttributeIdentifier("identifier", Identifier.Empty), faction);
282  }
283  else
284  {
285  newCollection.Add(new HumanPrefab(npcElement, file, npcSetIdentifier: from), faction);
286  }
287  }
288  humanPrefabCollections.Add(newCollection);
289  break;
290  }
291  }
292 
293  this.humanPrefabCollections = humanPrefabCollections.ToImmutableArray();
294  }
295 
296  public int GetModuleCount(Identifier moduleFlag)
297  {
298  if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return int.MaxValue; }
299  return moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag)?.Count ?? 0;
300  }
301 
302  public void SetModuleCount(Identifier moduleFlag, int count)
303  {
304  if (moduleFlag == Identifier.Empty || moduleFlag == "none") { return; }
305  if (count <= 0)
306  {
307  moduleCounts.RemoveAll(m => m.Identifier == moduleFlag);
308  }
309  else
310  {
311  var moduleCount = moduleCounts.FirstOrDefault(m => m.Identifier == moduleFlag);
312  if (moduleCount == null)
313  {
314  moduleCounts.Add(new ModuleCount(moduleFlag, count));
315  }
316  else
317  {
318  moduleCount.Count = count;
319  }
320  }
321  }
322 
323  public void SetAllowedLocationTypes(IEnumerable<Identifier> allowedLocationTypes)
324  {
325  this.allowedLocationTypes.Clear();
326  foreach (Identifier locationType in allowedLocationTypes)
327  {
328  if (locationType == "any") { continue; }
329  this.allowedLocationTypes.Add(locationType);
330  }
331  }
332 
333  public IReadOnlyList<HumanPrefab> GetHumanPrefabs(IEnumerable<FactionPrefab> factions, Rand.RandSync randSync)
334  {
335  if (!humanPrefabCollections.Any()) { return Array.Empty<HumanPrefab>(); }
336 
337  var collection = humanPrefabCollections.GetRandom(randSync);
338  return collection.GetByFaction(factions).ToImmutableList();
339  }
340 
342  {
343  foreach (var collection in humanPrefabCollections)
344  {
345  foreach (var prefab in collection)
346  {
347  if (prefab != null && prefab.CampaignInteractionType == interactionType)
348  {
349  return true;
350  }
351  }
352  }
353  return false;
354  }
355 
356  public ImmutableHashSet<Identifier> GetStoreIdentifiers()
357  {
358  if (StoreIdentifiers == null)
359  {
360  var storeIdentifiers = new HashSet<Identifier>();
361  foreach (var collection in humanPrefabCollections)
362  {
363  foreach (var prefab in collection)
364  {
365  if (prefab?.CampaignInteractionType == CampaignMode.InteractionType.Store)
366  {
367  storeIdentifiers.Add(prefab.Identifier);
368  }
369  }
370  }
371  StoreIdentifiers = storeIdentifiers.ToImmutableHashSet();
372  }
373  return StoreIdentifiers;
374  }
375 
376  public override void Dispose() { }
377  }
378 }
Base class for content file types, which are loaded from filelist.xml via reflection....
Definition: ContentFile.cs:23
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
ContentPackage? ContentPackage
ContentPath? GetAttributeContentPath(string key)
int GetAttributeInt(string key, int def)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
OutpostGenerationParams(ContentXElement element, ContentFile file)
IReadOnlyList< HumanPrefab > GetHumanPrefabs(IEnumerable< FactionPrefab > factions, Rand.RandSync randSync)
static readonly PrefabCollection< OutpostGenerationParams > OutpostParams
bool CanHaveCampaignInteraction(CampaignMode.InteractionType interactionType)
IReadOnlyList< ModuleCount > ModuleCounts
void SetModuleCount(Identifier moduleFlag, int count)
void SetAllowedLocationTypes(IEnumerable< Identifier > allowedLocationTypes)
ImmutableHashSet< Identifier > GetStoreIdentifiers()
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
Definition: Prefab.cs:34
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)