#nullable enable using System.Collections.Generic; using System.Linq; namespace Barotrauma { class CountTargetsAction : BinaryOptionAction { [Serialize("", IsPropertySaveable.Yes)] public Identifier TargetTag { get; set; } [Serialize("", IsPropertySaveable.Yes, description: "Optional second tag. Can be used if the target must have two different tags.")] public Identifier SecondRequiredTargetTag { get; set; } [Serialize("", IsPropertySaveable.Yes, description: "Optional tag of a hull the target must be inside.")] public Identifier HullTag { get; set; } [Serialize(-1, IsPropertySaveable.Yes)] public int MinAmount { get; set; } [Serialize(-1, IsPropertySaveable.Yes)] public int MaxAmount { get; set; } [Serialize("", IsPropertySaveable.Yes)] public Identifier CompareToTarget { get; set; } [Serialize(-1.0f, IsPropertySaveable.Yes)] /// /// Minimum amount of targets, as a percentage of the number of entities tagged with CompareToTarget /// E.g. you could compare the number of entities tagged as "discoveredhull" to entities tagged as "anyhull" to require 50% of hulls to be discovered. /// public float MinPercentageRelativeToTarget { get; set; } [Serialize(-1.0f, IsPropertySaveable.Yes)] /// /// Maximum amount of targets, as a percentage of the number of entities tagged with CompareToTarget /// E.g. you could compare the number of entities tagged as "floodedhull" to entities tagged as "anyhull" to require less than 50% of hulls to be flooded. /// public float MaxPercentageRelativeToTarget { get; set; } private readonly IReadOnlyList conditionals; public CountTargetsAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { var conditionalList = new List(); foreach (ContentXElement subElement in element.GetChildElements("conditional")) { conditionalList.AddRange(PropertyConditional.FromXElement(subElement!)); } conditionals = conditionalList; if (CompareToTarget.IsEmpty) { int amount = element.GetAttributeInt("amount", -1); if (amount > -1) { MinAmount = MaxAmount = amount; } if (MinAmount > MaxAmount && MaxAmount > -1) { DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {MinAmount} is larger than {MaxAmount} in {nameof(CountTargetsAction)}."); } } else { if (MinPercentageRelativeToTarget < 0.0f && MaxPercentageRelativeToTarget < 0.0f) { DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". Comparing to another target, but neither {nameof(MinPercentageRelativeToTarget)} or {nameof(MaxPercentageRelativeToTarget)} is set."); } } } protected override bool? DetermineSuccess() { var potentialTargets = ParentEvent.GetTargets(TargetTag); if (!SecondRequiredTargetTag.IsEmpty) { potentialTargets = potentialTargets.Where(t => ParentEvent.GetTargets(SecondRequiredTargetTag).Contains(t)); } if (!HullTag.IsEmpty) { var hulls = ParentEvent.GetTargets(HullTag).OfType(); potentialTargets = potentialTargets.Where(t => (t is Item it && hulls.Contains(it.CurrentHull)) || (t is Character c && hulls.Contains(c.CurrentHull))); } if (conditionals.Any()) { potentialTargets = potentialTargets.Where(t => conditionals.Any(c => c.Matches(t as ISerializableEntity))); } int targetCount = potentialTargets.Count(); if (CompareToTarget.IsEmpty) { if (MinAmount > -1 && targetCount < MinAmount) { return false; } if (MaxAmount > -1 && targetCount > MaxAmount) { return false; } } else { int compareToTargetCount = ParentEvent.GetTargets(CompareToTarget).Count(); float percentage = MathUtils.Percentage(targetCount, compareToTargetCount); if (MinPercentageRelativeToTarget > -1 && percentage < MinPercentageRelativeToTarget) { return false; } if (MaxPercentageRelativeToTarget > -1 && percentage > MaxPercentageRelativeToTarget) { return false; } } return true; } public override string ToDebugString() { return $"{ToolBox.GetDebugSymbol(HasBeenDetermined())} {nameof(CountTargetsAction)} -> (TargetTag: {TargetTag.ColorizeObject()}, " + $"Succeeded: {succeeded.ColorizeObject()})"; } } }