v1.6.17.0 (Unto the Breach update)
This commit is contained in:
@@ -31,7 +31,12 @@ namespace Barotrauma
|
||||
{
|
||||
public static readonly List<DelayedListElement> DelayList = new List<DelayedListElement>();
|
||||
|
||||
private enum DelayTypes { Timer = 0, ReachCursor = 1 }
|
||||
private enum DelayTypes
|
||||
{
|
||||
Timer = 0,
|
||||
[Obsolete("The delay type is unsupported.")]
|
||||
ReachCursor = 1
|
||||
}
|
||||
|
||||
private readonly DelayTypes delayType;
|
||||
private readonly float delay;
|
||||
@@ -39,8 +44,12 @@ namespace Barotrauma
|
||||
public DelayedEffect(ContentXElement element, string parentDebugName)
|
||||
: base(element, parentDebugName)
|
||||
{
|
||||
DelayTypes delayTypeAttr = element.GetAttributeEnum("delaytype", DelayTypes.Timer);
|
||||
if (delayTypeAttr is DelayTypes.Timer)
|
||||
delayType = element.GetAttributeEnum("delaytype", DelayTypes.Timer);
|
||||
if (delayType == DelayTypes.ReachCursor)
|
||||
{
|
||||
DebugConsole.AddWarning($"Potential error in {parentDebugName}: the delay type {DelayTypes.ReachCursor} is not supported.", contentPackage: element.ContentPackage);
|
||||
}
|
||||
if (delayType is DelayTypes.Timer)
|
||||
{
|
||||
delay = element.GetAttributeFloat("delay", 1.0f);
|
||||
}
|
||||
@@ -75,9 +84,15 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectile.User == null)
|
||||
var user =
|
||||
projectile.User ??
|
||||
projectile.Attacker ??
|
||||
projectile.Launcher?.GetRootInventoryOwner() as Character;
|
||||
if (user == null)
|
||||
{
|
||||
DebugConsole.LogError("Projectile: '" + projectile.Name + "' missing user to determine distance");
|
||||
#if DEBUG
|
||||
DebugConsole.LogError($"Projectile \"{projectile.Item.Prefab.Identifier}\" missing user");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -112,7 +127,7 @@ namespace Barotrauma
|
||||
switch (delayType)
|
||||
{
|
||||
case DelayTypes.Timer:
|
||||
DelayList.Add(new DelayedListElement(this, entity, targets, delay, worldPosition, null));
|
||||
DelayList.Add(new DelayedListElement(this, entity, currentTargets, delay, worldPosition, null));
|
||||
break;
|
||||
case DelayTypes.ReachCursor:
|
||||
Projectile projectile = (entity as Item)?.GetComponent<Projectile>();
|
||||
@@ -124,15 +139,19 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (projectile.User == null)
|
||||
var user =
|
||||
projectile.User ??
|
||||
projectile.Attacker ??
|
||||
projectile.Launcher?.GetRootInventoryOwner() as Character;
|
||||
if (user == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.LogError("Projectile " + projectile.Name + "missing user");
|
||||
DebugConsole.LogError($"Projectile \"{projectile.Item.Prefab.Identifier}\" missing user");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
DelayList.Add(new DelayedListElement(this, entity, targets, Vector2.Distance(entity.WorldPosition, projectile.User.CursorWorldPosition), worldPosition, entity.WorldPosition));
|
||||
DelayList.Add(new DelayedListElement(this, entity, currentTargets, Vector2.Distance(entity.WorldPosition, user.CursorWorldPosition), worldPosition, entity.WorldPosition));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,11 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Last limb of the character the effect is being used on.
|
||||
/// </summary>
|
||||
LastLimb = 1024
|
||||
LastLimb = 1024,
|
||||
/// <summary>
|
||||
/// All entities (items, structures) this item is linked to. Only valid on items.
|
||||
/// </summary>
|
||||
LinkedEntities = 2048
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -210,9 +214,17 @@ namespace Barotrauma
|
||||
public readonly float Impulse;
|
||||
public readonly float RotationRad;
|
||||
/// <summary>
|
||||
/// How many items to spawn.
|
||||
/// Minimum number of items to spawn. Use "Count" to spawn a fixed number of items.
|
||||
/// </summary>
|
||||
public readonly int Count;
|
||||
public readonly int MinCount;
|
||||
/// <summary>
|
||||
/// Maximum number of items to spawn. Use "Count" to spawn a fixed number of items.
|
||||
/// </summary>
|
||||
public readonly int MaxCount;
|
||||
/// <summary>
|
||||
/// Probability of spawning the item(s). 0-1.
|
||||
/// </summary>
|
||||
public readonly float Probability;
|
||||
/// <summary>
|
||||
/// Random offset added to the spawn position in pixels.
|
||||
/// </summary>
|
||||
@@ -273,7 +285,16 @@ namespace Barotrauma
|
||||
Condition = MathHelper.Clamp(element.GetAttributeFloat("condition", 1.0f), 0.0f, 1.0f);
|
||||
|
||||
RotationRad = MathHelper.ToRadians(element.GetAttributeFloat("rotation", 0.0f));
|
||||
Count = element.GetAttributeInt("count", 1);
|
||||
|
||||
int fixedCount = element.GetAttributeInt("count", 1);
|
||||
MinCount = element.GetAttributeInt(nameof(MinCount), fixedCount);
|
||||
MaxCount = element.GetAttributeInt(nameof(MaxCount), fixedCount);
|
||||
if (MinCount > MaxCount)
|
||||
{
|
||||
DebugConsole.AddWarning($"Potential error in a StatusEffect {parentDebugName}: mincount is larger than maxcount.");
|
||||
}
|
||||
Probability = element.GetAttributeFloat(nameof(Probability), 1.0f);
|
||||
|
||||
Spread = element.GetAttributeFloat("spread", 0f);
|
||||
AimSpreadRad = MathHelper.ToRadians(element.GetAttributeFloat("aimspread", 0f));
|
||||
Equip = element.GetAttributeBool("equip", false);
|
||||
@@ -291,6 +312,11 @@ namespace Barotrauma
|
||||
}
|
||||
InheritEventTags = element.GetAttributeBool(nameof(InheritEventTags), false);
|
||||
}
|
||||
|
||||
public int GetCount(Rand.RandSync randSync)
|
||||
{
|
||||
return Rand.Range(MinCount, MaxCount + 1, randSync);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -357,6 +383,11 @@ namespace Barotrauma
|
||||
/// Meaning, the higher the skill level, the less the skill is increased.
|
||||
/// </summary>
|
||||
public readonly bool Proportional;
|
||||
/// <summary>
|
||||
/// Should the skill increase popup be always shown regardless of how much the skill increases?
|
||||
/// Normally it's only shown when the skill reaches the next integer value.
|
||||
/// </summary>
|
||||
public readonly bool AlwayShowNotification;
|
||||
|
||||
public GiveSkill(ContentXElement element, string parentDebugName)
|
||||
{
|
||||
@@ -365,6 +396,7 @@ namespace Barotrauma
|
||||
TriggerTalents = element.GetAttributeBool(nameof(TriggerTalents), true);
|
||||
UseDeltaTime = element.GetAttributeBool(nameof(UseDeltaTime), false);
|
||||
Proportional = element.GetAttributeBool(nameof(Proportional), false);
|
||||
AlwayShowNotification = element.GetAttributeBool(nameof(AlwayShowNotification), false);
|
||||
|
||||
if (SkillIdentifier == Identifier.Empty)
|
||||
{
|
||||
@@ -403,7 +435,7 @@ namespace Barotrauma
|
||||
public bool TransferInventory { get; private set; }
|
||||
|
||||
[Serialize(0, IsPropertySaveable.No, description:
|
||||
"The maximum number of creatures of the given species and team that can exist in the current level before this status effect stops spawning any more.")]
|
||||
"The maximum number of creatures of the given species and team that can exist per team in the current level before this status effect stops spawning any more.")]
|
||||
public int TotalMaxCount { get; private set; }
|
||||
|
||||
[Serialize(0, IsPropertySaveable.No, description: "Amount of stun to apply on the spawned character.")]
|
||||
@@ -437,6 +469,9 @@ namespace Barotrauma
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool InheritEventTags { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "Should the character team be inherited from the entity that owns the status effect?")]
|
||||
public bool InheritTeam { get; private set; }
|
||||
|
||||
public CharacterSpawnInfo(ContentXElement element, string parentDebugName)
|
||||
{
|
||||
@@ -606,6 +641,11 @@ namespace Barotrauma
|
||||
private readonly bool removeItem, dropContainedItems, dropItem, removeCharacter, breakLimb, hideLimb;
|
||||
private readonly float hideLimbTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Identifier of a container the character's items should be moved into when the character is removed. Only valid if the effect removes the character.
|
||||
/// </summary>
|
||||
private readonly Identifier containerForItemsOnCharacterRemoval;
|
||||
|
||||
public readonly ActionType type = ActionType.OnActive;
|
||||
|
||||
private readonly List<Explosion> explosions;
|
||||
@@ -622,6 +662,11 @@ namespace Barotrauma
|
||||
private readonly bool spawnItemRandomly;
|
||||
private readonly List<CharacterSpawnInfo> spawnCharacters;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, the effect removes all talents from the target and refunds the talent points.
|
||||
/// </summary>
|
||||
public readonly bool refundTalents;
|
||||
|
||||
public readonly List<GiveTalentInfo> giveTalentInfos;
|
||||
|
||||
private readonly List<AITrigger> aiTriggers;
|
||||
@@ -791,7 +836,7 @@ namespace Barotrauma
|
||||
|
||||
protected StatusEffect(ContentXElement element, string parentDebugName)
|
||||
{
|
||||
tags = new HashSet<Identifier>(element.GetAttributeString("tags", "").Split(',').ToIdentifiers());
|
||||
tags = new HashSet<Identifier>(element.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>()));
|
||||
OnlyInside = element.GetAttributeBool("onlyinside", false);
|
||||
OnlyOutside = element.GetAttributeBool("onlyoutside", false);
|
||||
OnlyWhenDamagedByPlayer = element.GetAttributeBool("onlyplayertriggered", element.GetAttributeBool("onlywhendamagedbyplayer", false));
|
||||
@@ -966,7 +1011,8 @@ namespace Barotrauma
|
||||
dropItem = true;
|
||||
break;
|
||||
case "removecharacter":
|
||||
removeCharacter = true;
|
||||
removeCharacter = true;
|
||||
containerForItemsOnCharacterRemoval = subElement.GetAttributeIdentifier("moveitemstocontainer", Identifier.Empty);
|
||||
break;
|
||||
case "breaklimb":
|
||||
breakLimb = true;
|
||||
@@ -986,6 +1032,7 @@ namespace Barotrauma
|
||||
}
|
||||
requiredItems.Add(newRequiredItem);
|
||||
break;
|
||||
case "requiredafflictions":
|
||||
case "requiredaffliction":
|
||||
requiredAfflictions ??= new HashSet<(Identifier, float)>();
|
||||
Identifier[] ids = subElement.GetAttributeIdentifierArray("identifier", null) ?? subElement.GetAttributeIdentifierArray("type", Array.Empty<Identifier>());
|
||||
@@ -1093,6 +1140,9 @@ namespace Barotrauma
|
||||
giveTalentInfos.Add(newGiveTalentInfo);
|
||||
}
|
||||
break;
|
||||
case "refundtalents":
|
||||
refundTalents = true;
|
||||
break;
|
||||
case "aitrigger":
|
||||
aiTriggers ??= new List<AITrigger>();
|
||||
aiTriggers.Add(new AITrigger(subElement));
|
||||
@@ -1150,9 +1200,9 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var (propertyName, value) in PropertyEffects)
|
||||
{
|
||||
if (propertyName == "condition" && value.GetType() == typeof(float))
|
||||
if (ChangesItemCondition(propertyName, value, out float conditionValue))
|
||||
{
|
||||
return (float)value < 0.0f || (setValue && (float)value <= 0.0f);
|
||||
return conditionValue < 0.0f || (setValue && conditionValue <= 0.0f);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1162,14 +1212,32 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var (propertyName, value) in PropertyEffects)
|
||||
{
|
||||
if (propertyName == "condition" && value.GetType() == typeof(float))
|
||||
if (ChangesItemCondition(propertyName, value, out float conditionValue))
|
||||
{
|
||||
return (float)value > 0.0f || (setValue && (float)value > 0.0f);
|
||||
return conditionValue > 0.0f || (setValue && conditionValue > 0.0f);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ChangesItemCondition(Identifier propertyName, object value, out float conditionValue)
|
||||
{
|
||||
if (propertyName == "condition")
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case float f:
|
||||
conditionValue = f;
|
||||
return true;
|
||||
case int i:
|
||||
conditionValue = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
conditionValue = 0.0f;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool MatchesTagConditionals(ItemPrefab itemPrefab)
|
||||
{
|
||||
if (itemPrefab == null || !HasConditions)
|
||||
@@ -1404,7 +1472,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (OnlyInside && itemComponent.Item.CurrentHull == null) { return false; }
|
||||
if (OnlyOutside && itemComponent.Item.CurrentHull != null) { return false; }
|
||||
if (!TargetItemComponent.IsNullOrEmpty() && itemComponent.Name != TargetItemComponent) { return false; }
|
||||
if (!TargetItemComponent.IsNullOrEmpty() && !itemComponent.Name.Equals(TargetItemComponent, StringComparison.OrdinalIgnoreCase)) { return false; }
|
||||
if (TargetIdentifiers == null) { return true; }
|
||||
if (TargetIdentifiers.Contains("itemcomponent")) { return true; }
|
||||
if (itemComponent.Item.HasTag(TargetIdentifiers)) { return true; }
|
||||
@@ -1676,15 +1744,8 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
var target = targets[i];
|
||||
if (target is Character character)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(character);
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(limb.character);
|
||||
}
|
||||
Character targetCharacter = GetCharacterFromTarget(targets[i]);
|
||||
if (targetCharacter != null) { RemoveCharacter(targetCharacter); }
|
||||
}
|
||||
}
|
||||
if (breakLimb || hideLimb)
|
||||
@@ -1875,7 +1936,7 @@ namespace Barotrauma
|
||||
|
||||
if (talentTriggers != null)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
Character targetCharacter = GetCharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
foreach (Identifier talentTrigger in talentTriggers)
|
||||
@@ -1894,7 +1955,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (int giveExperience in giveExperiences)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
Character targetCharacter = GetCharacterFromTarget(target);
|
||||
if (targetCharacter != null && !targetCharacter.Removed)
|
||||
{
|
||||
targetCharacter?.Info?.GiveExperience(giveExperience);
|
||||
@@ -1904,7 +1965,7 @@ namespace Barotrauma
|
||||
|
||||
if (giveSkills != null)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
Character targetCharacter = GetCharacterFromTarget(target);
|
||||
if (targetCharacter is { Removed: false })
|
||||
{
|
||||
foreach (GiveSkill giveSkill in giveSkills)
|
||||
@@ -1914,11 +1975,11 @@ namespace Barotrauma
|
||||
|
||||
if (giveSkill.Proportional)
|
||||
{
|
||||
targetCharacter.Info?.ApplySkillGain(skillIdentifier, amount, !giveSkill.TriggerTalents);
|
||||
targetCharacter.Info?.ApplySkillGain(skillIdentifier, amount, !giveSkill.TriggerTalents, forceNotification: giveSkill.AlwayShowNotification);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier, amount, !giveSkill.TriggerTalents);
|
||||
targetCharacter.Info?.IncreaseSkillLevel(skillIdentifier, amount, !giveSkill.TriggerTalents, forceNotification: giveSkill.AlwayShowNotification);
|
||||
}
|
||||
|
||||
Identifier GetRandomSkill()
|
||||
@@ -1929,9 +1990,17 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (refundTalents)
|
||||
{
|
||||
if (GetCharacterFromTarget(target) is { Removed: false } c)
|
||||
{
|
||||
c.Info?.AddRefundPoints(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (giveTalentInfos != null)
|
||||
{
|
||||
Character targetCharacter = CharacterFromTarget(target);
|
||||
Character targetCharacter = GetCharacterFromTarget(target);
|
||||
if (targetCharacter?.Info == null) { continue; }
|
||||
if (!TalentTree.JobTalentTrees.TryGet(targetCharacter.Info.Job.Prefab.Identifier, out TalentTree characterTalentTree)) { continue; }
|
||||
|
||||
@@ -2015,6 +2084,16 @@ namespace Barotrauma
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(characterSpawnInfo.SpeciesName, position + Rand.Vector(characterSpawnInfo.Spread, Rand.RandSync.Unsynced) + characterSpawnInfo.Offset,
|
||||
onSpawn: newCharacter =>
|
||||
{
|
||||
if (characterSpawnInfo.InheritTeam)
|
||||
{
|
||||
newCharacter.TeamID = entity switch
|
||||
{
|
||||
Character c => c.TeamID,
|
||||
Item it => it.GetRootInventoryOwner() is Character owner ? owner.TeamID : it.Submarine?.TeamID ?? newCharacter.TeamID,
|
||||
MapEntity e => e.Submarine?.TeamID ?? newCharacter.TeamID,
|
||||
_ => newCharacter.TeamID
|
||||
};
|
||||
}
|
||||
if (characterSpawnInfo.TotalMaxCount > 0)
|
||||
{
|
||||
if (Character.CharacterList.Count(c => c.SpeciesName == characterSpawnInfo.SpeciesName && c.TeamID == newCharacter.TeamID) > characterSpawnInfo.TotalMaxCount)
|
||||
@@ -2119,9 +2198,13 @@ namespace Barotrauma
|
||||
if (spawnItems.Count > 0)
|
||||
{
|
||||
var randomSpawn = spawnItems.GetRandomUnsynced();
|
||||
for (int i = 0; i < randomSpawn.Count; i++)
|
||||
int count = randomSpawn.GetCount(Rand.RandSync.Unsynced);
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Unsynced) < randomSpawn.Probability)
|
||||
{
|
||||
ProcessItemSpawnInfo(randomSpawn);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ProcessItemSpawnInfo(randomSpawn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2129,9 +2212,13 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (ItemSpawnInfo itemSpawnInfo in spawnItems)
|
||||
{
|
||||
for (int i = 0; i < itemSpawnInfo.Count; i++)
|
||||
int count = itemSpawnInfo.GetCount(Rand.RandSync.Unsynced);
|
||||
if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Unsynced) < itemSpawnInfo.Probability)
|
||||
{
|
||||
ProcessItemSpawnInfo(itemSpawnInfo);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ProcessItemSpawnInfo(itemSpawnInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2167,9 +2254,8 @@ namespace Barotrauma
|
||||
intervalTimers ??= new Dictionary<Entity, float>();
|
||||
intervalTimers[entity] = Interval;
|
||||
}
|
||||
|
||||
}
|
||||
private static Character CharacterFromTarget(ISerializableEntity target)
|
||||
private static Character GetCharacterFromTarget(ISerializableEntity target)
|
||||
{
|
||||
Character targetCharacter = target as Character;
|
||||
if (targetCharacter == null)
|
||||
@@ -2182,6 +2268,46 @@ namespace Barotrauma
|
||||
return targetCharacter;
|
||||
}
|
||||
|
||||
private void RemoveCharacter(Character character)
|
||||
{
|
||||
if (containerForItemsOnCharacterRemoval != Identifier.Empty)
|
||||
{
|
||||
ItemPrefab containerPrefab =
|
||||
ItemPrefab.Prefabs.Find(me => me.Tags.Contains(containerForItemsOnCharacterRemoval)) ??
|
||||
MapEntityPrefab.FindByIdentifier(containerForItemsOnCharacterRemoval) as ItemPrefab;
|
||||
|
||||
if (containerPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Could not spawn a container for a removed character's items. No item found with the identifier or tag \"{containerForItemsOnCharacterRemoval}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner?.AddItemToSpawnQueue(containerPrefab, character.WorldPosition, onSpawned: OnItemContainerSpawned);
|
||||
}
|
||||
|
||||
void OnItemContainerSpawned(Item item)
|
||||
{
|
||||
if (character.Inventory == null) { return; }
|
||||
|
||||
item.UpdateTransform();
|
||||
item.AddTag("name:" + character.Name);
|
||||
if (character.Info?.Job is { } job) { item.AddTag($"job:{job.Name}"); }
|
||||
|
||||
if (item.GetComponent<ItemContainer>() is not ItemContainer itemContainer) { return; }
|
||||
List<Item> inventoryItems = new List<Item>(character.Inventory.AllItemsMod);
|
||||
foreach (Item inventoryItem in inventoryItems)
|
||||
{
|
||||
if (!itemContainer.Inventory.TryPutItem(inventoryItem, user: null, createNetworkEvent: true))
|
||||
{
|
||||
//if the item couldn't be put inside the despawn container, just drop it
|
||||
inventoryItem.Drop(dropper: character, createNetworkEvent: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(character);
|
||||
}
|
||||
|
||||
void SpawnItem(ItemSpawnInfo chosenItemSpawnInfo, Entity entity, PhysicsBody sourceBody, Vector2 position, Entity targetEntity)
|
||||
{
|
||||
Item parentItem = entity as Item;
|
||||
@@ -2303,8 +2429,16 @@ namespace Barotrauma
|
||||
{
|
||||
ignoredBodies = user?.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList();
|
||||
}
|
||||
|
||||
float damageMultiplier = 1f;
|
||||
|
||||
if (sourceEntity is Limb attackLimb)
|
||||
{
|
||||
damageMultiplier = attackLimb.attack?.DamageMultiplier ?? 1.0f;
|
||||
}
|
||||
|
||||
projectile.Shoot(user, spawnPos, spawnPos, rotation,
|
||||
ignoredBodies: ignoredBodies, createNetworkEvent: true);
|
||||
ignoredBodies: ignoredBodies, createNetworkEvent: true, damageMultiplier: damageMultiplier);
|
||||
projectile.Item.Submarine = projectile.LaunchSub = sourceEntity?.Submarine;
|
||||
}
|
||||
else if (newItem.body != null)
|
||||
@@ -2439,7 +2573,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (animationsToTrigger == null) { return; }
|
||||
// Could probably use a similar pattern in other places above too, but refactoring statuseffects is very volatile.
|
||||
if ((CharacterFromTarget(target) ?? entity as Character) is Character targetCharacter)
|
||||
if ((GetCharacterFromTarget(target) ?? entity as Character) is Character targetCharacter)
|
||||
{
|
||||
foreach (AnimLoadInfo animLoadInfo in animationsToTrigger)
|
||||
{
|
||||
@@ -2695,7 +2829,7 @@ namespace Barotrauma
|
||||
public bool HasTag(Identifier tag)
|
||||
{
|
||||
if (tag == null) { return true; }
|
||||
return tags.Contains(tag) || tags.Contains(tag);
|
||||
return tags.Contains(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user