Client LuaCsForBarotrauma
HumanPrefab.cs
1 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Xml.Linq;
7 
8 namespace Barotrauma
9 {
11  {
12  [Serialize("any", IsPropertySaveable.No)]
13  public Identifier Job { get; protected set; }
14 
16  public float Commonness { get; protected set; }
17 
19  public float HealthMultiplier { get; protected set; }
20 
22  public float HealthMultiplierInMultiplayer { get; protected set; }
23 
25  public float AimSpeed { get; protected set; }
26 
28  public float AimAccuracy { get; protected set; }
29 
31  public float SkillMultiplier { get; protected set; }
32 
34  public int ExperiencePoints { get; private set; }
35 
36  private readonly HashSet<Identifier> tags = new HashSet<Identifier>();
37 
38  [Serialize("", IsPropertySaveable.Yes)]
39  public string Tags
40  {
41  get => string.Join(",", tags);
42  set
43  {
44  tags.Clear();
45  if (!string.IsNullOrWhiteSpace(value))
46  {
47  string[] splitTags = value.Split(',');
48  foreach (var tag in splitTags)
49  {
50  tags.Add(tag.ToIdentifier());
51  }
52  }
53  }
54  }
55 
56  private readonly HashSet<Identifier> moduleFlags = new HashSet<Identifier>();
57 
58  [Serialize("", IsPropertySaveable.Yes, "What outpost module tags does the NPC prefer to spawn in.")]
59  public string ModuleFlags
60  {
61  get => string.Join(",", moduleFlags);
62  set
63  {
64  moduleFlags.Clear();
65  if (!string.IsNullOrWhiteSpace(value))
66  {
67  string[] splitFlags = value.Split(',');
68  foreach (var f in splitFlags)
69  {
70  moduleFlags.Add(f.ToIdentifier());
71  }
72  }
73  }
74  }
75 
76 
77  private readonly HashSet<Identifier> spawnPointTags = new HashSet<Identifier>();
78 
79  [Serialize("", IsPropertySaveable.Yes, "Tag(s) of the spawnpoints the NPC prefers to spawn at.")]
80  public string SpawnPointTags
81  {
82  get => string.Join(",", spawnPointTags);
83  set
84  {
85  spawnPointTags.Clear();
86  if (!string.IsNullOrWhiteSpace(value))
87  {
88  string[] splitTags = value.Split(',');
89  foreach (var tag in splitTags)
90  {
91  spawnPointTags.Add(tag.ToIdentifier());
92  }
93  }
94  }
95  }
96 
97  [Serialize(false, IsPropertySaveable.No, description: "If enabled, the NPC will not spawn if the specified spawn point tags can't be found.")]
98  public bool RequireSpawnPointTag { get; protected set; }
99 
101  public CampaignMode.InteractionType CampaignInteractionType { get; protected set; }
102 
104  public AIObjectiveIdle.BehaviorType Behavior { get; protected set; }
105 
106  [Serialize(1.0f, IsPropertySaveable.No, description:
107  "Affects how far the character can hear sounds created by AI targets with the tag ProvocativeToHumanAI. "+
108  "Used as a multiplier on the sound range of the target, e.g. a value of 0.5 would mean a target with a sound range of 1000 would need to be within 500 units for this character to hear it. "+
109  "Only affects the \"fight intruders\" objective, which makes the character go and inspect noises.")]
110  public float Hearing { get; set; } = 1.0f;
111 
112  [Serialize(float.PositiveInfinity, IsPropertySaveable.No)]
113  public float ReportRange { get; protected set; }
114 
115  [Serialize(float.PositiveInfinity, IsPropertySaveable.No)]
116  public float FindWeaponsRange { get; protected set; }
117 
118  public Identifier[] PreferredOutpostModuleTypes { get; protected set; }
119 
120  [Serialize("", IsPropertySaveable.No)]
121  public Identifier Faction { get; set; }
122 
123  [Serialize("", IsPropertySaveable.No)]
124  public Identifier Group { get; set; }
125 
126  [Serialize(false, IsPropertySaveable.No)]
127  public bool AllowDraggingIndefinitely { get; set; }
128 
129  public XElement Element { get; protected set; }
130 
131 
132  public readonly List<(ContentXElement element, float commonness)> ItemSets = new List<(ContentXElement element, float commonness)>();
133  public readonly List<(ContentXElement element, float commonness)> CustomCharacterInfos = new List<(ContentXElement element, float commonness)>();
134 
135  public readonly Identifier NpcSetIdentifier;
136 
137  public HumanPrefab(ContentXElement element, ContentFile file, Identifier npcSetIdentifier) : base(file, element.GetAttributeIdentifier("identifier", ""))
138  {
140  Element = element;
141  element.GetChildElements("itemset").ForEach(e => ItemSets.Add((e, e.GetAttributeFloat("commonness", 1))));
142  element.GetChildElements("character").ForEach(e => CustomCharacterInfos.Add((e, e.GetAttributeFloat("commonness", 1))));
143  PreferredOutpostModuleTypes = element.GetAttributeIdentifierArray("preferredoutpostmoduletypes", Array.Empty<Identifier>());
144  this.NpcSetIdentifier = npcSetIdentifier;
145  }
146 
147  public IEnumerable<Identifier> GetTags()
148  {
149  return tags;
150  }
151 
152  public IEnumerable<Identifier> GetModuleFlags()
153  {
154  return moduleFlags;
155  }
156 
157  public IEnumerable<Identifier> GetSpawnPointTags()
158  {
159  return spawnPointTags;
160  }
161 
162  public JobPrefab GetJobPrefab(Rand.RandSync randSync = Rand.RandSync.Unsynced, Func<JobPrefab, bool> predicate = null)
163  {
164  return !Job.IsEmpty && Job != "any" ? JobPrefab.Get(Job) : JobPrefab.Random(randSync, predicate);
165  }
166 
167  public void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn = null)
168  {
169  var humanAI = npc.AIController as HumanAIController;
170  if (humanAI != null)
171  {
172  var idleObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveIdle>();
173  if (positionToStayIn != null && Behavior == AIObjectiveIdle.BehaviorType.StayInHull)
174  {
175  idleObjective.TargetHull = AIObjectiveGoTo.GetTargetHull(positionToStayIn);
176  idleObjective.Behavior = AIObjectiveIdle.BehaviorType.StayInHull;
177  }
178  else
179  {
180  idleObjective.Behavior = Behavior;
181  foreach (Identifier moduleType in PreferredOutpostModuleTypes)
182  {
183  idleObjective.PreferredOutpostModuleTypes.Add(moduleType);
184  }
185  }
186  humanAI.ReportRange = Hearing;
187  humanAI.ReportRange = ReportRange;
188  humanAI.FindWeaponsRange = FindWeaponsRange;
189  humanAI.AimSpeed = AimSpeed;
190  humanAI.AimAccuracy = AimAccuracy;
191  }
193  {
194  (GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(npc, CampaignInteractionType);
195  if (positionToStayIn != null && humanAI != null)
196  {
197  humanAI.ObjectiveManager.SetForcedOrder(new AIObjectiveGoTo(positionToStayIn, npc, humanAI.ObjectiveManager, repeat: true, getDivingGearIfNeeded: false, closeEnough: 200)
198  {
199  FaceTargetOnCompleted = false,
200  DebugLogWhenFails = false,
201  IsWaitOrder = true,
202  CloseEnough = 100
203  });
204  }
205  }
206  }
207 
208  public bool GiveItems(Character character, Submarine submarine, WayPoint spawnPoint, Rand.RandSync randSync = Rand.RandSync.Unsynced, bool createNetworkEvents = true)
209  {
210  if (ItemSets == null || !ItemSets.Any()) { return false; }
211  var spawnItems = ToolBox.SelectWeightedRandom(ItemSets, it => it.commonness, randSync).element;
212  if (spawnItems != null)
213  {
214  foreach (ContentXElement itemElement in spawnItems.GetChildElements("item"))
215  {
216  int amount = itemElement.GetAttributeInt("amount", 1);
217  for (int i = 0; i < amount; i++)
218  {
219  InitializeItem(character, itemElement, submarine, this, spawnPoint, createNetworkEvents: createNetworkEvents);
220  }
221  }
222  }
223  return true;
224  }
225 
231  public CharacterInfo CreateCharacterInfo(Rand.RandSync randSync = Rand.RandSync.Unsynced)
232  {
233  var characterElement = ToolBox.SelectWeightedRandom(CustomCharacterInfos, info => info.commonness, randSync).element;
234  CharacterInfo characterInfo;
235  if (characterElement == null)
236  {
237  characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: GetJobPrefab(randSync), npcIdentifier: Identifier, randSync: randSync);
238  }
239  else
240  {
241  characterInfo = new CharacterInfo(characterElement, Identifier);
242  }
243  if (characterInfo.Job != null && !MathUtils.NearlyEqual(SkillMultiplier, 1.0f))
244  {
245  foreach (var skill in characterInfo.Job.GetSkills())
246  {
247  float newSkill = skill.Level * SkillMultiplier;
248  skill.IncreaseSkill(newSkill - skill.Level, increasePastMax: false);
249  }
250  characterInfo.Salary = characterInfo.CalculateSalary();
251  }
252  characterInfo.HumanPrefabIds = (NpcSetIdentifier, Identifier);
253  characterInfo.GiveExperience(ExperiencePoints);
254  return characterInfo;
255  }
256 
257  public static void InitializeItem(Character character, ContentXElement itemElement, Submarine submarine, HumanPrefab humanPrefab, WayPoint spawnPoint = null, Item parentItem = null, bool createNetworkEvents = true)
258  {
259  ItemPrefab itemPrefab;
260  string itemIdentifier = itemElement.GetAttributeString("identifier", "");
261  itemPrefab = MapEntityPrefab.FindByIdentifier(itemIdentifier.ToIdentifier()) as ItemPrefab;
262  if (itemPrefab == null)
263  {
264  DebugConsole.ThrowError("Tried to spawn \"" + humanPrefab?.Identifier + "\" with the item \"" + itemIdentifier + "\". Matching item prefab not found.",
265  contentPackage: itemElement?.ContentPackage);
266  return;
267  }
268  Item item = new Item(itemPrefab, character.Position, null);
269 #if SERVER
270  if (GameMain.Server != null && Entity.Spawner != null && createNetworkEvents)
271  {
272  if (GameMain.Server.EntityEventManager.UniqueEvents.Any(ev => ev.Entity == item))
273  {
274  string errorMsg = $"Error while spawning job items. Item {item.Name} created network events before the spawn event had been created.";
275  DebugConsole.ThrowError(errorMsg);
276  GameAnalyticsManager.AddErrorEventOnce("Job.InitializeJobItem:EventsBeforeSpawning", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
277  GameMain.Server.EntityEventManager.UniqueEvents.RemoveAll(ev => ev.Entity == item);
278  GameMain.Server.EntityEventManager.Events.RemoveAll(ev => ev.Entity == item);
279  }
280 
281  Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
282  }
283 #endif
284  if (itemElement.GetAttributeBool("equip", false))
285  {
286  //if the item is both pickable and wearable, try to wear it instead of picking it up
287  List<InvSlotType> allowedSlots =
288  item.GetComponents<Pickable>().Count() > 1 ?
289  new List<InvSlotType>(item.GetComponent<Wearable>()?.AllowedSlots ?? item.GetComponent<Pickable>().AllowedSlots) :
290  new List<InvSlotType>(item.AllowedSlots);
291  allowedSlots.Remove(InvSlotType.Any);
292 
293  character.Inventory.TryPutItem(item, null, allowedSlots);
294  }
295  else
296  {
297  character.Inventory.TryPutItem(item, null, item.AllowedSlots);
298  }
299  IdCard idCardComponent = item.GetComponent<IdCard>();
300  if (idCardComponent != null)
301  {
302  idCardComponent.Initialize(spawnPoint, character);
303  if (submarine != null && (submarine.Info.IsWreck || submarine.Info.IsOutpost))
304  {
305  idCardComponent.SubmarineSpecificID = submarine.SubmarineSpecificIDTag;
306  }
307 
308  var idCardTags = itemElement.GetAttributeStringArray("tags", Array.Empty<string>());
309  foreach (string tag in idCardTags)
310  {
311  item.AddTag(tag);
312  }
313  }
314 
315  foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
316  {
317  wifiComponent.TeamID = character.TeamID;
318  }
319  parentItem?.Combine(item, user: null);
320  foreach (ContentXElement childItemElement in itemElement.Elements())
321  {
322  int amount = childItemElement.GetAttributeInt("amount", 1);
323  for (int i = 0; i < amount; i++)
324  {
325  InitializeItem(character, childItemElement, submarine, humanPrefab, spawnPoint, item, createNetworkEvents);
326  }
327  }
328  }
329 
330  public override void Dispose() { }
331  }
332 }
readonly HashSet< Identifier > PreferredOutpostModuleTypes
Stores information about the Character that is needed between rounds in the menu etc....
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
static readonly Identifier HumanSpeciesName
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)
IEnumerable< ContentXElement > GetChildElements(string name)
IEnumerable< ContentXElement > Elements()
bool GetAttributeBool(string key, bool def)
int GetAttributeInt(string key, int def)
string?[] GetAttributeStringArray(string key, string[]? def, bool convertToLowerInvariant=false)
static EntitySpawner Spawner
Definition: Entity.cs:31
static GameSession?? GameSession
Definition: GameMain.cs:88
IEnumerable< Identifier > GetSpawnPointTags()
Definition: HumanPrefab.cs:157
CharacterInfo CreateCharacterInfo(Rand.RandSync randSync=Rand.RandSync.Unsynced)
Creates a character info from the human prefab. If there are custom character infos defined,...
Definition: HumanPrefab.cs:231
readonly List<(ContentXElement element, float commonness)> CustomCharacterInfos
Definition: HumanPrefab.cs:133
HumanPrefab(ContentXElement element, ContentFile file, Identifier npcSetIdentifier)
Definition: HumanPrefab.cs:137
readonly List<(ContentXElement element, float commonness)> ItemSets
Definition: HumanPrefab.cs:132
override void Dispose()
Definition: HumanPrefab.cs:330
IEnumerable< Identifier > GetTags()
Definition: HumanPrefab.cs:147
float HealthMultiplierInMultiplayer
Definition: HumanPrefab.cs:22
void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn=null)
Definition: HumanPrefab.cs:167
JobPrefab GetJobPrefab(Rand.RandSync randSync=Rand.RandSync.Unsynced, Func< JobPrefab, bool > predicate=null)
Definition: HumanPrefab.cs:162
IEnumerable< Identifier > GetModuleFlags()
Definition: HumanPrefab.cs:152
bool GiveItems(Character character, Submarine submarine, WayPoint spawnPoint, Rand.RandSync randSync=Rand.RandSync.Unsynced, bool createNetworkEvents=true)
Definition: HumanPrefab.cs:208
static void InitializeItem(Character character, ContentXElement itemElement, Submarine submarine, HumanPrefab humanPrefab, WayPoint spawnPoint=null, Item parentItem=null, bool createNetworkEvents=true)
Definition: HumanPrefab.cs:257
readonly Identifier NpcSetIdentifier
Definition: HumanPrefab.cs:135
AIObjectiveIdle.BehaviorType Behavior
Definition: HumanPrefab.cs:104
Identifier[] PreferredOutpostModuleTypes
Definition: HumanPrefab.cs:118
CampaignMode.InteractionType CampaignInteractionType
Definition: HumanPrefab.cs:101
IEnumerable< InvSlotType > AllowedSlots
List< InvSlotType > AllowedSlots
Definition: Pickable.cs:25
IEnumerable< Skill > GetSkills()
Definition: Job.cs:84
static JobPrefab Random(Rand.RandSync sync, Func< JobPrefab, bool > predicate=null)
static MapEntityPrefab FindByIdentifier(Identifier identifier)
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)