3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
61 private readonly ImmutableDictionary<Identifier, ImmutableArray<Identifier>> OptionTargetItems;
64 private readonly Color? color;
106 public readonly ImmutableArray<Identifier>
Options;
165 Name = TextManager.Get($
"OrderName.{Identifier}");
169 if (!
string.IsNullOrWhiteSpace(targetItemType))
173 ItemComponentType = Type.GetType(
"Barotrauma.Items.Components." + targetItemType,
true,
true);
178 DebugConsole.ThrowError(
"Error in the order definitions: item component type " + targetItemType +
" not found", e);
194 var optionTargetItems =
new Dictionary<Identifier, ImmutableArray<Identifier>>();
195 if (orderElement.
GetAttributeString(
"targetitems",
"") is
string targetItems && targetItems.Contains(
';'))
197 string[] splitTargetItems = targetItems.Split(
';');
199 if (splitTargetItems.Length !=
AllOptions.Length)
201 DebugConsole.ThrowError($
"Order \"{Identifier}\" has option-specific target items, but the option count doesn't match the target item count");
204 var allTargetItems =
new List<Identifier>();
207 Identifier[] optionTargetItemsSplit = i < splitTargetItems.Length ? splitTargetItems[i].Split(
',',
',').ToIdentifiers() : Array.Empty<
Identifier>();
208 for (
int j = 0; j < optionTargetItemsSplit.Length; j++)
210 optionTargetItemsSplit[j] = optionTargetItemsSplit[j].Value.Trim().ToIdentifier();
211 allTargetItems.Add(optionTargetItemsSplit[j]);
213 optionTargetItems.Add(
AllOptions[i], optionTargetItemsSplit.ToImmutableArray());
222 OptionTargetItems = optionTargetItems.ToImmutableDictionary();
226 this.CategoryIdentifier = (this.Category?.ToString() ??
string.Empty).ToIdentifier();
238 if (spriteElement !=
null)
243 var optionSprites =
new Dictionary<Identifier, Sprite>();
247 if (optionSpriteElements !=
null && optionSpriteElements.Any())
249 for (
int i = 0; i <
Options.Length; i++)
251 if (i >= optionSpriteElements.Count()) {
break; };
252 var sprite =
new Sprite(optionSpriteElements.ElementAt(i), lazyLoad:
true);
253 optionSprites.Add(
Options[i], sprite);
267 private bool HasSpecifiedJob(
Character character, IReadOnlyList<Identifier> jobs)
269 if (jobs ==
null || jobs.Count == 0) {
return false; }
271 if (jobIdentifier.IsEmpty) {
return false; }
272 for (
int i = 0; i < jobs.Count; i++)
274 if (jobIdentifier == jobs[i]) {
return true; }
283 public string GetChatMessage(
string targetCharacterName,
string targetRoomName,
Entity targetEntity,
bool givingOrderToSelf,
Identifier orderOption =
default,
bool isNewOrder =
true)
288 if (!givingOrderToSelf)
290 return TextManager.GetWithVariable(
"rearrangedorders",
"[name]", targetCharacterName ??
string.Empty).Value;
298 string messageTag = $
"{(givingOrderToSelf && !TargetAllCharacters ? "OrderDialogSelf
" : "OrderDialog
")}.{Identifier}";
299 if (!orderOption.IsEmpty)
303 messageTag += $
".{orderOption}";
307 string[] splitOption = orderOption.Value.Split(
'.');
308 if (splitOption.Length > 0)
310 messageTag += $
".{splitOption[0]}";
316 switch (targetEntity)
319 targetEntityName = item.Name;
322 targetEntityName = hull.DisplayName;
325 targetEntityName = structure.Name;
332 return TextManager.GetWithVariables(messageTag,
333 (
"[name]", targetCharacterName ??
string.Empty,
FormatCapitals.No),
335 (
"[roomname]", targetRoomName ??
string.Empty,
FormatCapitals.Yes)).Fallback(
"").Value;
346 if (component?.GetType() is Type componentType)
358 return firstMatchingComponent !=
null;
364 List<Item> matchingItems =
new List<Item>();
365 if (submarine ==
null) {
return matchingItems; }
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; }
380 if (interactableFor !=
null && (!item.IsInteractable(interactableFor) || (
UseController && !controller.
Item.
IsInteractable(interactableFor)))) {
continue; }
381 matchingItems.Add(item);
384 return matchingItems;
393 return GetMatchingItems(submarine, mustBelongToPlayerSub, interactableFor: interactableFor, orderOption: orderOption);
409 if (index < 0 || index >=
Options.Length) {
return null; }
422 option = $
"{option}.{order.Option}".ToIdentifier();
429 if (option.IsEmpty || !OptionTargetItems.TryGetValue(option, out ImmutableArray<Identifier> optionTargetItems))
435 return optionTargetItems;
441 if (item ==
null) {
return false; }
450 return item !=
null && targetItems !=
null && targetItems.Length > 0 && (targetItems.Contains(item.
Prefab.
Identifier) || item.
HasTag(targetItems));
462 return targetType
switch
464 OrderTargetType.Entity =>
new Order(
this, targetEntity:
null, targetItem:
null, orderGiver, isAutonomous),
467 _ =>
throw new NotImplementedException()
470 catch (NotImplementedException e)
472 DebugConsole.LogError($
"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}",
512 if (targetSpatialEntity ==
null)
523 return targetSpatialEntity;
546 public ref readonly ImmutableArray<Identifier>
Options => ref
Prefab.Options;
576 : this(prefab,
Identifier.Empty, 0,
OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
582 : this(prefab, option, 0,
OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
588 : this(prefab, prefab.
Options.FirstOrDefault(), 0,
OrderType.Current, null, target, orderGiver) { }
594 : this(prefab, option, 0,
OrderType.Current, null, target, orderGiver) { }
600 : this(prefab,
Identifier.Empty, 0,
OrderType.Current, null, wall, sectionIndex, orderGiver) { }
606 : this(prefab, option, 0,
OrderType.Current, null, wall, sectionIndex, orderGiver) { }
623 if (targetItem !=
null)
630 DebugConsole.AddWarning(
"AI: Tried to use a controller for operating an item, but couldn't find any.");
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)
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)
663 OrderPrefab prefab =
null,
665 int? manualPriority =
null,
667 AIObjective objective =
null,
668 Entity targetEntity =
null,
671 Character orderGiver =
null,
672 OrderTarget targetPosition =
null,
674 int? wallSectionIndex =
null,
675 bool? useController =
null)
678 Option = option.IfEmpty(other.Option);
680 Type = type ?? other.Type;
681 Objective = objective ?? other.Objective;
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()}");
706 return new Order(
this, option: option);
711 return new Order(
this, manualPriority: newPriority);
716 return new Order(
this, orderGiver: orderGiver);
721 return new Order(
this, objective: objective);
734 int sectionIndex = wall.
Sections.IndexOf(wallSection);
737 else if (spatialEntity is
Entity entity)
746 throw new InvalidOperationException($
"Unexpected input type: {spatialEntity.GetType().Name}");
756 return new Order(
this, targetEntity: item, targetItemComponent: component ??
GetTargetItemComponent(item), connectedController: controller);
761 return new Order(
this, targetEntity: wall, wallSectionIndex: sectionIndex, targetType:
OrderTargetType.WallSection);
766 return new Order(
this, type: type);
776 return new Order(
this);
781 if (
IsDismissal) {
throw new InvalidOperationException(
"Attempted to dismiss a dismissal order"); }
786 =>
Prefab.HasAppropriateJob(character);
789 =>
Prefab.HasPreferredJob(character);
792 string targetCharacterName,
string targetRoomName,
bool givingOrderToSelf,
Identifier orderOption =
default,
bool isNewOrder =
true)
793 =>
Prefab.GetChatMessage(targetCharacterName, targetRoomName,
TargetEntity, givingOrderToSelf, orderOption, isNewOrder);
800 return Prefab.GetTargetItemComponent(item);
805 return Prefab.TryGetTargetItemComponent(item, out firstMatchingComponent);
811 return Prefab.GetMatchingItems(submarine, mustBelongToPlayerSub, requiredTeam, interactableFor);
818 return Prefab.GetMatchingItems(mustBelongToPlayerSub, interactableFor);
823 return Prefab.GetOptionName(
id);
828 return Prefab.GetOptionName(
id);
833 return Prefab.GetOptionName(index);
852 order !=
null &&
MatchesOrder(order.Identifier, order.Option);
856 Identifier[] dismissedOrder = dismissOrderOption.Value.Split(
'.').Select(s => s.ToIdentifier()).ToArray();
857 if (dismissedOrder !=
null && dismissedOrder.Length > 0)
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;
863 return dismissedOrderOption ==
Option;
872 =>
Prefab.GetTargetItems(option);
876 return $
"Order ({Name})";
static Character? Controlled
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)
static GameSession?? GameSession
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
bool HasTag(Identifier tag)
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
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
static readonly PrefabCollection< OrderCategoryIcon > OrderCategoryIcons
OrderCategoryIcon(ContentXElement element, OrdersFile file)
readonly OrderCategory Category
Order WithOption(Identifier option)
readonly Entity TargetEntity
Order WithTargetPosition(OrderTarget targetPosition)
Order(OrderPrefab prefab, Identifier option, OrderTarget target, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.Position
bool MatchesOrder(Order order)
ref readonly ImmutableArray< Identifier > AppropriateJobs
ItemComponent GetTargetItemComponent(Item item)
Get the target item component based on the target item type
LocalizedString ContextualName
bool MatchesDismissedOrder(Identifier dismissOrderOption)
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
Order(OrderPrefab prefab, Entity targetEntity, ItemComponent targetItem, Character orderGiver=null, bool isAutonomous=false)
Constructor for orders with the target type OrderTargetType.Entity
override string ToString()
ref readonly ImmutableArray< Identifier > HiddenOptions
readonly Character OrderGiver
ref readonly ImmutableArray< Identifier > TargetItems
bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
static Identifier GetDismissOrderOption(Order order)
Used to create the order option for the Dismiss order to know which order it targets
Order WithType(OrderType type)
ISpatialEntity??? TargetSpatialEntity
Note this property doesn't return the follow target of the Follow objective, as expected!
LocalizedString GetOptionName(string id)
ref readonly ImmutableArray< Identifier > Options
readonly? int WallSectionIndex
readonly Identifier Option
LocalizedString GetOptionName(Identifier id)
bool HasPreferredJob(Character character)
Order WithTargetEntity(Entity entity)
readonly int ManualPriority
readonly OrderTarget TargetPosition
readonly bool UseController
Identifier AppropriateSkill
LocalizedString GetOptionName(int index)
bool DisplayGiverInTooltip
Order WithManualPriority(int newPriority)
Order(OrderPrefab prefab, OrderTarget target, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.Position
ref readonly ImmutableArray< Identifier > AllOptions
List< Item > GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor=null)
Only returns items which are interactable for this character
readonly Controller ConnectedController
Order(OrderPrefab prefab, Identifier option, Structure wall, int? sectionIndex, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.WallSection
readonly AIObjective Objective
Order WithItemComponent(Item item, ItemComponent component=null)
ref readonly ImmutableArray< Identifier > RequireItems
Order WithWallSection(Structure wall, int? sectionIndex)
readonly OrderPrefab Prefab
List< Item > GetMatchingItems(Submarine submarine, bool mustBelongToPlayerSub, CharacterTeamType? requiredTeam=null, Character interactableFor=null)
Only returns items which are interactable for this character
Order WithTargetSpatialEntity(ISpatialEntity spatialEntity)
Order(OrderPrefab prefab, Structure wall, int? sectionIndex, Character orderGiver=null)
Constructor for orders with the target type OrderTargetType.WallSection
bool MatchesOrder(Identifier orderIdentifier, Identifier orderOption)
ref readonly ImmutableArray< Identifier > ControllerTags
ImmutableArray< Identifier > GetTargetItems(Identifier option=default)
bool DrawIconWhenContained
readonly OrderTargetType TargetType
Order WithObjective(AIObjective objective)
bool HasAppropriateJob(Character character)
readonly ItemComponent TargetItemComponent
Order WithOrderGiver(Character orderGiver)
bool ColoredWhenControllingGiver
readonly ImmutableArray< Identifier > RequireItems
readonly ImmutableArray< Identifier > HiddenOptions
OrderPrefab(ContentXElement orderElement, OrdersFile file)
readonly bool UseController
readonly LocalizedString Name
bool HasOptionSpecificTargetItems
bool TryGetTargetItemComponent(Item item, out ItemComponent firstMatchingComponent)
List< Item > GetMatchingItems(bool mustBelongToPlayerSub, Character interactableFor=null, Identifier orderOption=default)
Only returns items which are interactable for this character
readonly ImmutableArray< Identifier > TargetItems
readonly bool AutoDismiss
If enabled and this is an Operate order, it will remove Operate orders of the same item from other ch...
readonly? OrderCategory Category
readonly Identifier CategoryIdentifier
readonly bool MustSetTarget
readonly bool CanTypeBeSubclass
readonly Sprite SymbolSprite
bool ColoredWhenControllingGiver
LocalizedString GetOptionName(int index)
static bool TargetItemsMatchItem(ImmutableArray< Identifier > targetItems, Item item)
bool TargetItemsMatchItem(Item item, Identifier option=default)
readonly bool TargetAllCharacters
readonly bool MustManuallyAssign
readonly ImmutableArray< Identifier > AllOptions
readonly ImmutableDictionary< Identifier, Sprite > OptionSprites
bool HasPreferredJob(Character character)
readonly Identifier AppropriateSkill
int AssignmentPriority
Affects how high on the order list the order will be placed (i.e. the manual priority order when it's...
readonly float FadeOutTime
readonly ImmutableArray< Identifier > AppropriateJobs
If defined, the order can only be quick-assigned to characters with these jobs. Or if it's a report,...
static readonly PrefabCollection< OrderPrefab > Prefabs
readonly ImmutableArray< Identifier > PreferredJobs
If defined, the order will be quick-assigned to characters with these jobs before characters with oth...
Order CreateInstance(OrderTargetType targetType, Character orderGiver=null, bool isAutonomous=false)
Create an Order instance with a null target
readonly bool IgnoreAtOutpost
static OrderPrefab Dismissal
ImmutableArray< Identifier > GetTargetItems(Identifier option=default)
readonly bool CanBeGeneralized
Can the order be turned into a non-entity-targeting one if it was originally created with a target en...
bool HasAppropriateJob(Character character)
bool DrawIconWhenContained
Should the order icon be drawn when the order target is inside a container
LocalizedString GetOptionName(string id)
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
ItemComponent GetTargetItemComponent(Item item)
Get the target item component based on the target item type
bool IsVisibleAsReportButton
string GetChatMessage(string targetCharacterName, string targetRoomName, Entity targetEntity, bool givingOrderToSelf, Identifier orderOption=default, bool isNewOrder=true)
readonly Type ItemComponentType
static readonly Identifier DismissalIdentifier
readonly ListDictionary< Identifier, LocalizedString > OptionNames
readonly ImmutableArray< Identifier > Options
readonly ImmutableArray< Identifier > ControllerTags
readonly LocalizedString ContextualName
Name that can be used with the contextual version of the order
OrderTargetType TargetType
static Identifier GetDismissOrderOption(Order order)
Used to create the order option for the Dismiss order to know which order it targets
LocalizedString GetOptionName(Identifier id)
bool DisplayGiverInTooltip
Prefab(ContentFile file, Identifier identifier)
readonly Identifier Identifier
Prefab that has a property serves as a deterministic hash of a prefab's identifier....
static readonly Submarine[] MainSubs
IEnumerable< Submarine > DockedTo