3 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
15 [
Serialize(
"",
IsPropertySaveable.Yes, description:
"Either the tag of the item(s) we want to check, or a character/container the items are inside.")]
42 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"If enabled, the doesn't need to be directly inside the container/character we're checking, but can be nested inside multiple containers (e.g. in a toolbelt in a character's inventory).")]
45 [
Serialize(-1,
IsPropertySaveable.Yes, description:
"Can be used to require the item to be in a specific ItemContainer of the target container. For example, the input slots of a fabricator (the first ItemContainer of the fabricator, with an index of 0).")]
48 private readonly
bool checkPercentage;
50 private float requiredConditionalMatchPercentage;
52 [
Serialize(100.0f,
IsPropertySaveable.Yes, description:
"What percentage of targets do the conditionals need to match for the check to succeed?")]
55 get {
return requiredConditionalMatchPercentage; }
56 set { requiredConditionalMatchPercentage = MathHelper.Clamp(value, 0.0f, 100.0f); }
59 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"When enabled, the number of matching items is compared to the number of matching items there were at the start of the round. Only valid if RequiredConditionalMatchPercentage is set.")]
62 private readonly IReadOnlyList<PropertyConditional> conditionals;
64 private readonly Identifier[] itemIdentifierSplit;
65 private readonly Identifier[] itemTags;
70 itemTags =
ItemTags.ToIdentifiers().ToArray();
71 var conditionalList =
new List<PropertyConditional>();
76 conditionals = conditionalList;
78 if (itemTags.None() &&
82 DebugConsole.ThrowError($
"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} does't define either tags or identifiers of the item to check.",
83 contentPackage: element.ContentPackage);
86 if (checkPercentage && conditionals.None())
88 DebugConsole.ThrowError($
"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} requires conditionals to be met on {requiredConditionalMatchPercentage}% of the targets, but there are no conditionals defined.");
90 if (
Amount != 1 && checkPercentage)
92 DebugConsole.ThrowError($
"Error in event \"{ParentEvent.Prefab.Identifier}\". Cannot define both '{Amount}' and '{RequiredConditionalMatchPercentage}' in {nameof(CheckItemAction)}.",
93 contentPackage: element.ContentPackage);
97 private bool EnoughTargets(
int totalTargets,
int targetsWithConditionalsMatched)
109 return targetsWithConditionalsMatched >=
Amount;
113 private readonly List<Item> tempTargetItems =
new List<Item>();
121 targets = targets.Where(t =>
122 (t is
Item it && hulls.Contains(it.CurrentHull)) ||
123 (t is
Character c && hulls.Contains(c.CurrentHull)));
128 if (conditionals.Any())
137 int targetCount = targets.Count();
138 if (targetCount >=
Amount)
140 tempTargetItems.Clear();
141 foreach (var target
in targets)
143 if (target is not
Item item) {
continue; }
144 if (itemTags.Any(item.HasTag) || itemIdentifierSplit.Contains(item.Prefab.Identifier) ||
145 (itemTags.None() && itemIdentifierSplit.None() && conditionals.Any()))
147 if (ConditionalsMatch(item, character:
null))
149 tempTargetItems.Add(item);
153 if (EnoughTargets(targetCount, tempTargetItems.Count))
155 TryApplyTagToItems(tempTargetItems);
160 foreach (var target
in targets)
165 if (CheckInventory(character.Inventory, character))
174 else if (target is
Item item)
177 foreach (var itemContainer
in item.GetComponents<
ItemContainer>())
181 if (CheckInventory(itemContainer.Inventory, character:
null))
199 if (inventory ==
null) {
return false; }
201 HashSet<Item> eventTargets =
new HashSet<Item>();
202 tempTargetItems.Clear();
203 foreach (Identifier tag
in itemTags)
207 if (target is Item item)
209 eventTargets.Add(item);
214 itemTags.Any(it.HasTag) ||
215 itemIdentifierSplit.Contains(it.Prefab.Identifier) ||
216 eventTargets.Contains(it),
220 if (ConditionalsMatch(item, character))
222 tempTargetItems.Add(item);
226 if (EnoughTargets(targetCount, tempTargetItems.Count))
228 TryApplyTagToItems(tempTargetItems);
235 private void TryApplyTagToItems(IEnumerable<Item> items)
239 foreach (var targetItem
in items)
246 private bool ConditionalsMatch(Item item, Character character =
null)
248 if (item ==
null) {
return false; }
249 foreach (PropertyConditional conditional
in conditionals)
251 if (!item.ConditionalMatches(conditional))
258 if (character ==
null) {
return false; }
263 if (character ==
null) {
return false; }
264 foreach (var wearable
in item.GetComponents<
Wearable>())
266 foreach (var allowedSlot
in wearable.AllowedSlots)
279 return $
"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CheckItemAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " +
280 (
ItemTags.Any() ? $
"ItemTags: {ItemTags.ColorizeObject()}, " : $
"ItemIdentifiers: {ItemIdentifiers.ColorizeObject()}, ") +
281 $
"Succeeded: {succeeded.ColorizeObject()})";
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
Can be used to do various kinds of checks on items: whether a specific kind of item exists,...
CheckItemAction(ScriptedEvent parentEvent, ContentXElement element)
override? bool DetermineSuccess()
Identifier ApplyTagToTarget
bool CompareToInitialAmount
override string ToDebugString()
Rich test to display in debugdraw
float RequiredConditionalMatchPercentage
Identifier ApplyTagToItem
IEnumerable< ContentXElement > GetChildElements(string name)
readonly ScriptedEvent ParentEvent
Inventory(Entity owner, int capacity, int slotsPerRow=5)
List< Item > FindAllItems(Func< Item, bool > predicate=null, bool recursive=false, List< Item > list=null)
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
static IEnumerable< PropertyConditional > FromXElement(ContentXElement element, Predicate< XAttribute >? predicate=null)
int GetInitialTargetCount(Identifier tag)
void AddTarget(Identifier tag, Entity target)
IEnumerable< Entity > GetTargets(Identifier tag)