Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/GameSession/CrewManager.cs
2 #if CLIENT
4 #endif
5 using Microsoft.Xna.Framework;
6 using System;
7 using System.Collections.Generic;
8 using System.Linq;
9 using System.Xml.Linq;
11 
12 namespace Barotrauma
13 {
14 
18  partial class CrewManager
19  {
20  const float ConversationIntervalMin = 100.0f;
21  const float ConversationIntervalMax = 180.0f;
22  const float ConversationIntervalMultiplierMultiplayer = 5.0f;
23  private float conversationTimer, conversationLineTimer;
24  private readonly List<(Character speaker, string line)> pendingConversationLines = new List<(Character speaker, string line)>();
25 
26  public const int MaxCrewSize = 16;
27 
28  private readonly List<CharacterInfo> characterInfos = new List<CharacterInfo>();
29  private readonly List<Character> characters = new List<Character>();
30 
31  public IEnumerable<Character> GetCharacters()
32  {
33  return characters;
34  }
39  public IEnumerable<CharacterInfo> GetCharacterInfos()
40  {
41  return characterInfos;
42  }
43 
44  private Character welcomeMessageNPC;
45 
46  public bool HasBots { get; set; }
47 
48  public class ActiveOrder
49  {
50  public readonly Order Order;
51  public float? FadeOutTime;
52  public ActiveOrder(Order order, float? fadeOutTime)
53  {
54  Order = order;
55  FadeOutTime = fadeOutTime;
56  }
57  }
58  public List<ActiveOrder> ActiveOrders { get; } = new List<ActiveOrder>();
59  public bool IsSinglePlayer { get; private set; }
60 
61  public ReadyCheck ActiveReadyCheck;
62 
63  public CrewManager(bool isSinglePlayer)
64  {
65  IsSinglePlayer = isSinglePlayer;
66  conversationTimer = 5.0f;
67  InitProjectSpecific();
68  }
69 
70  partial void InitProjectSpecific();
71 
72  public bool AddOrder(Order order, float? fadeOutTime)
73  {
74  if (order.TargetEntity == null)
75  {
76  string message = $"Attempted to add a \"{order.Name}\" order with no target entity to CrewManager!\n{Environment.StackTrace.CleanupStackTrace()}";
77  DebugConsole.AddWarning(message);
78  GameAnalyticsManager.AddErrorEventOnce("CrewManager.AddOrder:OrderTargetEntityNull", GameAnalyticsManager.ErrorSeverity.Error, message);
79  return false;
80  }
81 
82  // Ignore orders work a bit differently since the "unignore" order counters the "ignore" order
83  var isUnignoreOrder = order.Identifier == Tags.UnignoreThis;
84  var orderPrefab = !isUnignoreOrder ? order.Prefab : OrderPrefab.Prefabs[Tags.IgnoreThis];
85  ActiveOrder existingOrder = ActiveOrders.Find(o =>
86  o.Order.Prefab == orderPrefab && MatchesTarget(o.Order.TargetEntity, order.TargetEntity) &&
87  (o.Order.TargetType != Order.OrderTargetType.WallSection || o.Order.WallSectionIndex == order.WallSectionIndex));
88 
89  if (existingOrder != null)
90  {
91  if (!isUnignoreOrder)
92  {
93  existingOrder.FadeOutTime = fadeOutTime;
94  return false;
95  }
96  else
97  {
98  ActiveOrders.Remove(existingOrder);
99  return true;
100  }
101  }
102  else if (!isUnignoreOrder)
103  {
104  if (order.IsDeconstructOrder)
105  {
106  if (order.TargetEntity is Item item)
107  {
108  if (order.Identifier == Tags.DeconstructThis)
109  {
110  foreach (var stackedItem in item.GetStackedItems())
111  {
112  Item.DeconstructItems.Add(stackedItem);
113  }
114 #if CLIENT
115  HintManager.OnItemMarkedForDeconstruction(order.OrderGiver);
116 #endif
117  }
118  else
119  {
120  foreach (var stackedItem in item.GetStackedItems())
121  {
122  Item.DeconstructItems.Remove(stackedItem);
123  }
124  }
125  }
126  }
127  ActiveOrders.Add(new ActiveOrder(order, fadeOutTime));
128 #if CLIENT
129  HintManager.OnActiveOrderAdded(order);
130 #endif
131  return true;
132  }
133 
134  static bool MatchesTarget(Entity existingTarget, Entity newTarget)
135  {
136  if (existingTarget == newTarget) { return true; }
137  if (existingTarget is Hull existingHullTarget && newTarget is Hull newHullTarget)
138  {
139  return existingHullTarget.linkedTo.Contains(newHullTarget);
140  }
141  return false;
142  }
143 
144  return false;
145  }
146 
147  public void AddCharacterElements(XElement element)
148  {
149  foreach (var characterElement in element.Elements())
150  {
151  if (!characterElement.Name.ToString().Equals("character", StringComparison.OrdinalIgnoreCase)) { continue; }
152  CharacterInfo characterInfo = new CharacterInfo(new ContentXElement(contentPackage: null, characterElement));
153 #if CLIENT
154  if (characterElement.GetAttributeBool("lastcontrolled", false)) { characterInfo.LastControlled = true; }
155  characterInfo.CrewListIndex = characterElement.GetAttributeInt("crewlistindex", -1);
156 #endif
157  characterInfos.Add(characterInfo);
158  foreach (var subElement in characterElement.Elements())
159  {
160  switch (subElement.Name.ToString().ToLowerInvariant())
161  {
162  case "inventory":
163  characterInfo.InventoryData = subElement;
164  break;
165  case "health":
166  characterInfo.HealthData = subElement;
167  break;
168  case "orders":
169  characterInfo.OrderData = subElement;
170  break;
171  }
172  }
173  }
174  }
175 
180  public void RemoveCharacterInfo(CharacterInfo characterInfo)
181  {
182  characterInfos.Remove(characterInfo);
183  }
184 
185  public void AddCharacter(Character character, bool sortCrewList = true)
186  {
187  if (character.Removed)
188  {
189  DebugConsole.ThrowError("Tried to add a removed character to CrewManager!\n" + Environment.StackTrace.CleanupStackTrace());
190  return;
191  }
192  if (character.IsDead)
193  {
194  DebugConsole.ThrowError("Tried to add a dead character to CrewManager!\n" + Environment.StackTrace.CleanupStackTrace());
195  return;
196  }
197  if (character.Info == null)
198  {
199  if (character.Prefab.ContentPackage == GameMain.VanillaContent)
200  {
201  DebugConsole.ThrowError($"Added a character with no {nameof(CharacterInfo)} to the crew." + Environment.StackTrace.CleanupStackTrace());
202  }
203  else
204  {
205  DebugConsole.ThrowError($"Added add a character with no {nameof(CharacterInfo)} to the crew. This may lead to issues: consider adding {nameof(CharacterPrefab.HasCharacterInfo)}=\"True\" to the character config.");
206  }
207  }
208 
209  if (!characters.Contains(character))
210  {
211  characters.Add(character);
212  }
213  if (!characterInfos.Contains(character.Info))
214  {
215  characterInfos.Add(character.Info);
216  }
217 #if CLIENT
218  var characterComponent = AddCharacterToCrewList(character);
219  if (sortCrewList)
220  {
221  SortCrewList();
222  }
223  if (character.CurrentOrders != null)
224  {
225  foreach (var order in character.CurrentOrders)
226  {
227  AddCurrentOrderIcon(character, order);
228  }
229  }
230 #endif
231  if (character.AIController is HumanAIController humanAI)
232  {
233  var idleObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveIdle>();
234  if (idleObjective != null)
235  {
236  idleObjective.Behavior = character.Info.Job.Prefab.IdleBehavior;
237  }
238  }
239  }
240 
241  public bool IsFired(Character character)
242  {
243  return !GetCharacterInfos().Contains(character.Info);
244  }
245 
251  public void RemoveCharacter(Character character, bool removeInfo = false, bool resetCrewListIndex = true)
252  {
253  if (character == null)
254  {
255  DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace.CleanupStackTrace());
256  return;
257  }
258  characters.Remove(character);
259  if (removeInfo)
260  {
261  characterInfos.Remove(character.Info);
262 #if CLIENT
263  RemoveCharacterFromCrewList(character);
264 #endif
265  }
266 #if CLIENT
267  if (resetCrewListIndex)
268  {
269  ResetCrewListIndex(character);
270  }
271 #endif
272  }
273 
274  public void AddCharacterInfo(CharacterInfo characterInfo)
275  {
276  if (characterInfos.Contains(characterInfo))
277  {
278  DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace.CleanupStackTrace());
279  return;
280  }
281 
282  characterInfos.Add(characterInfo);
283  }
284 
285  public void ClearCharacterInfos()
286  {
287  characterInfos.Clear();
288  }
289 
290  public void InitRound()
291  {
292 #if CLIENT
293  GUIContextMenu.CurrentContextMenu = null;
294 #endif
295 
296  characters.Clear();
297 
298  List<WayPoint> spawnWaypoints = null;
299  List<WayPoint> mainSubWaypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub).ToList();
300 
302  {
303  spawnWaypoints = GetOutpostSpawnpoints();
304  while (spawnWaypoints.Count > characterInfos.Count)
305  {
306  spawnWaypoints.RemoveAt(Rand.Int(spawnWaypoints.Count));
307  }
308  while (spawnWaypoints.Any() && spawnWaypoints.Count < characterInfos.Count)
309  {
310  spawnWaypoints.Add(spawnWaypoints[Rand.Int(spawnWaypoints.Count)]);
311  }
312  }
313  if (spawnWaypoints == null || !spawnWaypoints.Any())
314  {
315  spawnWaypoints = mainSubWaypoints;
316  }
317 
318  System.Diagnostics.Debug.Assert(spawnWaypoints.Count == mainSubWaypoints.Count);
319 
320  for (int i = 0; i < spawnWaypoints.Count; i++)
321  {
322  var info = characterInfos[i];
323  info.TeamID = CharacterTeamType.Team1;
324  Character character = Character.Create(info, spawnWaypoints[i].WorldPosition, info.Name);
325  InitializeCharacter(character, mainSubWaypoints[i], spawnWaypoints[i]);
326 
327  AddCharacter(character, sortCrewList: false);
328 #if CLIENT
329  if (IsSinglePlayer && (Character.Controlled == null || character.Info.LastControlled)) { Character.Controlled = character; }
330 #endif
331  }
332 
333 #if CLIENT
334  if (IsSinglePlayer) { SortCrewList(); }
335 #endif
336 
337  //longer delay in multiplayer to prevent the server from triggering NPC conversations while the players are still loading the round
338  conversationTimer = IsSinglePlayer ? Rand.Range(5.0f, 10.0f) : Rand.Range(45.0f, 60.0f);
339  }
340 
344  public List<WayPoint> GetOutpostSpawnpoints()
345  {
346  return WayPoint.WayPointList.FindAll(wp =>
347  wp.SpawnType == SpawnType.Human &&
348  wp.Submarine == Level.Loaded.StartOutpost &&
349  wp.CurrentHull != null &&
350  wp.CurrentHull.OutpostModuleTags.Contains("airlock".ToIdentifier()));
351  }
352 
353  public void InitializeCharacter(Character character, WayPoint mainSubWaypoint, WayPoint spawnWaypoint)
354  {
355  if (character.Info != null)
356  {
357  if (!character.Info.StartItemsGiven && character.Info.InventoryData != null)
358  {
359  DebugConsole.AddWarning($"Error when initializing a round: character \"{character.Name}\" has not been given their initial items but has saved inventory data. Using the saved inventory data instead of giving the character new items.");
360  }
361  if (character.Info.InventoryData != null)
362  {
363  character.SpawnInventoryItems(character.Inventory, character.Info.InventoryData.FromPackage(null));
364  }
365  else if (!character.Info.StartItemsGiven)
366  {
367  character.GiveJobItems(mainSubWaypoint);
368  foreach (Item item in character.Inventory.AllItems)
369  {
370  //if the character is loaded from a human prefab with preconfigured items, its ID card gets assigned to the sub it spawns in
371  //we don't want that in this case, the crew's cards shouldn't be submarine-specific
372  var idCard = item.GetComponent<Items.Components.IdCard>();
373  if (idCard != null)
374  {
375  idCard.SubmarineSpecificID = 0;
376  }
377  }
378  }
379  if (character.Info.HealthData != null)
380  {
381  CharacterInfo.ApplyHealthData(character, character.Info.HealthData);
382  }
383 
384  character.LoadTalents();
385 
386  character.GiveIdCardTags(mainSubWaypoint);
387  character.GiveIdCardTags(spawnWaypoint);
388  character.Info.StartItemsGiven = true;
389  if (character.Info.OrderData != null)
390  {
391  character.Info.ApplyOrderData();
392  }
393  }
394  }
395 
396  public void RenameCharacter(CharacterInfo characterInfo, string newName)
397  {
398  characterInfo.Rename(newName);
399  RenameCharacterProjSpecific(characterInfo);
400  }
401 
402  partial void RenameCharacterProjSpecific(CharacterInfo characterInfo);
403 
404  public void FireCharacter(CharacterInfo characterInfo)
405  {
406  RemoveCharacterInfo(characterInfo);
407  }
408 
409  public void ClearCurrentOrders()
410  {
411  foreach (var characterInfo in characterInfos)
412  {
413  characterInfo?.ClearCurrentOrders();
414  }
415  }
416 
417  public void Update(float deltaTime)
418  {
419  foreach (ActiveOrder order in ActiveOrders)
420  {
421  if (order.FadeOutTime.HasValue) { order.FadeOutTime -= deltaTime; }
422  }
423  ActiveOrders.RemoveAll(o => (o.FadeOutTime.HasValue && o.FadeOutTime <= 0.0f) ||
424  (o.Order.TargetEntity != null && o.Order.TargetEntity.Removed));
425 
426  UpdateConversations(deltaTime);
427  UpdateProjectSpecific(deltaTime);
428  ActiveReadyCheck?.Update(deltaTime);
429  if (ActiveReadyCheck != null && ActiveReadyCheck.IsFinished)
430  {
431  ActiveReadyCheck = null;
432  }
433  }
434 
435  #region Dialog
436 
437  public void AddConversation(List<(Character speaker, string line)> conversationLines)
438  {
439  if (conversationLines == null || conversationLines.Count == 0) { return; }
440  pendingConversationLines.AddRange(conversationLines);
441  }
442 
443  partial void CreateRandomConversation();
444 
445  private void UpdateConversations(float deltaTime)
446  {
448 #if CLIENT
449  if (GameMain.GameSession?.GameMode is TutorialMode tutorialMode && tutorialMode.Tutorial is Tutorial tutorial && tutorial.TutorialPrefab.DisableBotConversations) { return; }
450 #endif
451  if (GameMain.NetworkMember != null && GameMain.NetworkMember.ServerSettings.DisableBotConversations) { return; }
452 
453  conversationTimer -= deltaTime;
454  if (conversationTimer <= 0.0f)
455  {
456  CreateRandomConversation();
457  conversationTimer = Rand.Range(ConversationIntervalMin, ConversationIntervalMax);
458  if (GameMain.NetworkMember != null)
459  {
460  conversationTimer *= ConversationIntervalMultiplierMultiplayer;
461  }
462  }
463 
464  if (welcomeMessageNPC == null)
465  {
466  foreach (Character npc in Character.CharacterList)
467  {
468  if ((npc.TeamID != CharacterTeamType.FriendlyNPC && npc.TeamID != CharacterTeamType.None) || npc.CurrentHull == null || npc.IsIncapacitated) { continue; }
469  if (npc.AIController is HumanAIController humanAI && (humanAI.ObjectiveManager.IsCurrentObjective<AIObjectiveFindSafety>() || humanAI.ObjectiveManager.IsCurrentObjective<AIObjectiveCombat>()))
470  {
471  continue;
472  }
473  foreach (Character player in Character.CharacterList)
474  {
475  if (player.TeamID != npc.TeamID && !player.IsIncapacitated && player.CurrentHull == npc.CurrentHull)
476  {
477  List<Character> availableSpeakers = new List<Character>() { npc, player };
478  List<Identifier> dialogFlags = new List<Identifier>() { "OutpostNPC".ToIdentifier(), "EnterOutpost".ToIdentifier() };
479  if (npc.HumanPrefab != null)
480  {
481  foreach (var tag in npc.HumanPrefab.GetTags())
482  {
483  dialogFlags.Add(tag);
484  }
485  }
486  if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
487  {
488  if (campaignMode.Map?.CurrentLocation?.Type?.Identifier == "abandoned")
489  {
490  dialogFlags.Remove("OutpostNPC".ToIdentifier());
491  }
492  else if (campaignMode.Map?.CurrentLocation?.Reputation != null)
493  {
494  float normalizedReputation = MathUtils.InverseLerp(
495  campaignMode.Map.CurrentLocation.Reputation.MinReputation,
496  campaignMode.Map.CurrentLocation.Reputation.MaxReputation,
497  campaignMode.Map.CurrentLocation.Reputation.Value);
498  if (normalizedReputation < 0.2f)
499  {
500  dialogFlags.Add("LowReputation".ToIdentifier());
501  }
502  else if (normalizedReputation > 0.8f)
503  {
504  dialogFlags.Add("HighReputation".ToIdentifier());
505  }
506  }
507  }
508  pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers, dialogFlags));
509  welcomeMessageNPC = npc;
510  break;
511  }
512  }
513  if (welcomeMessageNPC != null) { break; }
514  }
515  }
516  else if (welcomeMessageNPC.Removed)
517  {
518  welcomeMessageNPC = null;
519  }
520 
521  if (pendingConversationLines.Count > 0)
522  {
523  conversationLineTimer -= deltaTime;
524  if (conversationLineTimer <= 0.0f)
525  {
526  //speaker of the next line can't speak, interrupt the conversation
527  if (pendingConversationLines[0].speaker.SpeechImpediment >= 100.0f)
528  {
529  pendingConversationLines.Clear();
530  return;
531  }
532 
533  pendingConversationLines[0].speaker.Speak(pendingConversationLines[0].line, null);
534  if (pendingConversationLines.Count > 1)
535  {
536  conversationLineTimer = MathHelper.Clamp(pendingConversationLines[0].line.Length * 0.1f, 1.0f, 5.0f);
537  }
538  pendingConversationLines.RemoveAt(0);
539  }
540  }
541  }
542 
543 #endregion
544 
545  public static Character GetCharacterForQuickAssignment(Order order, Character controlledCharacter, IEnumerable<Character> characters, bool includeSelf = false)
546  {
547  bool isControlledCharacterNull = controlledCharacter == null;
548 #if !DEBUG
549  if (isControlledCharacterNull) { return null; }
550 #endif
551  if (order.Category == OrderCategory.Operate && HumanAIController.IsItemTargetedBySomeone(order.TargetItemComponent, controlledCharacter != null ? controlledCharacter.TeamID : CharacterTeamType.Team1, out Character operatingCharacter) &&
552  (isControlledCharacterNull || operatingCharacter.CanHearCharacter(controlledCharacter)))
553  {
554  return operatingCharacter;
555  }
556  return GetCharactersSortedForOrder(order, characters, controlledCharacter, includeSelf).FirstOrDefault(c => isControlledCharacterNull || c.CanHearCharacter(controlledCharacter)) ?? controlledCharacter;
557  }
558 
559  public static IEnumerable<Character> GetCharactersSortedForOrder(Order order, IEnumerable<Character> characters, Character controlledCharacter, bool includeSelf, IEnumerable<Character> extraCharacters = null)
560  {
561  var filteredCharacters = characters.Where(c => controlledCharacter == null || ((includeSelf || c != controlledCharacter) && c.TeamID == controlledCharacter.TeamID));
562  if (extraCharacters != null)
563  {
564  filteredCharacters = filteredCharacters.Union(extraCharacters);
565  }
566  return filteredCharacters
567  // Prioritize those who are on the same submarine as the controlled character
568  .OrderByDescending(c => Character.Controlled == null || c.Submarine == Character.Controlled.Submarine)
569  // Prioritize those who are already ordered to operate the device
570  .ThenByDescending(c
571  => order.Category == OrderCategory.Operate
572  && c.CurrentOrders.Any(o
573  => o != null
574  && o.Identifier == order.Identifier
575  && o.TargetEntity == order.TargetEntity))
576  // Prioritize those with the appropriate job for the order
577  .ThenByDescending(order.HasAppropriateJob)
578  // Prioritize those who don't yet have the same order (which allows quick-assigning the order to different characters)
579  .ThenByDescending(c => c.CurrentOrders.None(o => o != null && o.Identifier == order.Identifier))
580  // Prioritize those with the preferred job for the order
581  .ThenByDescending(order.HasPreferredJob)
582  // Prioritize bots over player-controlled characters
583  .ThenByDescending(c => c.IsBot)
584  // Prioritize those with a lower current objective priority
585  .ThenBy(c => c.AIController is HumanAIController humanAI ? humanAI.ObjectiveManager.CurrentObjective?.Priority : 0)
586  // Prioritize those with a higher order skill level
587  .ThenByDescending(c => c.GetSkillLevel(order.AppropriateSkill));
588  }
589 
590  partial void UpdateProjectSpecific(float deltaTime);
591 
592  public void SaveActiveOrders(XElement element)
593  {
594  // Only save orders with no fade out time (e.g. ignore orders)
595  var ordersToSave = new List<Order>();
596  foreach (var activeOrder in ActiveOrders)
597  {
598  var order = activeOrder?.Order;
599  if (order == null || activeOrder.FadeOutTime.HasValue) { continue; }
600  ordersToSave.Add(order.WithManualPriority(CharacterInfo.HighestManualOrderPriority));
601  }
602  CharacterInfo.SaveOrders(element, ordersToSave.ToArray());
603  }
604 
605  public void LoadActiveOrders(XElement element)
606  {
607  if (element == null) { return; }
608  foreach (var orderInfo in CharacterInfo.LoadOrders(element))
609  {
610  IIgnorable ignoreTarget = null;
611  if (orderInfo.IsIgnoreOrder)
612  {
613  switch (orderInfo.TargetType)
614  {
615  case Order.OrderTargetType.Entity:
616  ignoreTarget = orderInfo.TargetEntity as IIgnorable;
617  break;
618  case Order.OrderTargetType.WallSection when orderInfo.TargetEntity is Structure s && orderInfo.WallSectionIndex.HasValue:
619  ignoreTarget = s.GetSection(orderInfo.WallSectionIndex.Value);
620  break;
621  default:
622  DebugConsole.ThrowError("Error loading an ignore order - can't find a proper ignore target");
623  continue;
624  }
625  }
626  if (orderInfo.TargetEntity == null || (orderInfo.IsIgnoreOrder && ignoreTarget == null))
627  {
628  // The order target doesn't exist anymore, just discard the loaded order
629  continue;
630  }
631  if (ignoreTarget != null)
632  {
633  ignoreTarget.OrderedToBeIgnored = true;
634  }
635  AddOrder(orderInfo, null);
636  }
637  }
638  }
639 }
float Priority
Final priority value after all calculations.
AIObjective CurrentObjective
Includes orders.
static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id=Entity.NullEntityID, bool isRemotePlayer=false, bool hasAi=true, RagdollParams ragdoll=null, bool spawnInitialItems=true)
Create a new character
void GiveIdCardTags(WayPoint spawnPoint, bool createNetworkEvent=false)
void SpawnInventoryItems(Inventory inventory, ContentXElement itemData)
Stores information about the Character that is needed between rounds in the menu etc....
static void SaveOrders(XElement parentElement, params Order[] orders)
static void ApplyHealthData(Character character, XElement healthData, Func< AfflictionPrefab, bool > afflictionPredicate=null)
static List< Order > LoadOrders(XElement ordersElement)
static void ApplyOrderData(Character character, XElement orderData)
Responsible for keeping track of the characters in the player crew, saving and loading their orders,...
void RemoveCharacter(Character character, bool removeInfo=false, bool resetCrewListIndex=true)
Remove the character from the crew (and crew menus).
static Character GetCharacterForQuickAssignment(Order order, Character controlledCharacter, IEnumerable< Character > characters, bool includeSelf=false)
void RemoveCharacterInfo(CharacterInfo characterInfo)
Remove info of a selected character. The character will not be visible in any menus or the round summ...
IEnumerable< CharacterInfo > GetCharacterInfos()
Note: this only returns AI characters' infos in multiplayer. The infos are used to manage hiring/firi...
static IEnumerable< Character > GetCharactersSortedForOrder(Order order, IEnumerable< Character > characters, Character controlledCharacter, bool includeSelf, IEnumerable< Character > extraCharacters=null)
void AddCurrentOrderIcon(Character character, Order order)
Displays the specified order in the crew UI next to the character.
List< WayPoint > GetOutpostSpawnpoints()
Returns the potential crew spawnpositions for the crew in the loaded outpost
void AddConversation(List<(Character speaker, string line)> conversationLines)
GUIComponent AddCharacterToCrewList(Character character)
Add character to the list without actually adding it to the crew
void InitializeCharacter(Character character, WayPoint mainSubWaypoint, WayPoint spawnWaypoint)
void RenameCharacter(CharacterInfo characterInfo, string newName)
void AddCharacter(Character character, bool sortCrewList=true)
Submarine Submarine
Definition: Entity.cs:53
static GameSession?? GameSession
Definition: GameMain.cs:88
static ContentPackage VanillaContent
Definition: GameMain.cs:84
static GameModePreset TestMode
static bool IsItemTargetedBySomeone(ItemComponent target, CharacterTeamType team, out Character operatingCharacter)
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
static HashSet< Item > DeconstructItems
Items that have been marked for deconstruction
JobPrefab Prefab
Definition: Job.cs:18
readonly Entity TargetEntity
Definition: Order.cs:495
bool IsDeconstructOrder
Definition: Order.cs:559
readonly Character OrderGiver
Definition: Order.cs:499
readonly? int WallSectionIndex
Definition: Order.cs:536
bool HasPreferredJob(Character character)
Identifier AppropriateSkill
Definition: Order.cs:555
OrderCategory? Category
Definition: Order.cs:556
Identifier Identifier
Definition: Order.cs:540
readonly OrderPrefab Prefab
Definition: Order.cs:481
bool HasAppropriateJob(Character character)
readonly ItemComponent TargetItemComponent
Definition: Order.cs:496
static readonly PrefabCollection< OrderPrefab > Prefabs
Definition: Order.cs:41
ContentPackage? ContentPackage
Definition: Prefab.cs:37
readonly bool DisableBotConversations
readonly TutorialPrefab TutorialPrefab
Definition: Tutorial.cs:49
static WayPoint[] SelectCrewSpawnPoints(List< CharacterInfo > crew, Submarine submarine)
OrderCategory
Definition: Order.cs:12