Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -5,6 +5,7 @@ using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
namespace Barotrauma
@@ -68,7 +69,7 @@ namespace Barotrauma
SpeciesGroup,
/// <summary>
/// Check against the target's tags. Only works on items.
/// Check against the target's tags. Only works on items and characters.
///
/// Several tags can be checked against by using a comma-separated list.
/// </summary>
@@ -104,7 +105,12 @@ namespace Barotrauma
/// <summary>
/// Check against the current World Hostility setting (previously known as "Difficulty").
/// </summary>
WorldHostility
WorldHostility,
/// <summary>
/// Check against the difficulty of the current level.
/// </summary>
LevelDifficulty
}
public enum LogicalOperatorType
@@ -180,6 +186,11 @@ namespace Barotrauma
/// Only works on items.
/// </summary>
public readonly string TargetItemComponent;
/// <summary>
/// When targeting item components, should we require them all to match the conditional or any (default).
/// </summary>
public readonly LogicalOperatorType ItemComponentComparison;
/// <summary>
/// If set to true, the conditionals defined by this element check against the attacking character instead of the attacked character.
@@ -205,11 +216,13 @@ namespace Barotrauma
public static IEnumerable<PropertyConditional> FromXElement(ContentXElement element, Predicate<XAttribute>? predicate = null)
{
var targetItemComponent = element.GetAttributeString(nameof(TargetItemComponent), "");
var targetContainer = element.GetAttributeBool(nameof(TargetContainer), false);
var targetSelf = element.GetAttributeBool(nameof(TargetSelf), false);
var targetGrandParent = element.GetAttributeBool(nameof(TargetGrandParent), false);
var targetContainedItem = element.GetAttributeBool(nameof(TargetContainedItem), false);
string targetItemComponent = element.GetAttributeString(nameof(TargetItemComponent), string.Empty);
bool targetContainer = element.GetAttributeBool(nameof(TargetContainer), false);
bool targetSelf = element.GetAttributeBool(nameof(TargetSelf), false);
bool targetGrandParent = element.GetAttributeBool(nameof(TargetGrandParent), false);
bool targetContainedItem = element.GetAttributeBool(nameof(TargetContainedItem), false);
LogicalOperatorType itemComponentComparison = element.GetAttributeEnum(nameof(ItemComponentComparison), LogicalOperatorType.Or);
ConditionType? overrideConditionType = null;
if (element.GetAttributeBool(nameof(ConditionType.SkillRequirement), false))
@@ -222,23 +235,22 @@ namespace Barotrauma
if (!IsValid(attribute)) { continue; }
if (predicate != null && !predicate(attribute)) { continue; }
var (comparisonOperator, attributeValueString) = ExtractComparisonOperatorFromConditionString(attribute.Value);
(ComparisonOperatorType comparisonOperator, string attributeValueString) = ExtractComparisonOperatorFromConditionString(attribute.Value);
if (string.IsNullOrWhiteSpace(attributeValueString))
{
DebugConsole.ThrowError($"Conditional attribute value is empty: {element}", contentPackage: element.ContentPackage);
continue;
}
var conditionType = overrideConditionType ??
(Enum.TryParse(attribute.Name.LocalName, ignoreCase: true, out ConditionType type)
? type
: ConditionType.PropertyValueOrAffliction);
ConditionType conditionType = overrideConditionType ??
(Enum.TryParse(attribute.Name.LocalName, ignoreCase: true, out ConditionType type) ? type : ConditionType.PropertyValueOrAffliction);
yield return new PropertyConditional(
attributeName: attribute.NameAsIdentifier(),
comparisonOperator: comparisonOperator,
attributeValue: attributeValueString,
targetItemComponent: targetItemComponent,
itemComponentComparison: itemComponentComparison,
targetSelf: targetSelf,
targetContainer: targetContainer,
targetGrandParent: targetGrandParent,
@@ -269,6 +281,7 @@ namespace Barotrauma
ComparisonOperatorType comparisonOperator,
string attributeValue,
string targetItemComponent,
LogicalOperatorType itemComponentComparison,
bool targetSelf,
bool targetContainer,
bool targetGrandParent,
@@ -278,6 +291,7 @@ namespace Barotrauma
AttributeName = attributeName;
TargetItemComponent = targetItemComponent;
ItemComponentComparison = itemComponentComparison;
TargetSelf = targetSelf;
TargetContainer = targetContainer;
TargetGrandParent = targetGrandParent;
@@ -421,7 +435,15 @@ namespace Barotrauma
}
return ComparisonOperatorIsNotEquals;
case ConditionType.HasTag:
return ItemMatchesTagCondition(target);
if (targetChar != null)
{
return CheckMatchingTags(targetChar.Params.HasTag);
}
if (target is Item item)
{
return CheckMatchingTags(item.HasTag);
}
return ComparisonOperatorIsNotEquals;
case ConditionType.HasStatusTag:
if (target == null) { return ComparisonOperatorIsNotEquals; }
@@ -458,14 +480,18 @@ namespace Barotrauma
return ComparisonOperatorIsNotEquals
? numTagsFound < AttributeValueAsTags.Length // true when some tag wasn't found
: numTagsFound >= AttributeValueAsTags.Length; // true when all the tags are found
case ConditionType.WorldHostility:
{
case ConditionType.LevelDifficulty:
if (Level.Loaded is { } level)
{
return NumberMatchesRequirement(level.Difficulty);
}
return false;
case ConditionType.WorldHostility:
if (GameMain.GameSession?.Campaign is CampaignMode campaign)
{
return Compare(campaign.Settings.WorldHostility, cachedHostilityValue, ComparisonOperator);
}
return false;
}
return false;
default:
bool equals = CheckOnlyEquality(target);
return ComparisonOperatorIsNotEquals
@@ -547,15 +573,13 @@ namespace Barotrauma
? matches <= 0
: matches >= AttributeValueAsTags.Length;
}
private bool ItemMatchesTagCondition(ISerializableEntity? target)
private bool CheckMatchingTags(Func<Identifier, bool> predicate)
{
if (target is not Item item) { return ComparisonOperatorIsNotEquals; }
int matches = 0;
foreach (var tag in AttributeValueAsTags)
foreach (Identifier tag in AttributeValueAsTags)
{
if (item.HasTag(tag)) { matches++; }
if (predicate(tag)) { matches++; }
}
return SufficientTagMatches(matches);
}
@@ -563,13 +587,7 @@ namespace Barotrauma
public bool TargetTagMatchesTagCondition(Identifier targetTag)
{
if (targetTag.IsEmpty || Type != ConditionType.HasTag) { return false; }
int matches = 0;
foreach (var tag in AttributeValueAsTags)
{
if (targetTag == tag) { matches++; }
}
return SufficientTagMatches(matches);
return CheckMatchingTags(targetTag.Equals);
}
private bool NumberMatchesRequirement(float testedValue)
@@ -660,5 +678,69 @@ namespace Barotrauma
_ => leftValue.CompareTo(rightValue) == 0,
};
}
/// <summary>
/// Seeks for child elements of name "conditional" and bundles them with an attribute of name "comparison".
/// </summary>
public static LogicalComparison? LoadConditionals(ContentXElement element, LogicalOperatorType defaultOperatorType = LogicalOperatorType.And)
{
var conditionalElements = element.GetChildElements("conditional");
if (conditionalElements.None()) { return default; }
List<PropertyConditional> conditionals = new();
foreach (ContentXElement subElement in conditionalElements)
{
conditionals.AddRange(FromXElement(subElement));
}
var logicalOperator = element.GetAttributeEnum("comparison", defaultOperatorType);
return new LogicalComparison(conditionals, logicalOperator);
}
public static bool CheckConditionals(ISerializableEntity conditionalTarget, IEnumerable<PropertyConditional> conditionals, LogicalOperatorType logicalOperator)
{
if (conditionals == null) { return true; }
if (conditionals.None()) { return true; }
switch (logicalOperator)
{
case LogicalOperatorType.And:
foreach (var conditional in conditionals)
{
if (!conditional.Matches(conditionalTarget))
{
// Some conditional didn't match.
return false;
}
}
// All conditionals matched.
return true;
case LogicalOperatorType.Or:
foreach (var conditional in conditionals)
{
if (conditional.Matches(conditionalTarget))
{
// Some conditional matched.
return true;
}
}
// None of the conditionals matched.
return false;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Bundles up a bunch of conditionals with a logical operator.
/// </summary>
public class LogicalComparison
{
public readonly ImmutableArray<PropertyConditional> Conditionals;
public readonly LogicalOperatorType LogicalOperator;
public LogicalComparison(IEnumerable<PropertyConditional> conditionals, LogicalOperatorType logicalOperator)
{
Conditionals = conditionals.ToImmutableArray();
LogicalOperator = logicalOperator;
}
}
}
}