Client LuaCsForBarotrauma
Order.cs
3 using Microsoft.Xna.Framework;
4 using System;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Linq;
8 
9 namespace Barotrauma
10 {
11  public enum OrderCategory
12  {
13  Emergency,
14  Movement,
15  Power,
17  Operate
18  }
19 
21  {
23 
24  public OrderCategoryIcon(ContentXElement element, OrdersFile file) : base(file, element.GetAttributeIdentifier("category", ""))
25  {
26  Category = Enum.Parse<OrderCategory>(Identifier.Value, true);
27  var spriteElement = element.GetChildElement("sprite");
28  Sprite = new Sprite(spriteElement, lazyLoad: true);
29  Color = element.GetAttributeColor("color", Color.White);
30  }
31 
32  public readonly OrderCategory Category;
33  public readonly Sprite Sprite;
34  public readonly Color Color;
35 
36  public override void Dispose() { Sprite?.Remove(); }
37  }
38 
40  {
42 
43  public readonly static Identifier DismissalIdentifier = "dismissed".ToIdentifier();
45 
46  public readonly OrderCategory? Category;
47  public readonly Identifier CategoryIdentifier;
48 
49  public readonly LocalizedString Name;
54 
55  public readonly Sprite SymbolSprite;
56 
57  public readonly Type ItemComponentType;
58  public readonly bool CanTypeBeSubclass;
59  public readonly ImmutableArray<Identifier> TargetItems;
60  public readonly ImmutableArray<Identifier> RequireItems;
61  private readonly ImmutableDictionary<Identifier, ImmutableArray<Identifier>> OptionTargetItems;
62  public bool HasOptionSpecificTargetItems => OptionTargetItems != null && OptionTargetItems.Any();
63 
64  private readonly Color? color;
65  public Color Color
66  {
67  get
68  {
69  if (color.HasValue)
70  {
71  return color.Value;
72  }
74  {
75  return OrderCategoryIcon.OrderCategoryIcons[Category.ToIdentifier()].Color;
76  }
77  else
78  {
79  return Color.White;
80  }
81  }
82  }
83 
84  //if true, the order is issued to all available characters
85  public readonly bool TargetAllCharacters;
87 
88  public bool IsVisibleAsReportButton =>
89  IsReport && !Hidden && SymbolSprite != null &&
90  (!TraitorModeOnly || GameMain.GameSession is { TraitorsEnabled: true });
91 
92  public bool TraitorModeOnly;
93 
95 
96  public readonly float FadeOutTime;
97 
98  public readonly bool UseController;
99 
100  public readonly ImmutableArray<Identifier> ControllerTags;
101 
105  public readonly ImmutableArray<Identifier> AppropriateJobs;
106  public readonly ImmutableArray<Identifier> Options;
107  public readonly ImmutableArray<Identifier> HiddenOptions;
108  public readonly ImmutableArray<Identifier> AllOptions;
110 
111  public readonly ImmutableDictionary<Identifier, Sprite> OptionSprites;
112 
113  public readonly bool MustSetTarget;
118  public readonly bool CanBeGeneralized;
119  public readonly Identifier AppropriateSkill;
120  public readonly bool Hidden;
121  public readonly bool IgnoreAtOutpost;
122 
123  public bool HasOptions => Options.Length > 1;
124  public readonly bool MustManuallyAssign;
125 
130  public readonly bool AutoDismiss;
131 
135  public readonly ImmutableArray<Identifier> PreferredJobs;
136 
137  public enum OrderTargetType
138  {
139  Entity,
140  Position,
142  }
143  public OrderTargetType TargetType { get; }
144  public int? WallSectionIndex { get; }
145  public bool IsIgnoreOrder => Identifier == Tags.IgnoreThis || Identifier == Tags.UnignoreThis;
146 
147  public bool IsDeconstructOrder => Identifier == Tags.DeconstructThis || Identifier == Tags.DontDeconstructThis;
148 
152  public bool DrawIconWhenContained { get; }
153 
158  public int AssignmentPriority { get; }
159 
160  public bool ColoredWhenControllingGiver { get; }
161  public bool DisplayGiverInTooltip { get; }
162 
163  public OrderPrefab(ContentXElement orderElement, OrdersFile file) : base(file, orderElement.GetAttributeIdentifier("identifier", ""))
164  {
165  Name = TextManager.Get($"OrderName.{Identifier}");
166  ContextualName = TextManager.Get($"OrderNameContextual.{Identifier}").Fallback(Name);
167 
168  string targetItemType = orderElement.GetAttributeString("targetitemtype", "");
169  if (!string.IsNullOrWhiteSpace(targetItemType))
170  {
171  try
172  {
173  ItemComponentType = Type.GetType("Barotrauma.Items.Components." + targetItemType, true, true);
174  }
175 
176  catch (Exception e)
177  {
178  DebugConsole.ThrowError("Error in the order definitions: item component type " + targetItemType + " not found", e);
179  }
180  }
181  CanTypeBeSubclass = orderElement.GetAttributeBool("cantypebesubclass", false);
182  color = orderElement.GetAttributeColor("color");
183  FadeOutTime = orderElement.GetAttributeFloat("fadeouttime", 0.0f);
184  UseController = orderElement.GetAttributeBool("usecontroller", false);
185  ControllerTags = orderElement.GetAttributeIdentifierArray("controllertags", Array.Empty<Identifier>()).ToImmutableArray();
186  TargetAllCharacters = orderElement.GetAttributeBool("targetallcharacters", false);
187  AppropriateJobs = orderElement.GetAttributeIdentifierArray("appropriatejobs", Array.Empty<Identifier>()).ToImmutableArray();
188  TraitorModeOnly = orderElement.GetAttributeBool("TraitorModeOnly", false);
189  PreferredJobs = orderElement.GetAttributeIdentifierArray("preferredjobs", Array.Empty<Identifier>()).ToImmutableArray();
190  Options = orderElement.GetAttributeIdentifierArray("options", Array.Empty<Identifier>()).ToImmutableArray();
191  HiddenOptions = orderElement.GetAttributeIdentifierArray("hiddenoptions", Array.Empty<Identifier>()).ToImmutableArray();
192  AllOptions = Options.Concat(HiddenOptions).ToImmutableArray();
193 
194  var optionTargetItems = new Dictionary<Identifier, ImmutableArray<Identifier>>();
195  if (orderElement.GetAttributeString("targetitems", "") is string targetItems && targetItems.Contains(';'))
196  {
197  string[] splitTargetItems = targetItems.Split(';');
198 #if DEBUG
199  if (splitTargetItems.Length != AllOptions.Length)
200  {
201  DebugConsole.ThrowError($"Order \"{Identifier}\" has option-specific target items, but the option count doesn't match the target item count");
202  }
203 #endif
204  var allTargetItems = new List<Identifier>();
205  for (int i = 0; i < AllOptions.Length; i++)
206  {
207  Identifier[] optionTargetItemsSplit = i < splitTargetItems.Length ? splitTargetItems[i].ToIdentifiers().ToArray() : Array.Empty<Identifier>();
208  allTargetItems.AddRange(optionTargetItemsSplit);
209  optionTargetItems.Add(AllOptions[i], optionTargetItemsSplit.ToImmutableArray());
210  }
211  TargetItems = allTargetItems.ToImmutableArray();
212  }
213  else
214  {
215  TargetItems = orderElement.GetAttributeIdentifierArray("targetitems", Array.Empty<Identifier>(), trim: true).ToImmutableArray();
216  }
217  RequireItems = orderElement.GetAttributeIdentifierArray("requireitems", Array.Empty<Identifier>(), trim: true).ToImmutableArray();
218  OptionTargetItems = optionTargetItems.ToImmutableDictionary();
219 
220  var category = orderElement.GetAttributeString("category", null);
221  this.Category = !string.IsNullOrWhiteSpace(category) ? Enum.Parse<OrderCategory>(category, true) : (OrderCategory?)null;
222  this.CategoryIdentifier = (this.Category?.ToString() ?? string.Empty).ToIdentifier();
223  MustSetTarget = orderElement.GetAttributeBool("mustsettarget", false);
224  CanBeGeneralized = !MustSetTarget && orderElement.GetAttributeBool("canbegeneralized", true);
225  AppropriateSkill = orderElement.GetAttributeIdentifier("appropriateskill", Identifier.Empty);
226  Hidden = orderElement.GetAttributeBool("hidden", false);
227  IgnoreAtOutpost = orderElement.GetAttributeBool("ignoreatoutpost", false);
228 
229  OptionNames =
231  TextManager.Get("OrderOptions." + Identifier).Split(',', ','), Options.Length, i => Options[i]);
232 
233  var spriteElement = orderElement.GetChildElement("sprite");
234  if (spriteElement != null)
235  {
236  SymbolSprite = new Sprite(spriteElement, lazyLoad: true);
237  }
238 
239  var optionSprites = new Dictionary<Identifier, Sprite>();
240  if (Options != null && Options.Length > 0)
241  {
242  var optionSpriteElements = orderElement.GetChildElement("optionsprites")?.GetChildElements("sprite");
243  if (optionSpriteElements != null && optionSpriteElements.Any())
244  {
245  for (int i = 0; i < Options.Length; i++)
246  {
247  if (i >= optionSpriteElements.Count()) { break; };
248  var sprite = new Sprite(optionSpriteElements.ElementAt(i), lazyLoad: true);
249  optionSprites.Add(Options[i], sprite);
250  }
251  }
252  }
253  OptionSprites = optionSprites.ToImmutableDictionary();
254 
255  MustManuallyAssign = orderElement.GetAttributeBool("mustmanuallyassign", false);
256  DrawIconWhenContained = orderElement.GetAttributeBool("displayiconwhencontained", false);
257  AutoDismiss = orderElement.GetAttributeBool("autodismiss", Category == OrderCategory.Operate || Category == OrderCategory.Movement);
258  AssignmentPriority = Math.Clamp(orderElement.GetAttributeInt("assignmentpriority", 100), 0, 100);
259  ColoredWhenControllingGiver = orderElement.GetAttributeBool("coloredwhencontrollinggiver", false);
260  DisplayGiverInTooltip = orderElement.GetAttributeBool("displaygiverintooltip", false);
261  }
262 
263  private bool HasSpecifiedJob(Character character, IReadOnlyList<Identifier> jobs)
264  {
265  if (jobs == null || jobs.Count == 0) { return false; }
266  Identifier jobIdentifier = character?.Info?.Job?.Prefab?.Identifier ?? Identifier.Empty;
267  if (jobIdentifier.IsEmpty) { return false; }
268  for (int i = 0; i < jobs.Count; i++)
269  {
270  if (jobIdentifier == jobs[i]) { return true; }
271  }
272  return false;
273  }
274 
275  public bool HasAppropriateJob(Character character) => HasSpecifiedJob(character, AppropriateJobs);
276 
277  public bool HasPreferredJob(Character character) => HasSpecifiedJob(character, PreferredJobs);
278 
279  public string GetChatMessage(string targetCharacterName, string targetRoomName, Entity targetEntity, bool givingOrderToSelf, Identifier orderOption = default, bool isNewOrder = true)
280  {
281  if (!TargetAllCharacters && !isNewOrder && Identifier != "dismissed")
282  {
283  // Use special dialogue when we're rearranging character orders
284  if (!givingOrderToSelf)
285  {
286  return TextManager.GetWithVariable("rearrangedorders", "[name]", targetCharacterName ?? string.Empty).Value;
287  }
288  else
289  {
290  // Say nothing when rearranging the orders of the character you're controlling
291  return string.Empty;
292  }
293  }
294  string messageTag = $"{(givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf" : "OrderDialog")}.{Identifier}";
295  if (!orderOption.IsEmpty)
296  {
297  if (Identifier != "dismissed")
298  {
299  messageTag += $".{orderOption}";
300  }
301  else
302  {
303  string[] splitOption = orderOption.Value.Split('.');
304  if (splitOption.Length > 0)
305  {
306  messageTag += $".{splitOption[0]}";
307  }
308  }
309  }
310 
311  LocalizedString targetEntityName = string.Empty;
312  switch (targetEntity)
313  {
314  case Item item:
315  targetEntityName = item.Name;
316  break;
317  case Hull hull:
318  targetEntityName = hull.DisplayName;
319  break;
320  case Structure structure:
321  targetEntityName = structure.Name;
322  break;
323  case Character character:
324  targetEntityName = character.DisplayName;
325  break;
326  }
327 
328  return TextManager.GetWithVariables(messageTag,
329  ("[name]", targetCharacterName ?? string.Empty, FormatCapitals.No),
330  ("[target]", targetEntityName, FormatCapitals.No),
331  ("[roomname]", targetRoomName ?? string.Empty, FormatCapitals.Yes)).Fallback("").Value;
332  }
333 
338  {
339  if (item?.Components == null || ItemComponentType == null) { return null; }
340  foreach (ItemComponent component in item.Components)
341  {
342  if (component?.GetType() is Type componentType)
343  {
344  if (componentType == ItemComponentType) { return component; }
345  if (CanTypeBeSubclass && componentType.IsSubclassOf(ItemComponentType)) { return component; }
346  }
347  }
348  return null;
349  }
350 
351  public bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
352  {
353  firstMatchingComponent = GetTargetItemComponent(item);
354  return firstMatchingComponent != null;
355  }
356 
358  public List<Item> GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam = null, Character interactableFor = null, Identifier orderOption = default)
359  {
360  List<Item> matchingItems = new List<Item>();
361  if (submarine == null) { return matchingItems; }
362  if (ItemComponentType != null || TargetItems.Any() || RequireItems.Any())
363  {
364  foreach (var item in Item.ItemList)
365  {
366  if (RequireItems.Any() && !TargetItemsMatchItem(RequireItems, item)) { continue; }
367  if (TargetItems.Any() && !TargetItemsMatchItem(item, orderOption)) { continue; }
368  if (RequireItems.None() && TargetItems.None() && !TryGetTargetItemComponent(item, out _)) { continue; }
369  if (mustBelongToPlayerSub && item.Submarine?.Info != null && item.Submarine.Info.Type != SubmarineType.Player) { continue; }
370  if (item.Submarine != submarine && !submarine.DockedTo.Contains(item.Submarine)) { continue; }
371  if (requiredTeam.HasValue && (item.Submarine == null || item.Submarine.TeamID != requiredTeam.Value)) { continue; }
372  if (item.NonInteractable) { continue; }
373  if (ItemComponentType != null && item.Components.None(c => c.GetType() == ItemComponentType)) { continue; }
374  Controller controller = null;
375  if (UseController && !item.TryFindController(out controller, tags: ControllerTags)) { continue; }
376  if (interactableFor != null && (!item.IsInteractable(interactableFor) || (UseController && !controller.Item.IsInteractable(interactableFor)))) { continue; }
377  matchingItems.Add(item);
378  }
379  }
380  return matchingItems;
381  }
382 
384  public List<Item> GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor = null, Identifier orderOption = default)
385  {
386  Submarine submarine = Character.Controlled != null && Character.Controlled.TeamID == CharacterTeamType.Team2 && Submarine.MainSubs.Length > 1 ?
387  Submarine.MainSubs[1] :
389  return GetMatchingItems(submarine, mustBelongToPlayerSub, interactableFor: interactableFor, orderOption: orderOption);
390  }
391 
392  public LocalizedString GetOptionName(string id)
393  {
394  return GetOptionName(id.ToIdentifier());
395  }
396 
398  {
399  if (OptionNames.ContainsKey(id)) { return OptionNames[id]; }
400  return string.Empty;
401  }
402 
403  public LocalizedString GetOptionName(int index)
404  {
405  if (index < 0 || index >= Options.Length) { return null; }
406  return OptionNames[Options[index]];
407  }
408 
414  {
415  Identifier option = order.Identifier;
416  if (order.Option != Identifier.Empty)
417  {
418  option = $"{option}.{order.Option}".ToIdentifier();
419  }
420  return option;
421  }
422 
423  public ImmutableArray<Identifier> GetTargetItems(Identifier option = default)
424  {
425  if (option.IsEmpty || !OptionTargetItems.TryGetValue(option, out ImmutableArray<Identifier> optionTargetItems))
426  {
427  return TargetItems;
428  }
429  else
430  {
431  return optionTargetItems;
432  }
433  }
434 
435  public bool TargetItemsMatchItem(Item item, Identifier option = default)
436  {
437  if (item == null) { return false; }
438  if (Identifier == Tags.DeconstructThis && item.AllowDeconstruct && !Item.DeconstructItems.Contains(item)) { return true; }
439  if (Identifier == Tags.DontDeconstructThis && Item.DeconstructItems.Contains(item)) { return true; }
440  ImmutableArray<Identifier> targetItems = GetTargetItems(option);
441  return TargetItemsMatchItem(targetItems, item);
442  }
443 
444  public static bool TargetItemsMatchItem(ImmutableArray<Identifier> targetItems, Item item)
445  {
446  return item != null && targetItems != null && targetItems.Length > 0 && (targetItems.Contains(item.Prefab.Identifier) || item.HasTag(targetItems));
447  }
448 
449  public override void Dispose() { }
450 
454  public Order CreateInstance(OrderTargetType targetType, Character orderGiver = null, bool isAutonomous = false)
455  {
456  try
457  {
458  return targetType switch
459  {
460  OrderTargetType.Entity => new Order(this, targetEntity: null, targetItem: null, orderGiver, isAutonomous),
461  OrderTargetType.Position => new Order(this, target: null, orderGiver),
462  OrderTargetType.WallSection => new Order(this, wall: null, sectionIndex: null, orderGiver),
463  _ => throw new NotImplementedException()
464  };
465  }
466  catch (NotImplementedException e)
467  {
468  DebugConsole.LogError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}",
469  contentPackage: ContentPackage);
470  return null;
471  }
472  }
473  }
474 
475  class Order
476  {
477  public readonly OrderPrefab Prefab;
478  public readonly Identifier Option;
479  public readonly int ManualPriority;
480  public readonly OrderType Type;
481  public readonly AIObjective Objective;
482  public bool IsCurrentOrder => Type == OrderType.Current;
483  public bool IsDismissal => Prefab.IsDismissal;
484 
485  public enum OrderType
486  {
487  Current,
488  Previous
489  }
490 
491  public readonly Entity TargetEntity;
494 
495  public readonly Character OrderGiver;
496 
497  public readonly OrderTarget TargetPosition;
498 
499  private ISpatialEntity targetSpatialEntity;
500 
505  {
506  get
507  {
508  if (targetSpatialEntity == null)
509  {
510  if (TargetType == OrderTargetType.WallSection && WallSectionIndex.HasValue)
511  {
512  targetSpatialEntity = (TargetEntity as Structure)?.Sections[WallSectionIndex.Value];
513  }
514  else
515  {
516  targetSpatialEntity = TargetEntity ?? TargetPosition as ISpatialEntity;
517  }
518  }
519  return targetSpatialEntity;
520  }
521  }
522 
524 
525  public enum OrderTargetType
526  {
527  Entity,
528  Position,
530  }
531  public readonly OrderTargetType TargetType;
532  public readonly int? WallSectionIndex;
533 
534  public LocalizedString Name => Prefab.Name;
535  public LocalizedString ContextualName => Prefab.ContextualName;
537  public Type ItemComponentType => Prefab.ItemComponentType;
538  public bool CanTypeBeSubclass => Prefab.CanTypeBeSubclass;
539  public ref readonly ImmutableArray<Identifier> ControllerTags => ref Prefab.ControllerTags;
540  public ref readonly ImmutableArray<Identifier> TargetItems => ref Prefab.TargetItems;
541  public ref readonly ImmutableArray<Identifier> RequireItems => ref Prefab.RequireItems;
542  public ref readonly ImmutableArray<Identifier> Options => ref Prefab.Options;
543  public ref readonly ImmutableArray<Identifier> HiddenOptions => ref Prefab.HiddenOptions;
544  public ref readonly ImmutableArray<Identifier> AllOptions => ref Prefab.AllOptions;
545  public Sprite SymbolSprite => Prefab.SymbolSprite;
546  public Color Color => Prefab.Color;
547  public bool TargetAllCharacters => Prefab.TargetAllCharacters;
548  public ref readonly ImmutableArray<Identifier> AppropriateJobs => ref Prefab.AppropriateJobs;
549  public float FadeOutTime => Prefab.FadeOutTime;
550  public bool MustSetTarget => Prefab.MustSetTarget;
551  public Identifier AppropriateSkill => Prefab.AppropriateSkill;
552  public OrderCategory? Category => Prefab.Category;
553  public bool MustManuallyAssign => Prefab.MustManuallyAssign;
554  public bool IsIgnoreOrder => Prefab.IsIgnoreOrder;
555  public bool IsDeconstructOrder => Prefab.IsDeconstructOrder;
556  public bool DrawIconWhenContained => Prefab.DrawIconWhenContained;
557  public bool Hidden => Prefab.Hidden;
558  public bool IgnoreAtOutpost => Prefab.IgnoreAtOutpost;
559  public bool IsReport => Prefab.IsReport;
560  public bool AutoDismiss => Prefab.AutoDismiss;
561  public int AssignmentPriority => Prefab.AssignmentPriority;
562 
563  public bool ColoredWhenControllingGiver => Prefab.ColoredWhenControllingGiver;
564  public bool DisplayGiverInTooltip => Prefab.DisplayGiverInTooltip;
565 
566  public readonly bool UseController;
567 
571  public Order(OrderPrefab prefab, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
572  : this(prefab, Identifier.Empty, 0, OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
573 
577  public Order(OrderPrefab prefab, Identifier option, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
578  : this(prefab, option, 0, OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
579 
583  public Order(OrderPrefab prefab, OrderTarget target, Character orderGiver = null)
584  : this(prefab, prefab.Options.FirstOrDefault(), 0, OrderType.Current, null, target, orderGiver) { }
585 
589  public Order(OrderPrefab prefab, Identifier option, OrderTarget target, Character orderGiver = null)
590  : this(prefab, option, 0, OrderType.Current, null, target, orderGiver) { }
591 
595  public Order(OrderPrefab prefab, Structure wall, int? sectionIndex, Character orderGiver = null)
596  : this(prefab, Identifier.Empty, 0, OrderType.Current, null, wall, sectionIndex, orderGiver) { }
597 
601  public Order(OrderPrefab prefab, Identifier option, Structure wall, int? sectionIndex, Character orderGiver = null)
602  : this(prefab, option, 0, OrderType.Current, null, wall, sectionIndex, orderGiver) { }
603 
607  private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
608  {
609  Prefab = prefab;
610  Option = option;
611  ManualPriority = manualPriority;
612  Type = orderType;
613  Objective = aiObjective;
614 
615  UseController = Prefab.UseController;
616 
617  OrderGiver = orderGiver;
618  TargetEntity = targetEntity;
619  if (targetItem != null)
620  {
621  if (UseController)
622  {
624  if (ConnectedController == null)
625  {
626  DebugConsole.AddWarning("AI: Tried to use a controller for operating an item, but couldn't find any.");
627  UseController = false;
628  }
629  }
630  TargetEntity = targetItem.Item;
631  TargetItemComponent = targetItem;
632  }
633 
634  TargetType = OrderTargetType.Entity;
635  }
636 
640  private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, OrderTarget target, Character orderGiver = null)
641  : this(prefab, option, manualPriority, orderType, aiObjective, targetEntity: null, targetItem: null, orderGiver)
642  {
643  TargetPosition = target;
644  TargetType = OrderTargetType.Position;
645  }
646 
650  private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Structure wall, int? sectionIndex, Character orderGiver = null)
651  : this(prefab, option, manualPriority, orderType, aiObjective, targetEntity: wall, null, orderGiver: orderGiver)
652  {
653  WallSectionIndex = sectionIndex;
654  TargetType = OrderTargetType.WallSection;
655  }
656 
657  private Order(
658  Order other,
659  OrderPrefab prefab = null,
660  Identifier option = default,
661  int? manualPriority = null,
662  OrderType? type = null,
663  AIObjective objective = null,
664  Entity targetEntity = null,
665  ItemComponent targetItemComponent = null,
666  Controller connectedController = null,
667  Character orderGiver = null,
668  OrderTarget targetPosition = null,
669  OrderTargetType? targetType = null,
670  int? wallSectionIndex = null,
671  bool? useController = null)
672  {
673  Prefab = prefab ?? other.Prefab;
674  Option = option.IfEmpty(other.Option);
675  ManualPriority = manualPriority ?? other.ManualPriority;
676  Type = type ?? other.Type;
677  Objective = objective ?? other.Objective;
678 
679  TargetEntity = targetEntity ?? other.TargetEntity;
680  TargetItemComponent = targetItemComponent ?? other.TargetItemComponent;
681  ConnectedController = connectedController ?? other.ConnectedController;
682 
683  OrderGiver = orderGiver ?? other.OrderGiver;
684 
685  TargetPosition = targetPosition ?? other.TargetPosition;
686 
687  TargetType = targetType ?? other.TargetType;
688  WallSectionIndex = wallSectionIndex ?? other.WallSectionIndex;
689 
690  UseController = useController ?? other.UseController;
691 
692 #if DEBUG
693  if (UseController && ConnectedController == null)
694  {
695  DebugConsole.ThrowError($"AI: Created an Order {Identifier} that's set to use a Controller, but a Controller was not specified.\n{Environment.StackTrace.CleanupStackTrace()}");
696  }
697 #endif
698  }
699 
700  public Order WithOption(Identifier option)
701  {
702  return new Order(this, option: option);
703  }
704 
705  public Order WithManualPriority(int newPriority)
706  {
707  return new Order(this, manualPriority: newPriority);
708  }
709 
710  public Order WithOrderGiver(Character orderGiver)
711  {
712  return new Order(this, orderGiver: orderGiver);
713  }
714 
715  public Order WithObjective(AIObjective objective)
716  {
717  return new Order(this, objective: objective);
718  }
719 
721  {
722  return new Order(this, targetEntity: entity, targetType: OrderTargetType.Entity);
723  }
724 
726  {
727  if (spatialEntity is WallSection wallSection)
728  {
729  Structure wall = wallSection.Wall;
730  int sectionIndex = wall.Sections.IndexOf(wallSection);
731  return WithWallSection(wall, sectionIndex);
732  }
733  else if (spatialEntity is Entity entity)
734  {
735  return WithTargetEntity(entity);
736  }
737  else if (spatialEntity is OrderTarget orderTarget)
738  {
739  return WithTargetPosition(orderTarget);
740  }
741 
742  throw new InvalidOperationException($"Unexpected input type: {spatialEntity.GetType().Name}");
743  }
744 
745  public Order WithItemComponent(Item item, ItemComponent component = null)
746  {
747  Controller controller = null;
748  if (UseController)
749  {
750  controller = item?.FindController(tags: ControllerTags);
751  }
752  return new Order(this, targetEntity: item, targetItemComponent: component ?? GetTargetItemComponent(item), connectedController: controller);
753  }
754 
755  public Order WithWallSection(Structure wall, int? sectionIndex)
756  {
757  return new Order(this, targetEntity: wall, wallSectionIndex: sectionIndex, targetType: OrderTargetType.WallSection);
758  }
759 
760  public Order WithType(OrderType type)
761  {
762  return new Order(this, type: type);
763  }
764 
765  public Order WithTargetPosition(OrderTarget targetPosition)
766  {
767  return new Order(this, targetPosition: targetPosition, targetType: OrderTargetType.Position);
768  }
769 
770  public Order Clone()
771  {
772  return new Order(this);
773  }
774 
776  {
777  if (IsDismissal) { throw new InvalidOperationException("Attempted to dismiss a dismissal order"); }
778  return new Order(this, prefab: OrderPrefab.Prefabs["dismissed"], option: GetDismissOrderOption(this));
779  }
780 
781  public bool HasAppropriateJob(Character character)
782  => Prefab.HasAppropriateJob(character);
783 
784  public bool HasPreferredJob(Character character)
785  => Prefab.HasPreferredJob(character);
786 
787  public string GetChatMessage(
788  string targetCharacterName, string targetRoomName, bool givingOrderToSelf, Identifier orderOption = default, bool isNewOrder = true)
789  => Prefab.GetChatMessage(targetCharacterName, targetRoomName, TargetEntity, givingOrderToSelf, orderOption, isNewOrder);
790 
795  {
796  return Prefab.GetTargetItemComponent(item);
797  }
798 
799  public bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
800  {
801  return Prefab.TryGetTargetItemComponent(item, out firstMatchingComponent);
802  }
803 
805  public List<Item> GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam = null, Character interactableFor = null)
806  {
807  return Prefab.GetMatchingItems(submarine, mustBelongToPlayerSub, requiredTeam, interactableFor);
808  }
809 
810 
812  public List<Item> GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor = null)
813  {
814  return Prefab.GetMatchingItems(mustBelongToPlayerSub, interactableFor);
815  }
816 
817  public LocalizedString GetOptionName(string id)
818  {
819  return Prefab.GetOptionName(id);
820  }
821 
823  {
824  return Prefab.GetOptionName(id);
825  }
826 
827  public LocalizedString GetOptionName(int index)
828  {
829  return Prefab.GetOptionName(index);
830  }
831 
837  {
838  return OrderPrefab.GetDismissOrderOption(order);
839  }
840 
841  public bool MatchesOrder(Identifier orderIdentifier, Identifier orderOption) =>
842  orderIdentifier == Identifier && orderOption == Option;
843 
844  /*public bool MatchesOrder(Order order, Identifier option) =>
845  order != null && MatchesOrder(order.Identifier, option);*/
846 
847  public bool MatchesOrder(Order order) =>
848  order != null && MatchesOrder(order.Identifier, order.Option);
849 
850  public bool MatchesDismissedOrder(Identifier dismissOrderOption)
851  {
852  Identifier[] dismissedOrder = dismissOrderOption.Value.Split('.').Select(s => s.ToIdentifier()).ToArray();
853  if (dismissedOrder != null && dismissedOrder.Length > 0)
854  {
855  Identifier dismissedOrderIdentifier = dismissedOrder.Length > 0 ? dismissedOrder[0] : Identifier.Empty;
856  if (dismissedOrderIdentifier == Identifier.Empty || dismissedOrderIdentifier != Identifier) { return false; }
857  Identifier dismissedOrderOption = dismissedOrder.Length > 1 ? dismissedOrder[1] : Identifier.Empty;
858  if (dismissedOrderOption == Identifier.Empty && Option == Identifier.Empty) { return true; }
859  return dismissedOrderOption == Option;
860  }
861  else
862  {
863  return false;
864  }
865  }
866 
867  public ImmutableArray<Identifier> GetTargetItems(Identifier option = default)
868  => Prefab.GetTargetItems(option);
869 
870  public override string ToString()
871  {
872  return $"Order ({Name})";
873  }
874  }
875 }
string? GetAttributeString(string key, string? def)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
Color GetAttributeColor(string key, in Color def)
float GetAttributeFloat(string key, float def)
IEnumerable< ContentXElement > GetChildElements(string name)
ContentXElement? GetChildElement(string name)
bool GetAttributeBool(string key, bool def)
int GetAttributeInt(string key, int def)
Identifier GetAttributeIdentifier(string key, string def)
Entity(Submarine submarine, ushort id)
Definition: Entity.cs:90
static GameSession?? GameSession
Definition: GameMain.cs:88
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
static readonly List< Item > ItemList
static HashSet< Item > DeconstructItems
Items that have been marked for deconstruction
Controller FindController(ImmutableArray< Identifier >? tags=null)
The base class for components holding the different functionalities of the item
JobPrefab Prefab
Definition: Job.cs:18
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
static readonly PrefabCollection< OrderCategoryIcon > OrderCategoryIcons
Definition: Order.cs:22
readonly Color Color
Definition: Order.cs:34
OrderCategoryIcon(ContentXElement element, OrdersFile file)
Definition: Order.cs:24
override void Dispose()
Definition: Order.cs:36
readonly Sprite Sprite
Definition: Order.cs:33
readonly OrderCategory Category
Definition: Order.cs:32
Order WithOption(Identifier option)
Definition: Order.cs:700
readonly Entity TargetEntity
Definition: Order.cs:491
Order WithTargetPosition(OrderTarget targetPosition)
Definition: Order.cs:765
Order(OrderPrefab prefab, Identifier option, OrderTarget target, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.Position
Definition: Order.cs:589
bool IsDeconstructOrder
Definition: Order.cs:555
bool MatchesOrder(Order order)
bool AutoDismiss
Definition: Order.cs:560
ref readonly ImmutableArray< Identifier > AppropriateJobs
Definition: Order.cs:548
ItemComponent GetTargetItemComponent(Item item)
Get the target item component based on the target item type
Definition: Order.cs:794
readonly OrderType Type
Definition: Order.cs:480
LocalizedString ContextualName
Definition: Order.cs:535
bool MatchesDismissedOrder(Identifier dismissOrderOption)
Definition: Order.cs:850
string GetChatMessage(string targetCharacterName, string targetRoomName, bool givingOrderToSelf, Identifier orderOption=default, bool isNewOrder=true)
Order(OrderPrefab prefab, Identifier option, Entity targetEntity, ItemComponent targetItem, Character orderGiver=null, bool isAutonomous=false)
Constructor for orders with the target type OrderTargetType.Entity
Definition: Order.cs:577
Order(OrderPrefab prefab, Entity targetEntity, ItemComponent targetItem, Character orderGiver=null, bool isAutonomous=false)
Constructor for orders with the target type OrderTargetType.Entity
Definition: Order.cs:571
Order GetDismissal()
Definition: Order.cs:775
LocalizedString Name
Definition: Order.cs:534
override string ToString()
Definition: Order.cs:870
ref readonly ImmutableArray< Identifier > HiddenOptions
Definition: Order.cs:543
readonly Character OrderGiver
Definition: Order.cs:495
ref readonly ImmutableArray< Identifier > TargetItems
Definition: Order.cs:540
bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
Definition: Order.cs:799
Order Clone()
Definition: Order.cs:770
static Identifier GetDismissOrderOption(Order order)
Used to create the order option for the Dismiss order to know which order it targets
Definition: Order.cs:836
Order WithType(OrderType type)
Definition: Order.cs:760
float FadeOutTime
Definition: Order.cs:549
bool IsReport
Definition: Order.cs:559
ISpatialEntity??? TargetSpatialEntity
Note this property doesn't return the follow target of the Follow objective, as expected!
Definition: Order.cs:505
LocalizedString GetOptionName(string id)
Definition: Order.cs:817
ref readonly ImmutableArray< Identifier > Options
Definition: Order.cs:542
bool IsIgnoreOrder
Definition: Order.cs:554
bool IsDismissal
Definition: Order.cs:483
readonly? int WallSectionIndex
Definition: Order.cs:532
Type ItemComponentType
Definition: Order.cs:537
readonly Identifier Option
Definition: Order.cs:478
LocalizedString GetOptionName(Identifier id)
Definition: Order.cs:822
bool HasPreferredJob(Character character)
bool CanTypeBeSubclass
Definition: Order.cs:538
bool IgnoreAtOutpost
Definition: Order.cs:558
Order WithTargetEntity(Entity entity)
Definition: Order.cs:720
readonly int ManualPriority
Definition: Order.cs:479
readonly OrderTarget TargetPosition
Definition: Order.cs:497
readonly bool UseController
Definition: Order.cs:566
Identifier AppropriateSkill
Definition: Order.cs:551
LocalizedString GetOptionName(int index)
Definition: Order.cs:827
bool DisplayGiverInTooltip
Definition: Order.cs:564
Order WithManualPriority(int newPriority)
Definition: Order.cs:705
Order(OrderPrefab prefab, OrderTarget target, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.Position
Definition: Order.cs:583
ref readonly ImmutableArray< Identifier > AllOptions
Definition: Order.cs:544
List< Item > GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor=null)
Only returns items which are interactable for this character
Definition: Order.cs:812
bool TargetAllCharacters
Definition: Order.cs:547
bool MustSetTarget
Definition: Order.cs:550
readonly Controller ConnectedController
Definition: Order.cs:493
OrderCategory? Category
Definition: Order.cs:552
Order(OrderPrefab prefab, Identifier option, Structure wall, int? sectionIndex, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.WallSection
Definition: Order.cs:601
readonly AIObjective Objective
Definition: Order.cs:481
Order WithItemComponent(Item item, ItemComponent component=null)
Definition: Order.cs:745
Hull TargetHull
Definition: Order.cs:523
ref readonly ImmutableArray< Identifier > RequireItems
Definition: Order.cs:541
Identifier Identifier
Definition: Order.cs:536
Order WithWallSection(Structure wall, int? sectionIndex)
Definition: Order.cs:755
bool IsCurrentOrder
Definition: Order.cs:482
bool MustManuallyAssign
Definition: Order.cs:553
readonly OrderPrefab Prefab
Definition: Order.cs:477
List< Item > GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam=null, Character interactableFor=null)
Only returns items which are interactable for this character
Definition: Order.cs:805
Order WithTargetSpatialEntity(ISpatialEntity spatialEntity)
Definition: Order.cs:725
Order(OrderPrefab prefab, Structure wall, int? sectionIndex, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.WallSection
Definition: Order.cs:595
bool MatchesOrder(Identifier orderIdentifier, Identifier orderOption)
ref readonly ImmutableArray< Identifier > ControllerTags
Definition: Order.cs:539
ImmutableArray< Identifier > GetTargetItems(Identifier option=default)
bool DrawIconWhenContained
Definition: Order.cs:556
readonly OrderTargetType TargetType
Definition: Order.cs:531
Sprite SymbolSprite
Definition: Order.cs:545
Order WithObjective(AIObjective objective)
Definition: Order.cs:715
bool HasAppropriateJob(Character character)
int AssignmentPriority
Definition: Order.cs:561
readonly ItemComponent TargetItemComponent
Definition: Order.cs:492
Order WithOrderGiver(Character orderGiver)
Definition: Order.cs:710
bool ColoredWhenControllingGiver
Definition: Order.cs:563
readonly ImmutableArray< Identifier > RequireItems
Definition: Order.cs:60
readonly ImmutableArray< Identifier > HiddenOptions
Definition: Order.cs:107
OrderPrefab(ContentXElement orderElement, OrdersFile file)
Definition: Order.cs:163
readonly bool UseController
Definition: Order.cs:98
readonly LocalizedString Name
Definition: Order.cs:49
bool HasOptionSpecificTargetItems
Definition: Order.cs:62
bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
Definition: Order.cs:351
List< Item > GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor=null, Identifier orderOption=default)
Only returns items which are interactable for this character
Definition: Order.cs:384
readonly ImmutableArray< Identifier > TargetItems
Definition: Order.cs:59
readonly bool Hidden
Definition: Order.cs:120
readonly bool AutoDismiss
If enabled and this is an Operate order, it will remove Operate orders of the same item from other ch...
Definition: Order.cs:130
readonly? OrderCategory Category
Definition: Order.cs:46
readonly Identifier CategoryIdentifier
Definition: Order.cs:47
readonly bool MustSetTarget
Definition: Order.cs:113
readonly bool CanTypeBeSubclass
Definition: Order.cs:58
readonly Sprite SymbolSprite
Definition: Order.cs:55
bool ColoredWhenControllingGiver
Definition: Order.cs:160
LocalizedString GetOptionName(int index)
Definition: Order.cs:403
static bool TargetItemsMatchItem(ImmutableArray< Identifier > targetItems, Item item)
Definition: Order.cs:444
bool TargetItemsMatchItem(Item item, Identifier option=default)
Definition: Order.cs:435
readonly bool TargetAllCharacters
Definition: Order.cs:85
readonly bool MustManuallyAssign
Definition: Order.cs:124
readonly ImmutableArray< Identifier > AllOptions
Definition: Order.cs:108
readonly ImmutableDictionary< Identifier, Sprite > OptionSprites
Definition: Order.cs:111
bool HasPreferredJob(Character character)
readonly Identifier AppropriateSkill
Definition: Order.cs:119
int AssignmentPriority
Affects how high on the order list the order will be placed (i.e. the manual priority order when it's...
Definition: Order.cs:158
readonly float FadeOutTime
Definition: Order.cs:96
readonly ImmutableArray< Identifier > AppropriateJobs
If defined, the order can only be quick-assigned to characters with these jobs. Or if it's a report,...
Definition: Order.cs:105
static readonly PrefabCollection< OrderPrefab > Prefabs
Definition: Order.cs:41
readonly ImmutableArray< Identifier > PreferredJobs
If defined, the order will be quick-assigned to characters with these jobs before characters with oth...
Definition: Order.cs:135
Order CreateInstance(OrderTargetType targetType, Character orderGiver=null, bool isAutonomous=false)
Create an Order instance with a null target
Definition: Order.cs:454
readonly bool IgnoreAtOutpost
Definition: Order.cs:121
static OrderPrefab Dismissal
Definition: Order.cs:44
ImmutableArray< Identifier > GetTargetItems(Identifier option=default)
Definition: Order.cs:423
readonly bool CanBeGeneralized
Can the order be turned into a non-entity-targeting one if it was originally created with a target en...
Definition: Order.cs:118
bool HasAppropriateJob(Character character)
bool DrawIconWhenContained
Should the order icon be drawn when the order target is inside a container
Definition: Order.cs:152
LocalizedString GetOptionName(string id)
Definition: Order.cs:392
List< Item > GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam=null, Character interactableFor=null, Identifier orderOption=default)
Only returns items which are interactable for this character
Definition: Order.cs:358
ItemComponent GetTargetItemComponent(Item item)
Get the target item component based on the target item type
Definition: Order.cs:337
bool IsVisibleAsReportButton
Definition: Order.cs:88
override void Dispose()
Definition: Order.cs:449
string GetChatMessage(string targetCharacterName, string targetRoomName, Entity targetEntity, bool givingOrderToSelf, Identifier orderOption=default, bool isNewOrder=true)
Definition: Order.cs:279
readonly Type ItemComponentType
Definition: Order.cs:57
static readonly Identifier DismissalIdentifier
Definition: Order.cs:43
readonly ListDictionary< Identifier, LocalizedString > OptionNames
Definition: Order.cs:109
readonly ImmutableArray< Identifier > Options
Definition: Order.cs:106
readonly ImmutableArray< Identifier > ControllerTags
Definition: Order.cs:100
readonly LocalizedString ContextualName
Name that can be used with the contextual version of the order
Definition: Order.cs:53
OrderTargetType TargetType
Definition: Order.cs:143
static Identifier GetDismissOrderOption(Order order)
Used to create the order option for the Dismiss order to know which order it targets
Definition: Order.cs:413
LocalizedString GetOptionName(Identifier id)
Definition: Order.cs:397
bool DisplayGiverInTooltip
Definition: Order.cs:161
Prefab(ContentFile file, Identifier identifier)
Definition: Prefab.cs:40
readonly Identifier Identifier
Definition: Prefab.cs:34
Prefab that has a property serves as a deterministic hash of a prefab's identifier....
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.
OrderCategory
Definition: Order.cs:12