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].Split(',', ',').ToIdentifiers() : Array.Empty<Identifier>();
208  for (int j = 0; j < optionTargetItemsSplit.Length; j++)
209  {
210  optionTargetItemsSplit[j] = optionTargetItemsSplit[j].Value.Trim().ToIdentifier();
211  allTargetItems.Add(optionTargetItemsSplit[j]);
212  }
213  optionTargetItems.Add(AllOptions[i], optionTargetItemsSplit.ToImmutableArray());
214  }
215  TargetItems = allTargetItems.ToImmutableArray();
216  }
217  else
218  {
219  TargetItems = orderElement.GetAttributeIdentifierArray("targetitems", Array.Empty<Identifier>(), trim: true).ToImmutableArray();
220  }
221  RequireItems = orderElement.GetAttributeIdentifierArray("requireitems", Array.Empty<Identifier>(), trim: true).ToImmutableArray();
222  OptionTargetItems = optionTargetItems.ToImmutableDictionary();
223 
224  var category = orderElement.GetAttributeString("category", null);
225  this.Category = !string.IsNullOrWhiteSpace(category) ? Enum.Parse<OrderCategory>(category, true) : (OrderCategory?)null;
226  this.CategoryIdentifier = (this.Category?.ToString() ?? string.Empty).ToIdentifier();
227  MustSetTarget = orderElement.GetAttributeBool("mustsettarget", false);
228  CanBeGeneralized = !MustSetTarget && orderElement.GetAttributeBool("canbegeneralized", true);
229  AppropriateSkill = orderElement.GetAttributeIdentifier("appropriateskill", Identifier.Empty);
230  Hidden = orderElement.GetAttributeBool("hidden", false);
231  IgnoreAtOutpost = orderElement.GetAttributeBool("ignoreatoutpost", false);
232 
233  OptionNames =
235  TextManager.Get("OrderOptions." + Identifier).Split(',', ','), Options.Length, i => Options[i]);
236 
237  var spriteElement = orderElement.GetChildElement("sprite");
238  if (spriteElement != null)
239  {
240  SymbolSprite = new Sprite(spriteElement, lazyLoad: true);
241  }
242 
243  var optionSprites = new Dictionary<Identifier, Sprite>();
244  if (Options != null && Options.Length > 0)
245  {
246  var optionSpriteElements = orderElement.GetChildElement("optionsprites")?.GetChildElements("sprite");
247  if (optionSpriteElements != null && optionSpriteElements.Any())
248  {
249  for (int i = 0; i < Options.Length; i++)
250  {
251  if (i >= optionSpriteElements.Count()) { break; };
252  var sprite = new Sprite(optionSpriteElements.ElementAt(i), lazyLoad: true);
253  optionSprites.Add(Options[i], sprite);
254  }
255  }
256  }
257  OptionSprites = optionSprites.ToImmutableDictionary();
258 
259  MustManuallyAssign = orderElement.GetAttributeBool("mustmanuallyassign", false);
260  DrawIconWhenContained = orderElement.GetAttributeBool("displayiconwhencontained", false);
261  AutoDismiss = orderElement.GetAttributeBool("autodismiss", Category == OrderCategory.Operate || Category == OrderCategory.Movement);
262  AssignmentPriority = Math.Clamp(orderElement.GetAttributeInt("assignmentpriority", 100), 0, 100);
263  ColoredWhenControllingGiver = orderElement.GetAttributeBool("coloredwhencontrollinggiver", false);
264  DisplayGiverInTooltip = orderElement.GetAttributeBool("displaygiverintooltip", false);
265  }
266 
267  private bool HasSpecifiedJob(Character character, IReadOnlyList<Identifier> jobs)
268  {
269  if (jobs == null || jobs.Count == 0) { return false; }
270  Identifier jobIdentifier = character?.Info?.Job?.Prefab?.Identifier ?? Identifier.Empty;
271  if (jobIdentifier.IsEmpty) { return false; }
272  for (int i = 0; i < jobs.Count; i++)
273  {
274  if (jobIdentifier == jobs[i]) { return true; }
275  }
276  return false;
277  }
278 
279  public bool HasAppropriateJob(Character character) => HasSpecifiedJob(character, AppropriateJobs);
280 
281  public bool HasPreferredJob(Character character) => HasSpecifiedJob(character, PreferredJobs);
282 
283  public string GetChatMessage(string targetCharacterName, string targetRoomName, Entity targetEntity, bool givingOrderToSelf, Identifier orderOption = default, bool isNewOrder = true)
284  {
285  if (!TargetAllCharacters && !isNewOrder && Identifier != "dismissed")
286  {
287  // Use special dialogue when we're rearranging character orders
288  if (!givingOrderToSelf)
289  {
290  return TextManager.GetWithVariable("rearrangedorders", "[name]", targetCharacterName ?? string.Empty).Value;
291  }
292  else
293  {
294  // Say nothing when rearranging the orders of the character you're controlling
295  return string.Empty;
296  }
297  }
298  string messageTag = $"{(givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf" : "OrderDialog")}.{Identifier}";
299  if (!orderOption.IsEmpty)
300  {
301  if (Identifier != "dismissed")
302  {
303  messageTag += $".{orderOption}";
304  }
305  else
306  {
307  string[] splitOption = orderOption.Value.Split('.');
308  if (splitOption.Length > 0)
309  {
310  messageTag += $".{splitOption[0]}";
311  }
312  }
313  }
314 
315  LocalizedString targetEntityName = string.Empty;
316  switch (targetEntity)
317  {
318  case Item item:
319  targetEntityName = item.Name;
320  break;
321  case Hull hull:
322  targetEntityName = hull.DisplayName;
323  break;
324  case Structure structure:
325  targetEntityName = structure.Name;
326  break;
327  case Character character:
328  targetEntityName = character.DisplayName;
329  break;
330  }
331 
332  return TextManager.GetWithVariables(messageTag,
333  ("[name]", targetCharacterName ?? string.Empty, FormatCapitals.No),
334  ("[target]", targetEntityName, FormatCapitals.No),
335  ("[roomname]", targetRoomName ?? string.Empty, FormatCapitals.Yes)).Fallback("").Value;
336  }
337 
342  {
343  if (item?.Components == null || ItemComponentType == null) { return null; }
344  foreach (ItemComponent component in item.Components)
345  {
346  if (component?.GetType() is Type componentType)
347  {
348  if (componentType == ItemComponentType) { return component; }
349  if (CanTypeBeSubclass && componentType.IsSubclassOf(ItemComponentType)) { return component; }
350  }
351  }
352  return null;
353  }
354 
355  public bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
356  {
357  firstMatchingComponent = GetTargetItemComponent(item);
358  return firstMatchingComponent != null;
359  }
360 
362  public List<Item> GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam = null, Character interactableFor = null, Identifier orderOption = default)
363  {
364  List<Item> matchingItems = new List<Item>();
365  if (submarine == null) { return matchingItems; }
366  if (ItemComponentType != null || TargetItems.Any() || RequireItems.Any())
367  {
368  foreach (var item in Item.ItemList)
369  {
370  if (RequireItems.Any() && !TargetItemsMatchItem(RequireItems, item)) { continue; }
371  if (TargetItems.Any() && !TargetItemsMatchItem(item, orderOption)) { continue; }
372  if (RequireItems.None() && TargetItems.None() && !TryGetTargetItemComponent(item, out _)) { continue; }
373  if (mustBelongToPlayerSub && item.Submarine?.Info != null && item.Submarine.Info.Type != SubmarineType.Player) { continue; }
374  if (item.Submarine != submarine && !submarine.DockedTo.Contains(item.Submarine)) { continue; }
375  if (requiredTeam.HasValue && (item.Submarine == null || item.Submarine.TeamID != requiredTeam.Value)) { continue; }
376  if (item.NonInteractable) { continue; }
377  if (ItemComponentType != null && item.Components.None(c => c.GetType() == ItemComponentType)) { continue; }
378  Controller controller = null;
379  if (UseController && !item.TryFindController(out controller, tags: ControllerTags)) { continue; }
380  if (interactableFor != null && (!item.IsInteractable(interactableFor) || (UseController && !controller.Item.IsInteractable(interactableFor)))) { continue; }
381  matchingItems.Add(item);
382  }
383  }
384  return matchingItems;
385  }
386 
388  public List<Item> GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor = null, Identifier orderOption = default)
389  {
390  Submarine submarine = Character.Controlled != null && Character.Controlled.TeamID == CharacterTeamType.Team2 && Submarine.MainSubs.Length > 1 ?
391  Submarine.MainSubs[1] :
393  return GetMatchingItems(submarine, mustBelongToPlayerSub, interactableFor: interactableFor, orderOption: orderOption);
394  }
395 
396  public LocalizedString GetOptionName(string id)
397  {
398  return GetOptionName(id.ToIdentifier());
399  }
400 
402  {
403  if (OptionNames.ContainsKey(id)) { return OptionNames[id]; }
404  return string.Empty;
405  }
406 
407  public LocalizedString GetOptionName(int index)
408  {
409  if (index < 0 || index >= Options.Length) { return null; }
410  return OptionNames[Options[index]];
411  }
412 
418  {
419  Identifier option = order.Identifier;
420  if (order.Option != Identifier.Empty)
421  {
422  option = $"{option}.{order.Option}".ToIdentifier();
423  }
424  return option;
425  }
426 
427  public ImmutableArray<Identifier> GetTargetItems(Identifier option = default)
428  {
429  if (option.IsEmpty || !OptionTargetItems.TryGetValue(option, out ImmutableArray<Identifier> optionTargetItems))
430  {
431  return TargetItems;
432  }
433  else
434  {
435  return optionTargetItems;
436  }
437  }
438 
439  public bool TargetItemsMatchItem(Item item, Identifier option = default)
440  {
441  if (item == null) { return false; }
442  if (Identifier == Tags.DeconstructThis && item.AllowDeconstruct && !Item.DeconstructItems.Contains(item)) { return true; }
443  if (Identifier == Tags.DontDeconstructThis && Item.DeconstructItems.Contains(item)) { return true; }
444  ImmutableArray<Identifier> targetItems = GetTargetItems(option);
445  return TargetItemsMatchItem(targetItems, item);
446  }
447 
448  public static bool TargetItemsMatchItem(ImmutableArray<Identifier> targetItems, Item item)
449  {
450  return item != null && targetItems != null && targetItems.Length > 0 && (targetItems.Contains(item.Prefab.Identifier) || item.HasTag(targetItems));
451  }
452 
453  public override void Dispose() { }
454 
458  public Order CreateInstance(OrderTargetType targetType, Character orderGiver = null, bool isAutonomous = false)
459  {
460  try
461  {
462  return targetType switch
463  {
464  OrderTargetType.Entity => new Order(this, targetEntity: null, targetItem: null, orderGiver, isAutonomous),
465  OrderTargetType.Position => new Order(this, target: null, orderGiver),
466  OrderTargetType.WallSection => new Order(this, wall: null, sectionIndex: null, orderGiver),
467  _ => throw new NotImplementedException()
468  };
469  }
470  catch (NotImplementedException e)
471  {
472  DebugConsole.LogError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}",
473  contentPackage: ContentPackage);
474  return null;
475  }
476  }
477  }
478 
479  class Order
480  {
481  public readonly OrderPrefab Prefab;
482  public readonly Identifier Option;
483  public readonly int ManualPriority;
484  public readonly OrderType Type;
485  public readonly AIObjective Objective;
486  public bool IsCurrentOrder => Type == OrderType.Current;
487  public bool IsDismissal => Prefab.IsDismissal;
488 
489  public enum OrderType
490  {
491  Current,
492  Previous
493  }
494 
495  public readonly Entity TargetEntity;
498 
499  public readonly Character OrderGiver;
500 
501  public readonly OrderTarget TargetPosition;
502 
503  private ISpatialEntity targetSpatialEntity;
504 
509  {
510  get
511  {
512  if (targetSpatialEntity == null)
513  {
514  if (TargetType == OrderTargetType.WallSection && WallSectionIndex.HasValue)
515  {
516  targetSpatialEntity = (TargetEntity as Structure)?.Sections[WallSectionIndex.Value];
517  }
518  else
519  {
520  targetSpatialEntity = TargetEntity ?? TargetPosition as ISpatialEntity;
521  }
522  }
523  return targetSpatialEntity;
524  }
525  }
526 
528 
529  public enum OrderTargetType
530  {
531  Entity,
532  Position,
534  }
535  public readonly OrderTargetType TargetType;
536  public readonly int? WallSectionIndex;
537 
538  public LocalizedString Name => Prefab.Name;
539  public LocalizedString ContextualName => Prefab.ContextualName;
541  public Type ItemComponentType => Prefab.ItemComponentType;
542  public bool CanTypeBeSubclass => Prefab.CanTypeBeSubclass;
543  public ref readonly ImmutableArray<Identifier> ControllerTags => ref Prefab.ControllerTags;
544  public ref readonly ImmutableArray<Identifier> TargetItems => ref Prefab.TargetItems;
545  public ref readonly ImmutableArray<Identifier> RequireItems => ref Prefab.RequireItems;
546  public ref readonly ImmutableArray<Identifier> Options => ref Prefab.Options;
547  public ref readonly ImmutableArray<Identifier> HiddenOptions => ref Prefab.HiddenOptions;
548  public ref readonly ImmutableArray<Identifier> AllOptions => ref Prefab.AllOptions;
549  public Sprite SymbolSprite => Prefab.SymbolSprite;
550  public Color Color => Prefab.Color;
551  public bool TargetAllCharacters => Prefab.TargetAllCharacters;
552  public ref readonly ImmutableArray<Identifier> AppropriateJobs => ref Prefab.AppropriateJobs;
553  public float FadeOutTime => Prefab.FadeOutTime;
554  public bool MustSetTarget => Prefab.MustSetTarget;
555  public Identifier AppropriateSkill => Prefab.AppropriateSkill;
556  public OrderCategory? Category => Prefab.Category;
557  public bool MustManuallyAssign => Prefab.MustManuallyAssign;
558  public bool IsIgnoreOrder => Prefab.IsIgnoreOrder;
559  public bool IsDeconstructOrder => Prefab.IsDeconstructOrder;
560  public bool DrawIconWhenContained => Prefab.DrawIconWhenContained;
561  public bool Hidden => Prefab.Hidden;
562  public bool IgnoreAtOutpost => Prefab.IgnoreAtOutpost;
563  public bool IsReport => Prefab.IsReport;
564  public bool AutoDismiss => Prefab.AutoDismiss;
565  public int AssignmentPriority => Prefab.AssignmentPriority;
566 
567  public bool ColoredWhenControllingGiver => Prefab.ColoredWhenControllingGiver;
568  public bool DisplayGiverInTooltip => Prefab.DisplayGiverInTooltip;
569 
570  public readonly bool UseController;
571 
575  public Order(OrderPrefab prefab, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
576  : this(prefab, Identifier.Empty, 0, OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
577 
581  public Order(OrderPrefab prefab, Identifier option, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
582  : this(prefab, option, 0, OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
583 
587  public Order(OrderPrefab prefab, OrderTarget target, Character orderGiver = null)
588  : this(prefab, prefab.Options.FirstOrDefault(), 0, OrderType.Current, null, target, orderGiver) { }
589 
593  public Order(OrderPrefab prefab, Identifier option, OrderTarget target, Character orderGiver = null)
594  : this(prefab, option, 0, OrderType.Current, null, target, orderGiver) { }
595 
599  public Order(OrderPrefab prefab, Structure wall, int? sectionIndex, Character orderGiver = null)
600  : this(prefab, Identifier.Empty, 0, OrderType.Current, null, wall, sectionIndex, orderGiver) { }
601 
605  public Order(OrderPrefab prefab, Identifier option, Structure wall, int? sectionIndex, Character orderGiver = null)
606  : this(prefab, option, 0, OrderType.Current, null, wall, sectionIndex, orderGiver) { }
607 
611  private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
612  {
613  Prefab = prefab;
614  Option = option;
615  ManualPriority = manualPriority;
616  Type = orderType;
617  Objective = aiObjective;
618 
619  UseController = Prefab.UseController;
620 
621  OrderGiver = orderGiver;
622  TargetEntity = targetEntity;
623  if (targetItem != null)
624  {
625  if (UseController)
626  {
628  if (ConnectedController == null)
629  {
630  DebugConsole.AddWarning("AI: Tried to use a controller for operating an item, but couldn't find any.");
631  UseController = false;
632  }
633  }
634  TargetEntity = targetItem.Item;
635  TargetItemComponent = targetItem;
636  }
637 
638  TargetType = OrderTargetType.Entity;
639  }
640 
644  private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, OrderTarget target, Character orderGiver = null)
645  : this(prefab, option, manualPriority, orderType, aiObjective, targetEntity: null, targetItem: null, orderGiver)
646  {
647  TargetPosition = target;
648  TargetType = OrderTargetType.Position;
649  }
650 
654  private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Structure wall, int? sectionIndex, Character orderGiver = null)
655  : this(prefab, option, manualPriority, orderType, aiObjective, targetEntity: wall, null, orderGiver: orderGiver)
656  {
657  WallSectionIndex = sectionIndex;
658  TargetType = OrderTargetType.WallSection;
659  }
660 
661  private Order(
662  Order other,
663  OrderPrefab prefab = null,
664  Identifier option = default,
665  int? manualPriority = null,
666  OrderType? type = null,
667  AIObjective objective = null,
668  Entity targetEntity = null,
669  ItemComponent targetItemComponent = null,
670  Controller connectedController = null,
671  Character orderGiver = null,
672  OrderTarget targetPosition = null,
673  OrderTargetType? targetType = null,
674  int? wallSectionIndex = null,
675  bool? useController = null)
676  {
677  Prefab = prefab ?? other.Prefab;
678  Option = option.IfEmpty(other.Option);
679  ManualPriority = manualPriority ?? other.ManualPriority;
680  Type = type ?? other.Type;
681  Objective = objective ?? other.Objective;
682 
683  TargetEntity = targetEntity ?? other.TargetEntity;
684  TargetItemComponent = targetItemComponent ?? other.TargetItemComponent;
685  ConnectedController = connectedController ?? other.ConnectedController;
686 
687  OrderGiver = orderGiver ?? other.OrderGiver;
688 
689  TargetPosition = targetPosition ?? other.TargetPosition;
690 
691  TargetType = targetType ?? other.TargetType;
692  WallSectionIndex = wallSectionIndex ?? other.WallSectionIndex;
693 
694  UseController = useController ?? other.UseController;
695 
696 #if DEBUG
697  if (UseController && ConnectedController == null)
698  {
699  DebugConsole.ThrowError($"AI: Created an Order {Identifier} that's set to use a Controller, but a Controller was not specified.\n{Environment.StackTrace.CleanupStackTrace()}");
700  }
701 #endif
702  }
703 
704  public Order WithOption(Identifier option)
705  {
706  return new Order(this, option: option);
707  }
708 
709  public Order WithManualPriority(int newPriority)
710  {
711  return new Order(this, manualPriority: newPriority);
712  }
713 
714  public Order WithOrderGiver(Character orderGiver)
715  {
716  return new Order(this, orderGiver: orderGiver);
717  }
718 
719  public Order WithObjective(AIObjective objective)
720  {
721  return new Order(this, objective: objective);
722  }
723 
725  {
726  return new Order(this, targetEntity: entity, targetType: OrderTargetType.Entity);
727  }
728 
730  {
731  if (spatialEntity is WallSection wallSection)
732  {
733  Structure wall = wallSection.Wall;
734  int sectionIndex = wall.Sections.IndexOf(wallSection);
735  return WithWallSection(wall, sectionIndex);
736  }
737  else if (spatialEntity is Entity entity)
738  {
739  return WithTargetEntity(entity);
740  }
741  else if (spatialEntity is OrderTarget orderTarget)
742  {
743  return WithTargetPosition(orderTarget);
744  }
745 
746  throw new InvalidOperationException($"Unexpected input type: {spatialEntity.GetType().Name}");
747  }
748 
749  public Order WithItemComponent(Item item, ItemComponent component = null)
750  {
751  Controller controller = null;
752  if (UseController)
753  {
754  controller = item?.FindController(tags: ControllerTags);
755  }
756  return new Order(this, targetEntity: item, targetItemComponent: component ?? GetTargetItemComponent(item), connectedController: controller);
757  }
758 
759  public Order WithWallSection(Structure wall, int? sectionIndex)
760  {
761  return new Order(this, targetEntity: wall, wallSectionIndex: sectionIndex, targetType: OrderTargetType.WallSection);
762  }
763 
764  public Order WithType(OrderType type)
765  {
766  return new Order(this, type: type);
767  }
768 
769  public Order WithTargetPosition(OrderTarget targetPosition)
770  {
771  return new Order(this, targetPosition: targetPosition, targetType: OrderTargetType.Position);
772  }
773 
774  public Order Clone()
775  {
776  return new Order(this);
777  }
778 
780  {
781  if (IsDismissal) { throw new InvalidOperationException("Attempted to dismiss a dismissal order"); }
782  return new Order(this, prefab: OrderPrefab.Prefabs["dismissed"], option: GetDismissOrderOption(this));
783  }
784 
785  public bool HasAppropriateJob(Character character)
786  => Prefab.HasAppropriateJob(character);
787 
788  public bool HasPreferredJob(Character character)
789  => Prefab.HasPreferredJob(character);
790 
791  public string GetChatMessage(
792  string targetCharacterName, string targetRoomName, bool givingOrderToSelf, Identifier orderOption = default, bool isNewOrder = true)
793  => Prefab.GetChatMessage(targetCharacterName, targetRoomName, TargetEntity, givingOrderToSelf, orderOption, isNewOrder);
794 
799  {
800  return Prefab.GetTargetItemComponent(item);
801  }
802 
803  public bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
804  {
805  return Prefab.TryGetTargetItemComponent(item, out firstMatchingComponent);
806  }
807 
809  public List<Item> GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam = null, Character interactableFor = null)
810  {
811  return Prefab.GetMatchingItems(submarine, mustBelongToPlayerSub, requiredTeam, interactableFor);
812  }
813 
814 
816  public List<Item> GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor = null)
817  {
818  return Prefab.GetMatchingItems(mustBelongToPlayerSub, interactableFor);
819  }
820 
821  public LocalizedString GetOptionName(string id)
822  {
823  return Prefab.GetOptionName(id);
824  }
825 
827  {
828  return Prefab.GetOptionName(id);
829  }
830 
831  public LocalizedString GetOptionName(int index)
832  {
833  return Prefab.GetOptionName(index);
834  }
835 
841  {
842  return OrderPrefab.GetDismissOrderOption(order);
843  }
844 
845  public bool MatchesOrder(Identifier orderIdentifier, Identifier orderOption) =>
846  orderIdentifier == Identifier && orderOption == Option;
847 
848  /*public bool MatchesOrder(Order order, Identifier option) =>
849  order != null && MatchesOrder(order.Identifier, option);*/
850 
851  public bool MatchesOrder(Order order) =>
852  order != null && MatchesOrder(order.Identifier, order.Option);
853 
854  public bool MatchesDismissedOrder(Identifier dismissOrderOption)
855  {
856  Identifier[] dismissedOrder = dismissOrderOption.Value.Split('.').Select(s => s.ToIdentifier()).ToArray();
857  if (dismissedOrder != null && dismissedOrder.Length > 0)
858  {
859  Identifier dismissedOrderIdentifier = dismissedOrder.Length > 0 ? dismissedOrder[0] : Identifier.Empty;
860  if (dismissedOrderIdentifier == Identifier.Empty || dismissedOrderIdentifier != Identifier) { return false; }
861  Identifier dismissedOrderOption = dismissedOrder.Length > 1 ? dismissedOrder[1] : Identifier.Empty;
862  if (dismissedOrderOption == Identifier.Empty && Option == Identifier.Empty) { return true; }
863  return dismissedOrderOption == Option;
864  }
865  else
866  {
867  return false;
868  }
869  }
870 
871  public ImmutableArray<Identifier> GetTargetItems(Identifier option = default)
872  => Prefab.GetTargetItems(option);
873 
874  public override string ToString()
875  {
876  return $"Order ({Name})";
877  }
878  }
879 }
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:704
readonly Entity TargetEntity
Definition: Order.cs:495
Order WithTargetPosition(OrderTarget targetPosition)
Definition: Order.cs:769
Order(OrderPrefab prefab, Identifier option, OrderTarget target, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.Position
Definition: Order.cs:593
bool IsDeconstructOrder
Definition: Order.cs:559
bool MatchesOrder(Order order)
bool AutoDismiss
Definition: Order.cs:564
ref readonly ImmutableArray< Identifier > AppropriateJobs
Definition: Order.cs:552
ItemComponent GetTargetItemComponent(Item item)
Get the target item component based on the target item type
Definition: Order.cs:798
readonly OrderType Type
Definition: Order.cs:484
LocalizedString ContextualName
Definition: Order.cs:539
bool MatchesDismissedOrder(Identifier dismissOrderOption)
Definition: Order.cs:854
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:581
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:575
Order GetDismissal()
Definition: Order.cs:779
LocalizedString Name
Definition: Order.cs:538
override string ToString()
Definition: Order.cs:874
ref readonly ImmutableArray< Identifier > HiddenOptions
Definition: Order.cs:547
readonly Character OrderGiver
Definition: Order.cs:499
ref readonly ImmutableArray< Identifier > TargetItems
Definition: Order.cs:544
bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
Definition: Order.cs:803
Order Clone()
Definition: Order.cs:774
static Identifier GetDismissOrderOption(Order order)
Used to create the order option for the Dismiss order to know which order it targets
Definition: Order.cs:840
Order WithType(OrderType type)
Definition: Order.cs:764
float FadeOutTime
Definition: Order.cs:553
bool IsReport
Definition: Order.cs:563
ISpatialEntity??? TargetSpatialEntity
Note this property doesn't return the follow target of the Follow objective, as expected!
Definition: Order.cs:509
LocalizedString GetOptionName(string id)
Definition: Order.cs:821
ref readonly ImmutableArray< Identifier > Options
Definition: Order.cs:546
bool IsIgnoreOrder
Definition: Order.cs:558
bool IsDismissal
Definition: Order.cs:487
readonly? int WallSectionIndex
Definition: Order.cs:536
Type ItemComponentType
Definition: Order.cs:541
readonly Identifier Option
Definition: Order.cs:482
LocalizedString GetOptionName(Identifier id)
Definition: Order.cs:826
bool HasPreferredJob(Character character)
bool CanTypeBeSubclass
Definition: Order.cs:542
bool IgnoreAtOutpost
Definition: Order.cs:562
Order WithTargetEntity(Entity entity)
Definition: Order.cs:724
readonly int ManualPriority
Definition: Order.cs:483
readonly OrderTarget TargetPosition
Definition: Order.cs:501
readonly bool UseController
Definition: Order.cs:570
Identifier AppropriateSkill
Definition: Order.cs:555
LocalizedString GetOptionName(int index)
Definition: Order.cs:831
bool DisplayGiverInTooltip
Definition: Order.cs:568
Order WithManualPriority(int newPriority)
Definition: Order.cs:709
Order(OrderPrefab prefab, OrderTarget target, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.Position
Definition: Order.cs:587
ref readonly ImmutableArray< Identifier > AllOptions
Definition: Order.cs:548
List< Item > GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor=null)
Only returns items which are interactable for this character
Definition: Order.cs:816
bool TargetAllCharacters
Definition: Order.cs:551
bool MustSetTarget
Definition: Order.cs:554
readonly Controller ConnectedController
Definition: Order.cs:497
OrderCategory? Category
Definition: Order.cs:556
Order(OrderPrefab prefab, Identifier option, Structure wall, int? sectionIndex, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.WallSection
Definition: Order.cs:605
readonly AIObjective Objective
Definition: Order.cs:485
Order WithItemComponent(Item item, ItemComponent component=null)
Definition: Order.cs:749
Hull TargetHull
Definition: Order.cs:527
ref readonly ImmutableArray< Identifier > RequireItems
Definition: Order.cs:545
Identifier Identifier
Definition: Order.cs:540
Order WithWallSection(Structure wall, int? sectionIndex)
Definition: Order.cs:759
bool IsCurrentOrder
Definition: Order.cs:486
bool MustManuallyAssign
Definition: Order.cs:557
readonly OrderPrefab Prefab
Definition: Order.cs:481
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:809
Order WithTargetSpatialEntity(ISpatialEntity spatialEntity)
Definition: Order.cs:729
Order(OrderPrefab prefab, Structure wall, int? sectionIndex, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.WallSection
Definition: Order.cs:599
bool MatchesOrder(Identifier orderIdentifier, Identifier orderOption)
ref readonly ImmutableArray< Identifier > ControllerTags
Definition: Order.cs:543
ImmutableArray< Identifier > GetTargetItems(Identifier option=default)
bool DrawIconWhenContained
Definition: Order.cs:560
readonly OrderTargetType TargetType
Definition: Order.cs:535
Sprite SymbolSprite
Definition: Order.cs:549
Order WithObjective(AIObjective objective)
Definition: Order.cs:719
bool HasAppropriateJob(Character character)
int AssignmentPriority
Definition: Order.cs:565
readonly ItemComponent TargetItemComponent
Definition: Order.cs:496
Order WithOrderGiver(Character orderGiver)
Definition: Order.cs:714
bool ColoredWhenControllingGiver
Definition: Order.cs:567
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:355
List< Item > GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor=null, Identifier orderOption=default)
Only returns items which are interactable for this character
Definition: Order.cs:388
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:407
static bool TargetItemsMatchItem(ImmutableArray< Identifier > targetItems, Item item)
Definition: Order.cs:448
bool TargetItemsMatchItem(Item item, Identifier option=default)
Definition: Order.cs:439
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:458
readonly bool IgnoreAtOutpost
Definition: Order.cs:121
static OrderPrefab Dismissal
Definition: Order.cs:44
ImmutableArray< Identifier > GetTargetItems(Identifier option=default)
Definition: Order.cs:427
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:396
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:362
ItemComponent GetTargetItemComponent(Item item)
Get the target item component based on the target item type
Definition: Order.cs:341
bool IsVisibleAsReportButton
Definition: Order.cs:88
override void Dispose()
Definition: Order.cs:453
string GetChatMessage(string targetCharacterName, string targetRoomName, Entity targetEntity, bool givingOrderToSelf, Identifier orderOption=default, bool isNewOrder=true)
Definition: Order.cs:283
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:417
LocalizedString GetOptionName(Identifier id)
Definition: Order.cs:401
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....
OrderCategory
Definition: Order.cs:12