Server LuaCsForBarotrauma
LevelObjectPrefab.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Linq;
6 using System.Xml.Linq;
7 
8 namespace Barotrauma
9 {
11  {
13 
14  public class ChildObject
15  {
16  public List<string> AllowedNames;
17  public int MinCount, MaxCount;
18 
19  public ChildObject()
20  {
21  AllowedNames = new List<string>();
22  MinCount = 1;
23  MaxCount = 1;
24  }
25 
26  public ChildObject(XElement element)
27  {
28  AllowedNames = element.GetAttributeStringArray("names", Array.Empty<string>()).ToList();
29  MinCount = element.GetAttributeInt("mincount", 1);
30  MaxCount = Math.Max(element.GetAttributeInt("maxcount", 1), MinCount);
31  }
32  }
33 
34  [Flags]
35  public enum SpawnPosType
36  {
37  None = 0,
38  MainPathWall = 1,
39  SidePathWall = 2,
40  CaveWall = 4,
41  NestWall = 8,
42  RuinWall = 16,
43  SeaFloor = 32,
44  MainPath = 64,
45  LevelStart = 128,
46  LevelEnd = 256,
47  OutpostWall = 512,
48  Wall = MainPathWall | SidePathWall | CaveWall,
49  }
50 
51  public List<Sprite> Sprites
52  {
53  get;
54  private set;
55  } = new List<Sprite>();
56 
58  {
59  get;
60  private set;
61  }
62 
63  [Serialize(1.0f, IsPropertySaveable.No), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
64  public float MinSize
65  {
66  get;
67  private set;
68  }
69  [Serialize(1.0f, IsPropertySaveable.No), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
70  public float MaxSize
71  {
72  get;
73  private set;
74  }
75 
79  [Serialize((Alignment.Top | Alignment.Bottom | Alignment.Left | Alignment.Right), IsPropertySaveable.Yes, description: "Which sides of a wall the object can spawn on."), Editable]
81  {
82  get;
83  private set;
84  }
85 
88  {
89  get;
90  private set;
91  }
92 
93  public XElement Config
94  {
95  get;
96  private set;
97  }
98 
99  public readonly List<ContentXElement> LevelTriggerElements;
100 
105  public readonly Dictionary<Identifier, float> OverrideCommonness;
106 
107  public XElement PhysicsBodyElement
108  {
109  get;
110  private set;
111  }
113  {
114  get;
115  private set;
116  } = -1;
117 
118  public Dictionary<Sprite, XElement> SpriteSpecificPhysicsBodyElements
119  {
120  get;
121  private set;
122  } = new Dictionary<Sprite, XElement>();
123 
124 
125  [Serialize(10000, IsPropertySaveable.No, description: "Maximum number of this specific object per level."), Editable(MinValueFloat = 0.01f, MaxValueFloat = 10.0f)]
126  public int MaxCount
127  {
128  get;
129  private set;
130  }
131 
132  [Serialize("0.0,1.0", IsPropertySaveable.Yes, description: "The sprite depth of the object (min, max). Values of 0 or less make the object render in front of walls, values larger than 0 make it render behind walls with a parallax effect."), Editable]
133  public Vector2 DepthRange
134  {
135  get;
136  private set;
137  }
138 
139  [Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f),
140  Serialize(0.0f, IsPropertySaveable.Yes, description: "The tendency for the prefab to form clusters. Used as an exponent for perlin noise values that are used to determine the probability for an object to spawn at a specific position.")]
145  public float ClusteringAmount
146  {
147  get;
148  private set;
149  }
150 
151  [Editable(MinValueFloat = 0.0f, MaxValueFloat = 1.0f),
152  Serialize(0.0f, IsPropertySaveable.Yes, description: "A value between 0-1 that determines the z-coordinate to sample perlin noise from when determining the probability " +
153  " for an object to spawn at a specific position. Using the same (or close) value for different objects means the objects tend " +
154  "to form clusters in the same areas.")]
161  public float ClusteringGroup
162  {
163  get;
164  private set;
165  }
166 
167  [Editable, Serialize("0,0", IsPropertySaveable.Yes, description: "Random offset from the surface the object spawns on.")]
168  public Vector2 RandomOffset
169  {
170  get;
171  private set;
172  }
173 
174  [Editable, Serialize(false, IsPropertySaveable.Yes, description: "Should the object be rotated to align it with the wall surface it spawns on.")]
175  public bool AlignWithSurface
176  {
177  get;
178  private set;
179  }
180 
181  [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the object be placed near the start of the level.")]
182  public bool AllowAtStart
183  {
184  get;
185  private set;
186  }
187 
188  [Editable, Serialize(true, IsPropertySaveable.Yes, description: "Can the object be placed near the end of the level.")]
189  public bool AllowAtEnd
190  {
191  get;
192  private set;
193  }
194 
195  [Serialize(0.0f, IsPropertySaveable.Yes, description: "Minimum length of a graph edge the object can spawn on."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
199  public float MinSurfaceWidth
200  {
201  get;
202  private set;
203  }
204 
205  private Vector2 randomRotation;
206  [Editable, Serialize("0.0,0.0", IsPropertySaveable.Yes, description: "How much the rotation of the object can vary (min and max values in degrees).")]
207  public Vector2 RandomRotation
208  {
209  get { return new Vector2(MathHelper.ToDegrees(randomRotation.X), MathHelper.ToDegrees(randomRotation.Y)); }
210  private set
211  {
212  randomRotation = new Vector2(MathHelper.ToRadians(value.X), MathHelper.ToRadians(value.Y));
213  }
214  }
215 
216  public Vector2 RandomRotationRad => randomRotation;
217 
218  private float swingAmount;
219  [Serialize(0.0f, IsPropertySaveable.Yes, description: "How much the object swings (in degrees)."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 360.0f)]
220  public float SwingAmount
221  {
222  get { return MathHelper.ToDegrees(swingAmount); }
223  private set
224  {
225  swingAmount = MathHelper.ToRadians(value);
226  }
227  }
228 
229  public float SwingAmountRad => swingAmount;
230 
231  [Serialize(0.0f, IsPropertySaveable.Yes, description: "How fast the object swings."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
232  public float SwingFrequency
233  {
234  get;
235  private set;
236  }
237 
238  [Editable, Serialize("0.0,0.0", IsPropertySaveable.Yes, description: "How much the scale of the object oscillates on each axis. A value of 0.5,0.5 would make the object's scale oscillate from 100% to 150%.")]
239  public Vector2 ScaleOscillation
240  {
241  get;
242  private set;
243  }
244 
245  [Serialize(0.0f, IsPropertySaveable.Yes, description: "How fast the object's scale oscillates."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
247  {
248  get;
249  private set;
250  }
251 
252  [Editable, Serialize(1.0f, IsPropertySaveable.Yes, description: "How likely it is for the object to spawn in a level. " +
253  "This is relative to the commonness of the other objects - for example, having an object with " +
254  "a commonness of 1 and another with a commonness of 10 would mean the latter appears in levels 10 times as frequently as the former. " +
255  "The commonness value can be overridden on specific level types.")]
256  public float Commonness
257  {
258  get;
259  private set;
260  }
261 
262  [Serialize(0.0f, IsPropertySaveable.Yes, description: "How much the object disrupts submarine's sonar."), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10.0f)]
263  public float SonarDisruption
264  {
265  get;
266  private set;
267  }
268 
269  [Serialize(false, IsPropertySaveable.Yes, description: "Can the object take damage from weapons/attacks that damage level walls."), Editable]
271  {
272  get;
273  private set;
274  }
275 
276  [Serialize(false, IsPropertySaveable.Yes, description: "Should the object disappear if the object is destroyed? Only relevant if TakeLevelWallDamage is true."), Editable]
277  public bool HideWhenBroken
278  {
279  get;
280  private set;
281  }
282 
283  [Serialize(100.0f, IsPropertySaveable.Yes, description: "Amount of health the object has. Only relevant if TakeLevelWallDamage is true."), Editable]
284  public float Health
285  {
286  get;
287  private set;
288  }
289 
290  [Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes), Editable]
291  public Color SpriteColor
292  {
293  get;
294  private set;
295  }
296 
297  public string Name => Identifier.Value;
298 
299  public List<ChildObject> ChildObjects
300  {
301  get;
302  private set;
303  }
304 
305  public Dictionary<Identifier, SerializableProperty> SerializableProperties
306  {
307  get; private set;
308  }
309 
314  public List<LevelObjectPrefab> OverrideProperties
315  {
316  get;
317  private set;
318  }
319 
320  public override string ToString()
321  {
322  return "LevelObjectPrefab (" + Identifier + ")";
323  }
324 
325 
326  public LevelObjectPrefab(ContentXElement element, LevelObjectPrefabsFile file, Identifier identifierOverride = default) : base(file, ParseIdentifier(identifierOverride, element))
327  {
328  ChildObjects = new List<ChildObject>();
329  LevelTriggerElements = new List<ContentXElement>();
330  OverrideProperties = new List<LevelObjectPrefab>();
331  OverrideCommonness = new Dictionary<Identifier, float>();
332 
334  if (element != null)
335  {
336  Config = element;
337 
338  LoadElements(file, element, -1);
339  InitProjSpecific(element);
340  }
341 
342  //use the maximum width of the sprite as the minimum surface width if no value is given
343  if (element != null && !element.Attributes("minsurfacewidth").Any())
344  {
345  if (Sprites.Any()) MinSurfaceWidth = Sprites[0].size.X * MaxSize;
347  }
348  }
349 
350  public static Identifier ParseIdentifier(Identifier identifierOverride, XElement element)
351  {
352  if (!identifierOverride.IsEmpty) { return identifierOverride; }
353  Identifier identifier = element.GetAttributeIdentifier("identifier", "");
354  if (identifier.IsEmpty)
355  {
356 #if DEBUG
357  DebugConsole.ThrowError($"Level object prefab \"{element.Name}\" has no identifier! Using the name as the identifier instead...");
358 #else
359  DebugConsole.AddWarning($"Level object prefab \"{element.Name}\" has no identifier! Using the name as the identifier instead...");
360 #endif
361  identifier = element.NameAsIdentifier();
362  }
363  return identifier;
364  }
365 
366  private void LoadElements(LevelObjectPrefabsFile file, ContentXElement element, int parentTriggerIndex)
367  {
368  int propertyOverrideCount = 0;
369  //load sprites first, OverrideProperties may need them (defaulting to the default sprite if no override is defined)
370  foreach (var subElement in element.Elements())
371  {
372  switch (subElement.Name.ToString().ToLowerInvariant())
373  {
374  case "sprite":
375  var newSprite = new Sprite(subElement, lazyLoad: true);
376  Sprites.Add(newSprite);
377  var spriteSpecificPhysicsBodyElement =
378  subElement.GetChildElement("PhysicsBody") ?? subElement.GetChildElement("Body") ??
379  subElement.GetChildElement("physicsbody") ?? subElement.GetChildElement("body");
380  if (spriteSpecificPhysicsBodyElement != null)
381  {
382  SpriteSpecificPhysicsBodyElements.Add(newSprite, spriteSpecificPhysicsBodyElement);
383  }
384  break;
385  case "deformablesprite":
386  DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
387  break;
388  }
389  }
390  foreach (var subElement in element.Elements())
391  {
392  switch (subElement.Name.ToString().ToLowerInvariant())
393  {
394  case "overridecommonness":
395  Identifier levelType = subElement.GetAttributeIdentifier("leveltype", Identifier.Empty);
396  if (!OverrideCommonness.ContainsKey(levelType))
397  {
398  OverrideCommonness.Add(levelType, subElement.GetAttributeFloat("commonness", 1.0f));
399  }
400  break;
401  case "leveltrigger":
402  case "trigger":
403  OverrideProperties.Add(null);
404  LevelTriggerElements.Add(subElement);
405  LoadElements(file, subElement, LevelTriggerElements.Count - 1);
406  break;
407  case "childobject":
408  ChildObjects.Add(new ChildObject(subElement));
409  break;
410  case "overrideproperties":
411  var propertyOverride = new LevelObjectPrefab(subElement, file, identifierOverride: $"{Identifier}-{propertyOverrideCount}".ToIdentifier());
412  OverrideProperties[OverrideProperties.Count - 1] = propertyOverride;
413  if (!propertyOverride.Sprites.Any() && propertyOverride.DeformableSprite == null)
414  {
415  propertyOverride.Sprites = Sprites;
416  propertyOverride.DeformableSprite = DeformableSprite;
417  }
418  propertyOverrideCount++;
419  break;
420  case "body":
421  case "physicsbody":
422  PhysicsBodyElement = subElement;
423  PhysicsBodyTriggerIndex = parentTriggerIndex;
424  break;
425  }
426  }
427  }
428 
429  partial void InitProjSpecific(ContentXElement element);
430 
431 
432  public float GetCommonness(CaveGenerationParams generationParams, bool requireCaveSpecificOverride = true)
433  {
434  if (generationParams != null &&
435  generationParams.Identifier != Identifier.Empty &&
436  OverrideCommonness.TryGetValue(generationParams.Identifier, out float commonness))
437  {
438  return commonness;
439  }
440  return requireCaveSpecificOverride ? 0.0f : Commonness;
441  }
442 
443  public float GetCommonness(LevelData levelData)
444  {
445  if (levelData.GenerationParams != null && levelData.GenerationParams.Identifier != Identifier.Empty &&
446  OverrideCommonness.TryGetValue(levelData.GenerationParams.Identifier, out float commonness) ||
447  (!levelData.GenerationParams.OldIdentifier.IsEmpty && OverrideCommonness.TryGetValue(levelData.GenerationParams.OldIdentifier, out commonness)))
448  {
449  return commonness;
450  }
451  if (levelData?.Biome != null)
452  {
453  if (OverrideCommonness.TryGetValue(levelData.Biome.Identifier, out float biomeCommonness))
454  {
455  return biomeCommonness;
456  }
457  }
458  return Commonness;
459  }
460 
461  public override void Dispose() { }
462  }
463 }
IEnumerable< XAttribute > Attributes()
IEnumerable< ContentXElement > Elements()
LevelGenerationParams GenerationParams
Definition: LevelData.cs:28
readonly Biome Biome
Definition: LevelData.cs:26
List< LevelObjectPrefab > OverrideProperties
A list of prefabs whose properties override this one's properties when a trigger is active....
Dictionary< Identifier, SerializableProperty > SerializableProperties
LevelObjectPrefab(ContentXElement element, LevelObjectPrefabsFile file, Identifier identifierOverride=default)
static readonly PrefabCollection< LevelObjectPrefab > Prefabs
float GetCommonness(CaveGenerationParams generationParams, bool requireCaveSpecificOverride=true)
readonly List< ContentXElement > LevelTriggerElements
float ClusteringGroup
A value between 0-1 that determines the z-coordinate to sample perlin noise from when determining the...
Dictionary< Sprite, XElement > SpriteSpecificPhysicsBodyElements
float ClusteringAmount
The tendency for the prefab to form clusters. Used as an exponent for perlin noise values that are us...
readonly Dictionary< Identifier, float > OverrideCommonness
Overrides the commonness of the object in a specific level type. Key = name of the level type,...
List< ChildObject > ChildObjects
float MinSurfaceWidth
Minimum length of a graph edge the object can spawn on.
static Identifier ParseIdentifier(Identifier identifierOverride, XElement element)
Alignment Alignment
Which sides of a wall the object can appear on.
float GetCommonness(LevelData levelData)
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)