Client LuaCsForBarotrauma
CharacterParams.cs
1 using Microsoft.Xna.Framework;
2 using System;
3 using System.Collections.Generic;
4 using System.Xml.Linq;
5 using System.Xml;
6 using System.Linq;
8 using System.Collections.Immutable;
9 #if CLIENT
10 using SoundType = Barotrauma.CharacterSound.SoundType;
11 #endif
12 
13 namespace Barotrauma
14 {
19  {
21  public Identifier SpeciesName { get; private set; }
22 
23  [Serialize("", IsPropertySaveable.Yes, description: "References to another species. Define only if the creature is a variant that needs to use a pre-existing translation."), Editable]
24  public Identifier SpeciesTranslationOverride { get; private set; }
25 
26  [Serialize("", IsPropertySaveable.Yes, description: "Overrides the name of the character, shown to the player. If the display name is not defined, the game first tries to find the translated name. If that is not found, the species name will be used."), Editable]
27  public string DisplayName { get; private set; }
28 
29  [Serialize("", IsPropertySaveable.Yes, description: "If defined, different species of the same group consider each other friendly and do not attack each other."), Editable]
30  public Identifier Group { get; private set; }
31 
32  [Serialize(false, IsPropertySaveable.Yes, description: "If enabled, the character is a humanoid and has different animation constraints relative to non-humanoid characters."), Editable(ReadOnly = true)]
33  public bool Humanoid { get; private set; }
34 
35  [Serialize(false, IsPropertySaveable.Yes, description: "If enabled, jobs can be assigned to characters of this species. Should be true for the player characters."), Editable(ReadOnly = true)]
36  public bool HasInfo { get; private set; }
37 
38  [Serialize(false, IsPropertySaveable.Yes, description: "Can the creature interact with items?"), Editable]
39  public bool CanInteract { get; private set; }
40 
41  [Serialize(false, IsPropertySaveable.Yes, description: "Should this character be treated as a husk?"), Editable]
42  public bool Husk { get; private set; }
43 
44  [Serialize(false, IsPropertySaveable.Yes, description:"Should this character use a special husk appendage, attached to the ragdoll, when it turns into a husk?"), Editable]
45  public bool UseHuskAppendage { get; private set; }
46 
47  [Serialize(false, IsPropertySaveable.Yes, description: "Does this character need oxygen to survive? Enabling this also makes the character vulnerable to high pressure when swimming outside of the submarine."), Editable]
48  public bool NeedsAir { get; set; }
49 
50  [Serialize(false, IsPropertySaveable.Yes, description: "Can the creature live without water or does it die on dry land?"), Editable]
51  public bool NeedsWater { get; set; }
52 
53  [Serialize(false, IsPropertySaveable.Yes, description: "Note: non-humans with a human AI aren't fully supported. Enabling this on a non-human character may lead to issues.")]
54  public bool UseHumanAI { get; set; }
55 
56  [Serialize(false, IsPropertySaveable.Yes, description: "Is this creature an artificial creature, like robot or machine that shouldn't be affected by afflictions that affect only organic creatures? Overrides DoesBleed."), Editable]
57  public bool IsMachine { get; set; }
58 
59  [Serialize(false, IsPropertySaveable.No, description:"Is the character able to send messages in the chat?"), Editable]
60  public bool CanSpeak { get; set; }
61 
62  [Serialize(true, IsPropertySaveable.Yes, description:"Is there a health bar shown above the character when it takes damage? Defaults to true."), Editable]
63  public bool ShowHealthBar { get; private set; }
64 
65  [Serialize(false, IsPropertySaveable.Yes, description: "Is this character's health shown at the top of the player's screen when they are in an active encounter?"), Editable]
66  public bool UseBossHealthBar { get; private set; }
67 
68  [Serialize(100f, IsPropertySaveable.Yes, description: "How much noise the character makes when moving?"), Editable(minValue: 0f, maxValue: 100000f)]
69  public float Noise { get; set; }
70 
71  [Serialize(100f, IsPropertySaveable.Yes, description: "How visible the character is?"), Editable(minValue: 0f, maxValue: 100000f)]
72  public float Visibility { get; set; }
73 
74  [Serialize("blood", IsPropertySaveable.Yes), Editable]
75  public string BloodDecal { get; private set; }
76 
77  [Serialize("blooddrop", IsPropertySaveable.Yes), Editable]
78  public string BleedParticleAir { get; private set; }
79 
80  [Serialize("waterblood", IsPropertySaveable.Yes), Editable]
81  public string BleedParticleWater { get; private set; }
82 
83  [Serialize(1f, IsPropertySaveable.Yes, description: "A multiplier to increase or decrease the number of bleeding particles to create."), Editable]
84  public float BleedParticleMultiplier { get; private set; }
85 
86  [Serialize(true, IsPropertySaveable.Yes, description: "Can the creature eat bodies? Used by player controlled creatures to allow them to eat. Currently applicable only to non-humanoids. To allow an AI controller to eat, just add an ai target with the state \"eat\""), Editable]
87  public bool CanEat { get; set; }
88 
89  [Serialize(10f, IsPropertySaveable.Yes, description: "How effectively/easily the character eats other characters. Affects the forces, the amount of particles, and the time required before the target is eaten away"), Editable(MinValueFloat = 1, MaxValueFloat = 1000, ValueStep = 1)]
90  public float EatingSpeed { get; set; }
91 
92  [Serialize(true, IsPropertySaveable.Yes, description: "Should the character AI use waypoints defined in the level to find a path to its targets?"), Editable]
93  public bool UsePathFinding { get; set; }
94 
95  [Serialize(1f, IsPropertySaveable.Yes, "Decreases the intensive path finding call frequency. Set to a lower value for insignificant creatures to improve performance."), Editable(minValue: 0f, maxValue: 1f)]
96  public float PathFinderPriority { get; set; }
97 
98  [Serialize(false, IsPropertySaveable.Yes, description: "Should the character be hidden in the sonar?"), Editable]
99  public bool HideInSonar { get; set; }
100 
101  [Serialize(false, IsPropertySaveable.Yes, description: "Should the character be hidden when using thermal goggles?"), Editable]
102  public bool HideInThermalGoggles { get; set; }
103 
104  [Serialize(0f, IsPropertySaveable.Yes, description: "If set to a value greater than zero, this character creates disrupting noise on the sonar when within range."), Editable]
105  public float SonarDisruption { get; set; }
106 
107  [Serialize(0f, IsPropertySaveable.Yes, description: "Range at which \"long distance\" blips for this character will appear on the sonar (used on some of the Abyss monsters)."), Editable]
108  public float DistantSonarRange { get; set; }
109 
110  [Serialize(25000f, IsPropertySaveable.Yes, "If the character is farther than this (in pixels) from the sub and the players, it will be disabled. The halved value is used for triggering simple physics where the ragdoll is disabled and only the main collider is updated."), Editable(MinValueFloat = 10000f, MaxValueFloat = 100000f)]
111  public float DisableDistance { get; set; }
112 
113  [Serialize(10f, IsPropertySaveable.Yes, "How frequent the recurring idle and attack sounds are?"), Editable(MinValueFloat = 1f, MaxValueFloat = 100f)]
114  public float SoundInterval { get; set; }
115 
116  [Serialize(false, IsPropertySaveable.Yes, description: "Should the character be drawn on top of characters that do not have this set? This currently has no effect if the character has no deformable sprites."), Editable]
117  public bool DrawLast { get; set; }
118 
119  [Serialize(1.0f, IsPropertySaveable.Yes, "Tells the bots how much they should prefer targeting this character with submarine weapons. Defaults to 1. Set 0 to tell the bots not to target this character at all. Distance to the target affects the decision making."), Editable]
120  public float AITurretPriority { get; set; }
121 
122  [Serialize(1.0f, IsPropertySaveable.Yes, "Tells the bots how much they should prefer targeting this character with submarine weapons tagged as \"slowturret\", like railguns. The tag is arbitrary and can be added to any turrets, just like the priority. Defaults to 1. Not used if AITurretPriority is 0. Distance to the target affects the decision making."), Editable]
123  public float AISlowTurretPriority { get; set; }
124 
125  [Serialize("", IsPropertySaveable.Yes, description: "Identifier or tag of the item the character's items are placed inside when the character despawns."), Editable]
126  public Identifier DespawnContainer { get; private set; }
127 
128  public readonly CharacterFile File;
129 
130  public XDocument VariantFile { get; private set; }
131 
132  public readonly List<SubParam> SubParams = new List<SubParam>();
133  public readonly List<SoundParams> Sounds = new List<SoundParams>();
134  public readonly List<ParticleParams> BloodEmitters = new List<ParticleParams>();
135  public readonly List<ParticleParams> GibEmitters = new List<ParticleParams>();
136  public readonly List<ParticleParams> DamageEmitters = new List<ParticleParams>();
137  public readonly List<InventoryParams> Inventories = new List<InventoryParams>();
138  public HealthParams Health { get; private set; }
145  public AIParams AI { get; private set; }
146 
148  {
149  File = file;
150  Load();
151  }
152 
153  protected override string GetName() => "Character Config File";
154 
155  public override ContentXElement MainElement
156  {
157  get
158  {
159  if (base.MainElement == null) { return null; }
160  return base.MainElement.IsOverride() ? base.MainElement.FirstElement() : base.MainElement;
161  }
162  }
163 
164  public static XElement CreateVariantXml(XElement variantXML, XElement baseXML)
165  {
166  XElement newXml = variantXML.CreateVariantXML(baseXML);
167  XElement variantAi = variantXML.GetChildElement("ai");
168  XElement baseAi = baseXML.GetChildElement("ai");
169  if (baseAi is null || baseAi.Elements().None()
170  || variantAi is null || variantAi.Elements().None())
171  {
172  return newXml;
173  }
174  // CreateVariantXML seems to merge the ai targets so that in the new xml we have both the old and the new target definitions.
175  var finalAiElement = newXml.GetChildElement("ai");
176  var processedTags = new HashSet<string>();
177  foreach (var aiTarget in finalAiElement.Elements().ToArray())
178  {
179  string tag = aiTarget.GetAttributeString("tag", null);
180  if (tag == null) { continue; }
181  if (processedTags.Contains(tag))
182  {
183  aiTarget.Remove();
184  continue;
185  }
186  processedTags.Add(tag);
187  var matchInSelf = variantAi.Elements().FirstOrDefault(e => e.GetAttributeString("tag", null) == tag);
188  var matchInParent = baseAi.Elements().FirstOrDefault(e => e.GetAttributeString("tag", null) == tag);
189  if (matchInSelf != null && matchInParent != null)
190  {
191  aiTarget.ReplaceWith(new XElement(matchInSelf));
192  }
193  }
194  return newXml;
195  }
196 
197  public bool Load()
198  {
199  UpdatePath(File.Path);
200  doc = XMLExtensions.TryLoadXml(Path);
201  if (MainElement == null)
202  {
203  DebugConsole.ThrowError("Main element null! Failed to load character params.");
204  return false;
205  }
206  Identifier variantOf = MainElement.VariantOf();
207  if (!variantOf.IsEmpty)
208  {
209  VariantFile = new XDocument(doc);
210  #warning TODO: determine that CreateVariantXML is equipped to do this
212  var oldElement = MainElement;
213  var parentElement = (XContainer)oldElement.Parent ?? doc; oldElement.Remove(); parentElement.Add(newRoot);
214  }
216  OriginalElement = new XElement(MainElement).FromPackage(Path.ContentPackage);
217  if (SpeciesName.IsEmpty && MainElement != null)
218  {
219  //backwards compatibility
221  }
222  CreateSubParams();
223  return IsLoaded;
224  }
225 
226  public bool Save(string fileNameWithoutExtension = null)
227  {
228  // Disable saving variants for now. Making it work probably requires more work.
229  if (VariantFile != null) { return false; }
230  Serialize();
231  return base.Save(fileNameWithoutExtension, new XmlWriterSettings
232  {
233  Indent = true,
234  OmitXmlDeclaration = true,
235  NewLineOnAttributes = false
236  });
237  }
238 
239  public override bool Reset(bool forceReload = false)
240  {
241  if (forceReload)
242  {
243  return Load();
244  }
245  Deserialize(OriginalElement, alsoChildren: true);
246  SubParams.ForEach(sp => sp.Reset());
247  return true;
248  }
249 
250  public static bool CompareGroup(Identifier group1, Identifier group2) => group1 != Identifier.Empty && group2 != Identifier.Empty && group1 == group2;
251 
252  protected void CreateSubParams()
253  {
254  if (MainElement == null)
255  {
256  DebugConsole.ThrowError("Main element null, cannot create sub params!");
257  return;
258  }
259  SubParams.Clear();
260  var healthElement = MainElement.GetChildElement("health");
261  if (healthElement != null)
262  {
263  Health = new HealthParams(healthElement, this);
264  }
265  else
266  {
267  DebugConsole.ThrowError($"No health parameters defined for character \"{(SpeciesName)}\".");
268  Health = new HealthParams(null, this);
269  }
270  SubParams.Add(Health);
271  // TODO: support for multiple ai elements?
272  var ai = MainElement.GetChildElement("ai");
273  if (ai != null)
274  {
275  AI = new AIParams(ai, this);
276  SubParams.Add(AI);
277  }
278  foreach (var element in MainElement.GetChildElements("bloodemitter"))
279  {
280  var emitter = new ParticleParams(element, this);
281  BloodEmitters.Add(emitter);
282  SubParams.Add(emitter);
283  }
284  foreach (var element in MainElement.GetChildElements("gibemitter"))
285  {
286  var emitter = new ParticleParams(element, this);
287  GibEmitters.Add(emitter);
288  SubParams.Add(emitter);
289  }
290  foreach (var element in MainElement.GetChildElements("damageemitter"))
291  {
292  var emitter = new ParticleParams(element, this);
293  GibEmitters.Add(emitter);
294  SubParams.Add(emitter);
295  }
296  foreach (var soundElement in MainElement.GetChildElements("sound"))
297  {
298  var sound = new SoundParams(soundElement, this);
299  Sounds.Add(sound);
300  SubParams.Add(sound);
301  }
302  foreach (var inventoryElement in MainElement.GetChildElements("inventory"))
303  {
304  var inventory = new InventoryParams(inventoryElement, this);
305  Inventories.Add(inventory);
306  SubParams.Add(inventory);
307  }
308  }
309 
310  public bool Deserialize(XElement element = null, bool alsoChildren = true, bool recursive = true, bool loadDefaultValues = true)
311  {
312  if (base.Deserialize(element))
313  {
314  //backwards compatibility
315  if (SpeciesName.IsEmpty)
316  {
317  SpeciesName = element.GetAttributeIdentifier("name", "[NAME NOT GIVEN]");
318  }
319  if (alsoChildren)
320  {
321  SubParams.ForEach(p => p.Deserialize(recursive));
322  }
323  return true;
324  }
325  return false;
326  }
327 
328  public bool Serialize(XElement element = null, bool alsoChildren = true, bool recursive = true)
329  {
330  if (base.Serialize(element))
331  {
332  if (alsoChildren)
333  {
334  SubParams.ForEach(p => p.Serialize(recursive));
335  }
336  return true;
337  }
338  return false;
339  }
340 
341 #if CLIENT
342  public void AddToEditor(ParamsEditor editor, bool alsoChildren = true, bool recursive = true, int space = 0)
343  {
344  base.AddToEditor(editor);
345  if (alsoChildren)
346  {
347  SubParams.ForEach(s => s.AddToEditor(editor, recursive));
348  }
349  if (space > 0)
350  {
351  new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, (int)(space * GUI.yScale)), editor.EditorBox.Content.RectTransform), style: null, color: ParamsEditor.Color)
352  {
353  CanBeFocused = false
354  };
355  }
356  }
357 #endif
358 
359  public bool AddSound() => TryAddSubParam(CreateElement("sound"), (e, c) => new SoundParams(e, c), out _, Sounds);
360 
361  public void AddInventory() => TryAddSubParam(CreateElement("inventory", new XElement("item")), (e, c) => new InventoryParams(e, c), out _, Inventories);
362 
363  public void AddBloodEmitter() => AddEmitter("bloodemitter");
364  public void AddGibEmitter() => AddEmitter("gibemitter");
365  public void AddDamageEmitter() => AddEmitter("damageemitter");
366 
367  private void AddEmitter(string type)
368  {
369  switch (type)
370  {
371  case "gibemitter":
372  TryAddSubParam(CreateElement(type), (e, c) => new ParticleParams(e, c), out _, GibEmitters);
373  break;
374  case "bloodemitter":
375  TryAddSubParam(CreateElement(type), (e, c) => new ParticleParams(e, c), out _, BloodEmitters);
376  break;
377  case "damageemitter":
378  TryAddSubParam(CreateElement(type), (e, c) => new ParticleParams(e, c), out _, DamageEmitters);
379  break;
380  default: throw new NotImplementedException(type);
381  }
382  }
383 
384  public bool RemoveSound(SoundParams soundParams) => RemoveSubParam(soundParams);
385  public bool RemoveBloodEmitter(ParticleParams emitter) => RemoveSubParam(emitter, BloodEmitters);
386  public bool RemoveGibEmitter(ParticleParams emitter) => RemoveSubParam(emitter, GibEmitters);
387  public bool RemoveDamageEmitter(ParticleParams emitter) => RemoveSubParam(emitter, DamageEmitters);
388  public bool RemoveInventory(InventoryParams inventory) => RemoveSubParam(inventory, Inventories);
389 
390  protected bool RemoveSubParam<T>(T subParam, IList<T> collection = null) where T : SubParam
391  {
392  if (subParam == null || subParam.Element == null || subParam.Element.Parent == null) { return false; }
393  if (collection != null && !collection.Contains(subParam)) { return false; }
394  if (!SubParams.Contains(subParam)) { return false; }
395  collection?.Remove(subParam);
396  SubParams.Remove(subParam);
397  subParam.Element.Remove();
398  return true;
399  }
400 
401  protected bool TryAddSubParam<T>(ContentXElement element, Func<ContentXElement, CharacterParams, T> constructor, out T subParam, IList<T> collection = null, Func<IList<T>, bool> filter = null) where T : SubParam
402  {
403  subParam = constructor(element, this);
404  if (collection != null && filter != null)
405  {
406  if (filter(collection)) { return false; }
407  }
408  MainElement.Add(element);
409  SubParams.Add(subParam);
410  collection?.Add(subParam);
411  return subParam != null;
412  }
413 
414  #region Subparams
415  public class SoundParams : SubParam
416  {
417  public override string Name => "Sound";
418 
420  public string File { get; private set; }
421 
422 #if CLIENT
423  [Serialize(SoundType.Idle, IsPropertySaveable.Yes), Editable]
424  public SoundType State { get; private set; }
425 #endif
426 
427  [Serialize(1000f, IsPropertySaveable.Yes), Editable(minValue: 0f, maxValue: 10000f)]
428  public float Range { get; private set; }
429 
430  [Serialize(1.0f, IsPropertySaveable.Yes), Editable(minValue: 0f, maxValue: 2.0f)]
431  public float Volume { get; private set; }
432 
433  [Serialize("", IsPropertySaveable.Yes, description: "Which tags are required for this sound to play?"), Editable()]
434  public string Tags
435  {
436  get { return string.Join(',', TagSet); }
437  private set
438  {
439  TagSet = value.Split(',')
440  .ToIdentifiers()
441  .Where(id => !id.IsEmpty)
442  .ToImmutableHashSet();
443  }
444  }
445 
446  public ImmutableHashSet<Identifier> TagSet { get; private set; }
447 
448  public SoundParams(ContentXElement element, CharacterParams character) : base(element, character)
449  {
450  Identifier genderFallback = element.GetAttributeIdentifier("gender", "");
451  if (genderFallback != Identifier.Empty && genderFallback != "None")
452  {
453  TagSet = TagSet.Add(genderFallback);
454  }
455  }
456  }
457 
458  public class ParticleParams : SubParam
459  {
460  private string name;
461  public override string Name
462  {
463  get
464  {
465  if (name == null && Element != null)
466  {
467  name = Element.Name.ToString().FormatCamelCaseWithSpaces();
468  }
469  return name;
470  }
471  }
472 
474  public string Particle { get; set; }
475 
476  [Serialize(0f, IsPropertySaveable.Yes), Editable(-360f, 360f, decimals: 0)]
477  public float AngleMin { get; private set; }
478 
479  [Serialize(0f, IsPropertySaveable.Yes), Editable(-360f, 360f, decimals: 0)]
480  public float AngleMax { get; private set; }
481 
482  [Serialize(1.0f, IsPropertySaveable.Yes), Editable(0f, 100f, decimals: 2)]
483  public float ScaleMin { get; private set; }
484 
485  [Serialize(1.0f, IsPropertySaveable.Yes), Editable(0f, 100f, decimals: 2)]
486  public float ScaleMax { get; private set; }
487 
488  [Serialize(0f, IsPropertySaveable.Yes), Editable(0f, 10000f, decimals: 0)]
489  public float VelocityMin { get; private set; }
490 
491  [Serialize(0f, IsPropertySaveable.Yes), Editable(0f, 10000f, decimals: 0)]
492  public float VelocityMax { get; private set; }
493 
494  [Serialize(0f, IsPropertySaveable.Yes), Editable(0f, 100f, decimals: 2)]
495  public float EmitInterval { get; private set; }
496 
497  [Serialize(0, IsPropertySaveable.Yes), Editable(0, 1000)]
498  public int ParticlesPerSecond { get; private set; }
499 
500  [Serialize(0, IsPropertySaveable.Yes), Editable(0, 1000)]
501  public int ParticleAmount { get; private set; }
502 
503  [Serialize(false, IsPropertySaveable.Yes), Editable]
504  public bool HighQualityCollisionDetection { get; private set; }
505 
506  [Serialize(false, IsPropertySaveable.Yes), Editable]
507  public bool CopyEntityAngle { get; private set; }
508 
509  public ParticleParams(ContentXElement element, CharacterParams character) : base(element, character) { }
510  }
511 
512  public class HealthParams : SubParam
513  {
514  public override string Name => "Health";
515 
516  [Serialize(100f, IsPropertySaveable.Yes, description: "How much (max) health does the character have?"), Editable(minValue: 1, maxValue: 10000f)]
517  public float Vitality { get; set; }
518 
520  public bool DoesBleed { get; set; }
521 
522  [Serialize(float.PositiveInfinity, IsPropertySaveable.Yes), Editable(minValue: 0, maxValue: float.PositiveInfinity)]
523  public float CrushDepth { get; set; }
524 
525  // Make editable?
526  [Serialize(false, IsPropertySaveable.Yes)]
527  public bool UseHealthWindow { get; set; }
528 
529  [Serialize(0f, IsPropertySaveable.Yes, description: "How easily the character heals from the bleeding wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
530  public float BleedingReduction { get; set; }
531 
532  [Serialize(0f, IsPropertySaveable.Yes, description: "How easily the character heals from the burn wounds. Default 0 (no extra healing)."), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
533  public float BurnReduction { get; set; }
534 
535  [Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
536  public float ConstantHealthRegeneration { get; set; }
537 
538  [Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
539  public float HealthRegenerationWhenEating { get; set; }
540 
541  [Serialize(false, IsPropertySaveable.Yes), Editable]
542  public bool StunImmunity { get; set; }
543 
544  [Serialize(false, IsPropertySaveable.Yes), Editable]
545  public bool PoisonImmunity { get; set; }
546 
547  [Serialize(1f, IsPropertySaveable.Yes, description: "1 = default, 0 = immune."), Editable(MinValueFloat = 0f, MaxValueFloat = 1000, DecimalCount = 1)]
548  public float PoisonVulnerability { get; set; }
549 
551  public float EmpVulnerability { get; set; }
552 
553  [Serialize(false, IsPropertySaveable.Yes, description: "Can afflictions affect the face/body tint of the character."), Editable]
554  public bool ApplyAfflictionColors { get; private set; }
555 
556  [Serialize("", IsPropertySaveable.Yes, description:"A comma-separated list of identifiers of afflictions that the creature is immune to."), Editable]
557  public string Immunities { get; private set; }
558 
559  private ImmutableHashSet<Identifier> _immunityIdentifiers;
560  public IEnumerable<Identifier> ImmunityIdentifiers
561  {
562  get
563  {
564  _immunityIdentifiers ??= Element.GetAttributeIdentifierArray("immunities", Array.Empty<Identifier>()).ToImmutableHashSet();
565  return _immunityIdentifiers;
566  }
567  }
568 
569  // TODO: limbhealths, sprite?
570 
571  public HealthParams(ContentXElement element, CharacterParams character) : base(element, character)
572  {
573  //backwards compatibility
574  if (CrushDepth < 0)
575  {
576  //invert y, convert to meters, and add 1000 to be on the safe side (previously the value would be from the bottom of the level)
577  float newCrushDepth = -CrushDepth * Physics.DisplayToRealWorldRatio + 1000;
578  DebugConsole.AddWarning($"Character \"{character.SpeciesName}\" has a negative crush depth. "+
579  "Previously the crush depths were defined as display units (e.g. -30000 would correspond to 300 meters below the level), "+
580  "but now they're in meters (e.g. 3000 would correspond to a depth of 3000 meters displayed on the nav terminal). "+
581  $"Changing the crush depth from {CrushDepth} to {newCrushDepth}.",
582  element.ContentPackage);
583  CrushDepth = newCrushDepth;
584  }
585  }
586  }
587 
588  public class InventoryParams : SubParam
589  {
590  public class InventoryItem : SubParam
591  {
592  public override string Name => "Item";
593 
594  [Serialize("", IsPropertySaveable.Yes, description: "Item identifier."), Editable()]
595  public string Identifier { get; private set; }
596 
597  public InventoryItem(ContentXElement element, CharacterParams character) : base(element, character) { }
598  }
599 
600  public override string Name => "Inventory";
601 
602  [Serialize("Any, Any", IsPropertySaveable.Yes, description: "Which slots the inventory holds? Accepted types: None, Any, RightHand, LeftHand, Head, InnerClothes, OuterClothes, Headset, and Card."), Editable()]
603  public string Slots { get; private set; }
604 
605  [Serialize(false, IsPropertySaveable.Yes), Editable]
606  public bool AccessibleWhenAlive { get; private set; }
607 
608  [Serialize(1.0f, IsPropertySaveable.Yes, description: "What are the odds that this inventory is spawned on the character?"), Editable(minValue: 0f, maxValue: 1.0f)]
609  public float Commonness { get; private set; }
610 
611  public List<InventoryItem> Items { get; private set; } = new List<InventoryItem>();
612 
613  public InventoryParams(ContentXElement element, CharacterParams character) : base(element, character)
614  {
615  foreach (var itemElement in element.GetChildElements("item"))
616  {
617  var item = new InventoryItem(itemElement, character);
618  SubParams.Add(item);
619  Items.Add(item);
620  }
621  }
622 
623  public void AddItem(string identifier = null)
624  {
625  if (Element == null) { return; }
626  identifier ??= "";
627  var element = CreateElement("item", new XAttribute("identifier", identifier));
628  Element.Add(element);
629  var item = new InventoryItem(element, Character);
630  SubParams.Add(item);
631  Items.Add(item);
632  }
633 
634  public bool RemoveItem(InventoryItem item) => RemoveSubParam(item, Items);
635  }
636 
637  public class AIParams : SubParam
638  {
639  public override string Name => "AI";
640 
641  [Serialize(1.0f, IsPropertySaveable.Yes, description: "How strong other characters think this character is? Only affects AI."), Editable()]
642  public float CombatStrength { get; private set; }
643 
644  [Serialize(1.0f, IsPropertySaveable.Yes, description: "Affects how far the character can see the targets. Used as a multiplier."), Editable(minValue: 0f, maxValue: 10f)]
645  public float Sight { get; private set; }
646 
647  [Serialize(1.0f, IsPropertySaveable.Yes, description: "Affects how far the character can hear the targets. Used as a multiplier."), Editable(minValue: 0f, maxValue: 10f)]
648  public float Hearing { get; private set; }
649 
650  [Serialize(-1.0f, IsPropertySaveable.Yes, description: "Hard limit to how far the character can spot targets from, regardless of the sight/hearing or how visible or how much noise the target is making. Not used if set to negative."), Editable]
651  public float MaxPerceptionDistance { get; set; }
652 
653  [Serialize(100f, IsPropertySaveable.Yes, description: "How much the targeting priority increases each time the character takes damage. Works like the greed value, described above. The default value is 100."), Editable(minValue: -1000f, maxValue: 1000f)]
654  public float AggressionHurt { get; private set; }
655 
656  [Serialize(10f, IsPropertySaveable.Yes, description: "How much the targeting priority increases each time the character does damage to the target. The actual priority adjustment is calculated based on the damage percentage multiplied by the greed value. The default value is 10, which means the priority will increase by 1 every time the character does damage 10% of the target's current health. If the damage is 50%, then the priority increase is 5."), Editable(minValue: 0f, maxValue: 1000f)]
657  public float AggressionGreed { get; private set; }
658 
659  [Serialize(0f, IsPropertySaveable.Yes, description: "If the health drops below this threshold, the character flees. In percentages."), Editable(minValue: 0f, maxValue: 100f)]
660  public float FleeHealthThreshold { get; set; }
661 
662  [Serialize(false, IsPropertySaveable.Yes, description: "Does the character attack when provoked? When enabled, overrides the predefined targeting state with Attack and increases the priority of it."), Editable()]
663  public bool AttackWhenProvoked { get; private set; }
664 
665  [Serialize(false, IsPropertySaveable.Yes, description: "The character will flee for a brief moment when being shot at if not performing an attack."), Editable]
666  public bool AvoidGunfire { get; private set; }
667 
668  [Serialize(0f, IsPropertySaveable.Yes, description: "How much damage is required for single attack to trigger avoiding/releasing targets."), Editable(minValue: 0f, maxValue: 1000f)]
669  public float DamageThreshold { get; private set; }
670 
671  [Serialize(3f, IsPropertySaveable.Yes, description: "How long the creature avoids gunfire. Also used when the creature is unlatched."), Editable(minValue: 0f, maxValue: 100f)]
672  public float AvoidTime { get; private set; }
673 
674  [Serialize(20f, IsPropertySaveable.Yes, description: "How long the creature flees before returning to normal state. When the creature sees the target or is being chased, it will always flee, if it's in the flee state."), Editable(minValue: 0f, maxValue: 100f)]
675  public float MinFleeTime { get; private set; }
676 
677  [Serialize(false, IsPropertySaveable.Yes, description: "Does the character try to break inside the sub?"), Editable]
678  public bool AggressiveBoarding { get; private set; }
679 
680  [Serialize(true, IsPropertySaveable.Yes, description: "Enforce aggressive behavior if the creature is spawned as a target of a monster mission."), Editable]
681  public bool EnforceAggressiveBehaviorForMissions { get; private set; }
682 
683  [Serialize(true, IsPropertySaveable.Yes, description: "Should the character target or ignore walls when it's outside the submarine."), Editable]
684  public bool TargetOuterWalls { get; private set; }
685 
686  [Serialize(false, IsPropertySaveable.Yes, description: "If enabled, the character chooses randomly from the available attacks. The priority is used as a weight for weighted random."), Editable]
687  public bool RandomAttack { get; private set; }
688 
689  [Serialize(false, IsPropertySaveable.Yes, description:"Does the creature know how to open doors (still requires a proper ID card). Humans can always open doors (They don't use this AI definition)."), Editable]
690  public bool CanOpenDoors { get; private set; }
691 
692  [Serialize(false, IsPropertySaveable.Yes, description:"Unlike human AI, monsters normally only use pathfinding when they are inside the submarine. When this is enabled, the monsters can also use pathfinding to get inside the sub. In practice, via doors and hatches."), Editable]
693  public bool UsePathFindingToGetInside { get; set; }
694 
695  [Serialize(false, IsPropertySaveable.Yes, description: "Does the creature close the doors behind it. Humans don't use this AI definition."), Editable]
696  public bool KeepDoorsClosed { get; private set; }
697 
698  [Serialize(true, IsPropertySaveable.Yes, "Is the creature allowed to navigate from and into the depths of the abyss? When enabled, the creatures will try to avoid the depths."), Editable]
699  public bool AvoidAbyss { get; set; }
700 
701  [Serialize(false, IsPropertySaveable.Yes, "Does the creature try to keep in the abyss? Has effect only when AvoidAbyss is false."), Editable]
702  public bool StayInAbyss { get; set; }
703 
704  [Serialize(false, IsPropertySaveable.Yes, "Does the creature patrol the flooded hulls while idling inside a friendly submarine?"), Editable]
705  public bool PatrolFlooded { get; set; }
706 
707  [Serialize(false, IsPropertySaveable.Yes, "Does the creature patrol the dry hulls while idling inside a friendly submarine?"), Editable]
708  public bool PatrolDry { get; set; }
709 
710  [Serialize(0f, IsPropertySaveable.Yes, description: "Initial aggression used in the circle attack pattern (0-100). The aggression affects how close and how fast to the target the monster circles."), Editable]
711  public float StartAggression { get; private set; }
712 
713  [Serialize(100f, IsPropertySaveable.Yes, description: "Maximum aggression used in the circle attack pattern (0-100). The aggression affects how close and how fast to the target the monster circles."), Editable]
714  public float MaxAggression { get; private set; }
715 
716  [Serialize(0f, IsPropertySaveable.Yes, description: "How quickly the aggression level increases from StartAggression to MaxAggression when using the circle attack pattern. Artificial amount, applied once per attack cycle."), Editable]
717 
718  public float AggressionCumulation { get; private set; }
719 
720  [Serialize(WallTargetingMethod.Target, IsPropertySaveable.Yes, description: "Defines the method of checking whether there's a blocking (submarine) wall."), Editable]
721  public WallTargetingMethod WallTargetingMethod { get; private set; }
722 
723  public IEnumerable<TargetParams> Targets => targets;
724  protected readonly List<TargetParams> targets = new List<TargetParams>();
725 
726  public AIParams(ContentXElement element, CharacterParams character) : base(element, character)
727  {
728  if (element == null) { return; }
729  element.GetChildElements("target").ForEach(t => TryAddTarget(t, out _));
730  element.GetChildElements("targetpriority").ForEach(t => TryAddTarget(t, out _));
731  }
732 
733  private bool TryAddTarget(ContentXElement targetElement, out TargetParams target)
734  {
735  string tag = targetElement.GetAttributeString("tag", null);
736  if (HasTag(tag))
737  {
738  target = null;
739  DebugConsole.AddWarning($"Trying to add multiple targets with the same tag ('{tag}') defined! Only the first will be used!",
740  targetElement.ContentPackage);
741  return false;
742  }
743  else
744  {
745  target = new TargetParams(targetElement, Character);
746  targets.Add(target);
747  SubParams.Add(target);
748  return true;
749  }
750  }
751 
752  public bool TryAddEmptyTarget(out TargetParams targetParams) => TryAddNewTarget("newtarget" + targets.Count, AIState.Attack, 0f, out targetParams);
753 
754  public bool TryAddNewTarget(string tag, AIState state, float priority, out TargetParams targetParams) =>
755  TryAddNewTarget(tag.ToIdentifier(), state, priority, out targetParams);
756 
757  public bool TryAddNewTarget(Identifier tag, AIState state, float priority, out TargetParams targetParams)
758  {
759  if (Element == null)
760  {
761  targetParams = null;
762  return false;
763  }
764  var element = TargetParams.CreateNewElement(Character, tag, state, priority);
765  if (TryAddTarget(element, out targetParams))
766  {
767  Element.Add(element);
768  return true;
769  }
770  else
771  {
772  return false;
773  }
774  }
775 
776  public bool HasTag(string tag) => HasTag(tag.ToIdentifier());
777 
778  public bool HasTag(Identifier tag)
779  {
780  if (tag == null) { return false; }
781  return targets.Any(t => t.Tag == tag);
782  }
783 
784  public bool RemoveTarget(TargetParams target) => RemoveSubParam(target, targets);
785 
786  public bool TryGetTarget(string targetTag, out TargetParams target)
787  => TryGetTarget(targetTag.ToIdentifier(), out target);
788 
789  public bool TryGetTarget(Identifier targetTag, out TargetParams target)
790  {
791  target = targets.FirstOrDefault(t => t.Tag == targetTag);
792  return target != null;
793  }
794 
795  public bool TryGetTarget(Character targetCharacter, out TargetParams target)
796  {
797  if (!TryGetTarget(targetCharacter.SpeciesName, out target))
798  {
799  target = targets.FirstOrDefault(t => t.Tag == targetCharacter.Params.Group);
800  }
801  return target != null;
802  }
803 
804  public bool TryGetTarget(IEnumerable<Identifier> tags, out TargetParams target)
805  {
806  target = null;
807  if (tags == null || tags.None()) { return false; }
808  float priority = -1;
809  foreach (var potentialTarget in targets)
810  {
811  if (potentialTarget.Priority > priority)
812  {
813  if (tags.Any(t => t == potentialTarget.Tag))
814  {
815  target = potentialTarget;
816  priority = target.Priority;
817  }
818  }
819  }
820  return target != null;
821  }
822 
823  public TargetParams GetTarget(string targetTag, bool throwError = true)
824  => GetTarget(targetTag.ToIdentifier(), throwError);
825 
826  public TargetParams GetTarget(Identifier targetTag, bool throwError = true)
827  {
828  if (targetTag.IsEmpty) { return null; }
829  if (!TryGetTarget(targetTag, out TargetParams target))
830  {
831  if (throwError)
832  {
833  DebugConsole.ThrowError($"Cannot find a target with the tag {targetTag}!");
834  }
835  }
836  return target;
837  }
838  }
839 
840  public class TargetParams : SubParam
841  {
842  public override string Name => "Target";
843 
844  [Serialize("", IsPropertySaveable.Yes, description: "Can be an item tag, species name or something else. Examples: decoy, provocative, light, dead, human, crawler, wall, nasonov, sonar, door, stronger, weaker, light, human, room..."), Editable()]
845  public Identifier Tag { get; private set; }
846 
848  public AIState State { get; set; }
849 
850  [Serialize(0f, IsPropertySaveable.Yes, description: "What base priority is given to the target?"), Editable(minValue: 0f, maxValue: 1000f, ValueStep = 1, DecimalCount = 0)]
851  public float Priority { get; set; }
852 
853  [Serialize(0f, IsPropertySaveable.Yes, description: "Generic distance that can be used for different purposes depending on the state. E.g. in Avoid state this defines the distance that the character tries to keep to the target. If the distance is 0, it's not used."), Editable(MinValueFloat = 0, ValueStep = 10, DecimalCount = 0)]
854  public float ReactDistance { get; set; }
855 
856  [Serialize(0f, IsPropertySaveable.Yes, description: "Used for defining the attack distance for PassiveAggressive and Aggressive states. If the distance is 0, it's not used."), Editable(MinValueFloat = 0, ValueStep = 10, DecimalCount = 0)]
857  public float AttackDistance { get; set; }
858 
859  [Serialize(0f, IsPropertySaveable.Yes, description: "Generic timer that can be used for different purposes depending on the state. E.g. in Observe state this defines how long the character in general keeps staring the targets (Some random is always applied)."), Editable]
860  public float Timer { get; set; }
861 
862  [Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored if it's inside a container/inventory. Only affects items."), Editable]
863  public bool IgnoreContained { get; set; }
864 
865  [Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored while the creature is inside. Doesn't matter where the target is."), Editable]
866  public bool IgnoreInside { get; set; }
867 
868  [Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored while the creature is outside. Doesn't matter where the target is."), Editable]
869  public bool IgnoreOutside { get; set; }
870 
871  [Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored if it's inside. Doesn't matter where the creature itself is."), Editable]
872  public bool IgnoreTargetInside { get; set; }
873 
874  [Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored if it's outside. Doesn't matter where the creature itself is."), Editable]
875  public bool IgnoreTargetOutside { get; set; }
876 
877  [Serialize(false, IsPropertySaveable.Yes, description: "Should the target be ignored if it's inside a different submarine than us? Normally only some targets are ignored when they are not inside the same sub."), Editable]
878  public bool IgnoreIfNotInSameSub { get; set; }
879 
880  [Serialize(false, IsPropertySaveable.Yes), Editable]
881  public bool IgnoreIncapacitated { get; set; }
882 
883  [Serialize(0f, IsPropertySaveable.Yes, description: "A generic threshold. For example, how much damage the protected target should take from an attacker before the creature starts defending it."), Editable]
884  public float Threshold { get; private set; }
885 
886  [Serialize(-1f, IsPropertySaveable.Yes, description: "A generic min threshold. Not used if set to negative."), Editable]
887  public float ThresholdMin { get; private set; }
888 
889  [Serialize(-1f, IsPropertySaveable.Yes, description: "A generic max threshold. Not used if set to negative."), Editable]
890  public float ThresholdMax { get; private set; }
891 
892  [Serialize(1.0f, IsPropertySaveable.Yes, description: "Can be used to make the monster perceive the target further than it normally can."), Editable]
893  public float PerceptionDistanceMultiplier { get; private set; }
894 
895  [Serialize(-1.0f, IsPropertySaveable.Yes, description: "Maximum distance at which the monster can perceive the target, regardless of the sight/hearing or how visible or how much noise the target is making. Not used if set to negative."), Editable]
896  public float MaxPerceptionDistance { get; private set; }
897 
898  [Serialize("0.0, 0.0", IsPropertySaveable.Yes, description: "A generic offset. Used for example for offsetting the react distance (vector length) and for offsetting the target position when a guardian flees to a pod."), Editable]
899  public Vector2 Offset { get; private set; }
900 
901  [Serialize(AttackPattern.Straight, IsPropertySaveable.Yes, description: "Defines the movement pattern of the character when approaching a target."), Editable]
902  public AttackPattern AttackPattern { get; set; }
903 
904  [Serialize(false, IsPropertySaveable.Yes, description: "If enabled, the AI will give more priority to targets close to the horizontal middle of the sub. Only applies to walls, hulls, and items like sonar. Circle and Sweep always does this regardless of this property."), Editable]
905  public bool PrioritizeSubCenter { get; set; }
906 
907  #region Sweep
908  [Serialize(0f, IsPropertySaveable.Yes, description: "Use to define a distance at which the creature starts the sweeping movement."), Editable(MinValueFloat = 0, MaxValueFloat = 10000, ValueStep = 1, DecimalCount = 0)]
909  public float SweepDistance { get; private set; }
910 
911  [Serialize(10f, IsPropertySaveable.Yes, description: "How much the sweep affects the steering?"), Editable(MinValueFloat = 0, MaxValueFloat = 100, ValueStep = 1f, DecimalCount = 1)]
912  public float SweepStrength { get; private set; }
913 
914  [Serialize(1f, IsPropertySaveable.Yes, description: "How quickly the sweep direction changes. Uses the sine wave pattern."), Editable(MinValueFloat = 0, MaxValueFloat = 10, ValueStep = 0.1f, DecimalCount = 2)]
915  public float SweepSpeed { get; private set; }
916  #endregion
917 
918  #region Circle
919  [Serialize(5000f, IsPropertySaveable.Yes, description:"How close to the target the character should be, before they start using the circle pattern instead of directional approaching."), Editable(MinValueFloat = 0f, MaxValueFloat = 20000f)]
920  public float CircleStartDistance { get; private set; }
921 
922  [Serialize(false, IsPropertySaveable.Yes, description:"Normally the target size is taken into account when calculating the distance to the target. Set this true to skip that.")]
923  public bool IgnoreTargetSize { get; private set; }
924 
925  [Serialize(1f, IsPropertySaveable.Yes, description:"Determines the rate how quickly the target movement position is rotated towards the attack target. The actual rotation is calculated once per each attack cycle, based on the current aggression level."), Editable(MinValueFloat = 0f, MaxValueFloat = 100f)]
926  public float CircleRotationSpeed { get; private set; }
927 
928  [Serialize(false, IsPropertySaveable.Yes, description:"When enabled, the circle rotation speed can change when the target is far. When this setting is disabled (default), the character will head directly towards the target when it's too far."), Editable]
929  public bool DynamicCircleRotationSpeed { get; private set; }
930 
931  [Serialize(0f, IsPropertySaveable.Yes, description:"How much the turn speed can differ between attack cycles (stays constant during the cycle)"), Editable(MinValueFloat = 0f, MaxValueFloat = 1f)]
932  public float CircleRandomRotationFactor { get; private set; }
933 
934  [Serialize(5f, IsPropertySaveable.Yes, description:"Affects how close to the target the character has to be before the strike phase of the circle behavior triggers. In the strike phase, the creature moves directly towards the target."), Editable(MinValueFloat = 0f, MaxValueFloat = 10f)]
935  public float CircleStrikeDistanceMultiplier { get; private set; }
936 
937  [Serialize(0f, IsPropertySaveable.Yes, description:"How much the target position is offset at maximum. Low values make the character hit the target earlier/always, higher values make it miss the target when the aggression intensity is low (early in the encounter)."), Editable(MinValueFloat = 0f, MaxValueFloat = 50f)]
938  public float CircleMaxRandomOffset { get; private set; }
939  #endregion
940 
944  public List<PropertyConditional> Conditionals { get; private set; } = new List<PropertyConditional>();
945 
946  public TargetParams(string tag, AIState state, float priority, CharacterParams character) :
947  this(CreateNewElement(character, tag, state, priority), character) { }
948 
949  public TargetParams(ContentXElement element, CharacterParams character) : base(element, character)
950  {
951  foreach (var subElement in element.Elements())
952  {
953  switch (subElement.Name.ToString().ToLowerInvariant())
954  {
955  case "conditional":
956  Conditionals.AddRange(PropertyConditional.FromXElement(subElement));
957  break;
958  }
959  }
960  }
961 
962  public static ContentXElement CreateNewElement(CharacterParams character, Identifier tag, AIState state, float priority) =>
963  CreateNewElement(character, tag.Value, state, priority);
964 
965  public static ContentXElement CreateNewElement(CharacterParams character, string tag, AIState state, float priority)
966  {
967  return new XElement("target",
968  new XAttribute("tag", tag),
969  new XAttribute("state", state),
970  new XAttribute("priority", priority)).FromPackage(character.File.ContentPackage);
971  }
972  }
973 
974  public abstract class SubParam : ISerializableEntity
975  {
976  public virtual string Name { get; set; }
977  public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; }
978  public ContentXElement Element { get; set; }
979  public List<SubParam> SubParams { get; set; } = new List<SubParam>();
980 
981  public CharacterParams Character { get; private set; }
982 
983  protected ContentXElement CreateElement(string name, params object[] attrs)
984  => new XElement(name, attrs).FromPackage(Element.ContentPackage);
985 
986  public SubParam(ContentXElement element, CharacterParams character)
987  {
988  Element = element;
989  Character = character;
991  }
992 
993  public virtual bool Deserialize(bool recursive = true)
994  {
996  if (recursive)
997  {
998  SubParams.ForEach(sp => sp.Deserialize(true));
999  }
1000  return SerializableProperties != null;
1001  }
1002 
1003  public virtual bool Serialize(bool recursive = true)
1004  {
1006  if (recursive)
1007  {
1008  SubParams.ForEach(sp => sp.Serialize(true));
1009  }
1010  return true;
1011  }
1012 
1013  public virtual void Reset()
1014  {
1015  // Don't use recursion, because the reset method might be overriden
1016  Deserialize(false);
1017  SubParams.ForEach(sp => sp.Reset());
1018  }
1019 
1020  protected bool RemoveSubParam<T>(T subParam, IList<T> collection = null) where T : SubParam
1021  {
1022  if (subParam == null || subParam.Element == null || subParam.Element.Parent == null) { return false; }
1023  if (collection != null && !collection.Contains(subParam)) { return false; }
1024  if (!SubParams.Contains(subParam)) { return false; }
1025  collection?.Remove(subParam);
1026  SubParams.Remove(subParam);
1027  subParam.Element.Remove();
1028  return true;
1029  }
1030 
1031 #if CLIENT
1033  public virtual void AddToEditor(ParamsEditor editor, bool recursive = true, int space = 0, GUIFont titleFont = null)
1034  {
1035  SerializableEntityEditor = new SerializableEntityEditor(editor.EditorBox.Content.RectTransform, this, inGame: false, showName: true, titleFont: titleFont ?? GUIStyle.LargeFont);
1036  if (recursive)
1037  {
1038  SubParams.ForEach(sp => sp.AddToEditor(editor, true, titleFont: titleFont ?? GUIStyle.SmallFont));
1039  }
1040  if (space > 0)
1041  {
1042  new GUIFrame(new RectTransform(new Point(editor.EditorBox.Rect.Width, space), editor.EditorBox.Content.RectTransform), style: null, color: new Color(20, 20, 20, 255))
1043  {
1044  CanBeFocused = false
1045  };
1046  }
1047  }
1048 #endif
1049  }
1050  #endregion
1051  }
1052 }
bool TryAddEmptyTarget(out TargetParams targetParams)
TargetParams GetTarget(Identifier targetTag, bool throwError=true)
readonly List< TargetParams > targets
bool TryGetTarget(string targetTag, out TargetParams target)
bool TryGetTarget(Character targetCharacter, out TargetParams target)
IEnumerable< TargetParams > Targets
TargetParams GetTarget(string targetTag, bool throwError=true)
bool TryGetTarget(Identifier targetTag, out TargetParams target)
bool TryAddNewTarget(string tag, AIState state, float priority, out TargetParams targetParams)
AIParams(ContentXElement element, CharacterParams character)
bool TryGetTarget(IEnumerable< Identifier > tags, out TargetParams target)
bool RemoveTarget(TargetParams target)
bool TryAddNewTarget(Identifier tag, AIState state, float priority, out TargetParams targetParams)
IEnumerable< Identifier > ImmunityIdentifiers
HealthParams(ContentXElement element, CharacterParams character)
InventoryItem(ContentXElement element, CharacterParams character)
InventoryParams(ContentXElement element, CharacterParams character)
void AddItem(string identifier=null)
ParticleParams(ContentXElement element, CharacterParams character)
SoundParams(ContentXElement element, CharacterParams character)
ImmutableHashSet< Identifier > TagSet
virtual bool Deserialize(bool recursive=true)
ContentXElement CreateElement(string name, params object[] attrs)
SubParam(ContentXElement element, CharacterParams character)
SerializableEntityEditor SerializableEntityEditor
bool RemoveSubParam< T >(T subParam, IList< T > collection=null)
virtual void AddToEditor(ParamsEditor editor, bool recursive=true, int space=0, GUIFont titleFont=null)
virtual bool Serialize(bool recursive=true)
Dictionary< Identifier, SerializableProperty > SerializableProperties
TargetParams(string tag, AIState state, float priority, CharacterParams character)
List< PropertyConditional > Conditionals
Conditionals that must be met for the character to be able to use these targeting parameters.
static ContentXElement CreateNewElement(CharacterParams character, string tag, AIState state, float priority)
static ContentXElement CreateNewElement(CharacterParams character, Identifier tag, AIState state, float priority)
TargetParams(ContentXElement element, CharacterParams character)
Contains character data that should be editable in the character editor.
bool RemoveBloodEmitter(ParticleParams emitter)
bool RemoveInventory(InventoryParams inventory)
bool RemoveSound(SoundParams soundParams)
bool TryAddSubParam< T >(ContentXElement element, Func< ContentXElement, CharacterParams, T > constructor, out T subParam, IList< T > collection=null, Func< IList< T >, bool > filter=null)
bool RemoveDamageEmitter(ParticleParams emitter)
AIParams AI
Parameters for EnemyAIController. Not used by HumanAIController.
readonly List< ParticleParams > DamageEmitters
override ContentXElement? MainElement
readonly List< SoundParams > Sounds
readonly CharacterFile File
bool Save(string fileNameWithoutExtension=null)
override string GetName()
bool Deserialize(XElement element=null, bool alsoChildren=true, bool recursive=true, bool loadDefaultValues=true)
bool RemoveSubParam< T >(T subParam, IList< T > collection=null)
readonly List< ParticleParams > GibEmitters
void AddToEditor(ParamsEditor editor, bool alsoChildren=true, bool recursive=true, int space=0)
readonly List< ParticleParams > BloodEmitters
CharacterParams(CharacterFile file)
bool Serialize(XElement element=null, bool alsoChildren=true, bool recursive=true)
static bool CompareGroup(Identifier group1, Identifier group2)
static XElement CreateVariantXml(XElement variantXML, XElement baseXML)
readonly List< InventoryParams > Inventories
readonly List< SubParam > SubParams
override bool Reset(bool forceReload=false)
bool RemoveGibEmitter(ParticleParams emitter)
static CharacterPrefab FindBySpeciesName(Identifier speciesName)
ContentXElement ConfigElement
readonly ContentPackage ContentPackage
Definition: ContentFile.cs:136
void Add(ContentXElement elem)
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
ContentPackage? ContentPackage
IEnumerable< ContentXElement > GetChildElements(string name)
ContentXElement? GetChildElement(string name)
Identifier GetAttributeIdentifier(string key, string def)
virtual void UpdatePath(ContentPath fullPath)
ContentXElement CreateElement(string name, params object[] attrs)
ContentXElement OriginalElement
virtual Rectangle Rect
RectTransform RectTransform
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
static IEnumerable< PropertyConditional > FromXElement(ContentXElement element, Predicate< XAttribute >? predicate=null)
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
static void SerializeProperties(ISerializableEntity obj, XElement element, bool saveIfDefault=false, bool ignoreEditable=false)