3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using System.Globalization;
41 PropertyValueOrAffliction,
220 foreach (var attribute
in element.Attributes())
222 if (!IsValid(attribute)) {
continue; }
223 if (predicate !=
null && !predicate(attribute)) {
continue; }
225 var (comparisonOperator, attributeValueString) = ExtractComparisonOperatorFromConditionString(attribute.Value);
226 if (
string.IsNullOrWhiteSpace(attributeValueString))
228 DebugConsole.ThrowError($
"Conditional attribute value is empty: {element}", contentPackage: element.ContentPackage);
232 var conditionType = overrideConditionType ??
233 (Enum.TryParse(attribute.Name.LocalName, ignoreCase:
true, out
ConditionType type)
238 attributeName: attribute.NameAsIdentifier(),
239 comparisonOperator: comparisonOperator,
240 attributeValue: attributeValueString,
241 targetItemComponent: targetItemComponent,
242 targetSelf: targetSelf,
243 targetContainer: targetContainer,
244 targetGrandParent: targetGrandParent,
245 targetContainedItem: targetContainedItem,
246 conditionType: conditionType);
250 private static bool IsValid(XAttribute attribute)
252 switch (attribute.Name.ToString().ToLowerInvariant())
254 case "targetitemcomponent":
256 case "targetcontainer":
257 case "targetgrandparent":
258 case "targetcontaineditem":
259 case "skillrequirement":
267 private PropertyConditional(
268 Identifier attributeName,
270 string attributeValue,
271 string targetItemComponent,
273 bool targetContainer,
274 bool targetGrandParent,
275 bool targetContainedItem,
286 Type = conditionType;
291 .Select(s => s.ToIdentifier())
293 if (
float.TryParse(
AttributeValue, NumberStyles.Float, CultureInfo.InvariantCulture, out
float value))
300 cachedHostilityValue = hostilityValue;
309 string conditionStr = str;
310 if (str.IndexOf(
' ') is var i and >= 0)
316 return (op, conditionStr);
322 switch (op.ToLowerInvariant())
344 case "greaterthanequals":
348 case "lessthanequals":
360 ? MatchesContained(target)
361 : MatchesDirect(target);
366 var containedItems = target
switch
375 => Enumerable.Empty<
Item>()
377 foreach (var containedItem
in containedItems)
379 if (MatchesDirect(containedItem)) {
return true; }
384 private bool MatchesDirect(ISerializableEntity? target)
387 if (target is Limb limb) { targetChar = limb.character; }
395 if (targetChar is { CharacterHealth: { } health })
398 float afflictionStrength = affliction?.Strength ?? 0f;
400 return NumberMatchesRequirement(afflictionStrength);
404 else if (target?.SerializableProperties !=
null
405 && target.SerializableProperties.TryGetValue(
AttributeName, out var property))
407 return PropertyMatchesRequirement(target, property);
409 else if (targetChar?.SerializableProperties !=
null
410 && targetChar.SerializableProperties.TryGetValue(
AttributeName, out var characterProperty))
412 return PropertyMatchesRequirement(targetChar, characterProperty);
414 return ComparisonOperatorIsNotEquals;
416 if (targetChar !=
null)
418 float skillLevel = targetChar.GetSkillLevel(
AttributeName.ToIdentifier());
420 return NumberMatchesRequirement(skillLevel);
422 return ComparisonOperatorIsNotEquals;
424 return ItemMatchesTagCondition(target);
426 if (target ==
null) {
return ComparisonOperatorIsNotEquals; }
428 int numTagsFound = 0;
431 bool tagFound =
false;
432 foreach (var durationEffect
in StatusEffect.DurationList)
434 if (!durationEffect.Targets.Contains(target)) {
continue; }
435 if (durationEffect.Parent.HasTag(tag))
443 foreach (var delayedEffect
in DelayedEffect.DelayList)
445 if (!delayedEffect.Targets.Contains(target)) {
continue; }
446 if (delayedEffect.Parent.HasTag(tag))
458 return ComparisonOperatorIsNotEquals
463 if (GameMain.GameSession?.Campaign is CampaignMode campaign)
465 return Compare(campaign.Settings.WorldHostility, cachedHostilityValue,
ComparisonOperator);
470 bool equals = CheckOnlyEquality(target);
471 return ComparisonOperatorIsNotEquals
477 private bool CheckOnlyEquality(ISerializableEntity? target)
482 if (target ==
null) {
return false; }
487 if (target is not Character {Info: { } characterInfo})
496 if (target is Character targetCharacter)
500 else if (target is Limb targetLimb)
508 if (target is Character targetCharacter)
510 return CharacterParams.CompareGroup(
AttributeValue.ToIdentifier(), targetCharacter.Params.Group);
512 else if (target is Limb targetLimb)
514 return CharacterParams.CompareGroup(
AttributeValue.ToIdentifier(), targetLimb.character.Params.Group);
536 return target is Limb limb
537 && Enum.TryParse(
AttributeValue, ignoreCase:
true, out LimbType attributeLimbType)
538 && attributeLimbType == limb.type;
544 private bool SufficientTagMatches(
int matches)
546 return ComparisonOperatorIsNotEquals
551 private bool ItemMatchesTagCondition(ISerializableEntity? target)
553 if (target is not Item item) {
return ComparisonOperatorIsNotEquals; }
558 if (item.HasTag(tag)) { matches++; }
560 return SufficientTagMatches(matches);
570 if (targetTag == tag) { matches++; }
572 return SufficientTagMatches(matches);
575 private bool NumberMatchesRequirement(
float testedValue)
577 if (!
FloatValue.HasValue) {
return ComparisonOperatorIsNotEquals; }
582 private bool PropertyMatchesRequirement(ISerializableEntity target, SerializableProperty property)
584 Type type =
property.PropertyType;
586 if (type == typeof(
float) || type == typeof(
int))
588 float floatValue =
property.GetFloatValue(target);
589 return NumberMatchesRequirement(floatValue);
597 if (type == typeof(
bool))
600 equals =
property.GetBoolValue(target) == attributeValueBool;
604 var value =
property.GetValue(target);
608 return ComparisonOperatorIsNotEquals
612 DebugConsole.ThrowError(
"Couldn't compare " +
AttributeValue.ToString() +
" (" +
AttributeValue.GetType() +
") to property \"" + property.Name +
"\" (" + type +
")! "
613 +
"Make sure the type of the value set in the config files matches the type of the property.");
617 static bool AreValuesEquivalent(
object? value,
string desiredValue)
621 return desiredValue.Equals(
"null", StringComparison.OrdinalIgnoreCase);
625 return (value.ToString() ??
"").Equals(desiredValue);
635 return MathUtils.NearlyEqual(val1, val2);
645 return !MathUtils.NearlyEqual(val1, val2);
653 return comparisonOperator
switch
660 _ => leftValue.CompareTo(rightValue) == 0,
string? GetAttributeString(string key, string? def)
bool GetAttributeBool(string key, bool def)
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
IEnumerable< Item > ContainedItems
The base class for components holding the different functionalities of the item
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
readonly ComparisonOperatorType ComparisonOperator
bool Matches(ISerializableEntity? target)
readonly bool TargetContainer
If set to true, the conditionals defined by this element check against the entity containing the targ...
bool TargetTagMatchesTagCondition(Identifier targetTag)
static ComparisonOperatorType GetComparisonOperatorType(string op)
readonly string AttributeValue
readonly bool TargetGrandParent
If this and TargetContainer are set to true, the conditionals defined by this element check against t...
readonly string TargetItemComponent
If set to the name of one of the target's ItemComponents, the conditionals defined by this element ch...
static bool Compare< T >(T leftValue, T rightValue, ComparisonOperatorType comparisonOperator)
ComparisonOperatorType
There are several ways to compare properties to values. The comparison operator to use can be specifi...
readonly ImmutableArray< Identifier > AttributeValueAsTags
readonly bool TargetContainedItem
If set to true, the conditionals defined by this element check against the items contained by the tar...
static bool CompareFloat(float val1, float val2, ComparisonOperatorType op)
readonly? float FloatValue
readonly bool TargetSelf
If set to true, the conditionals defined by this element check against the attacking character instea...
readonly ConditionType Type
static IEnumerable< PropertyConditional > FromXElement(ContentXElement element, Predicate< XAttribute >? predicate=null)
readonly Identifier AttributeName
static ComparisonOperatorType ComparisonOperator
ConditionType
Category of properties to check against