v1.6.17.0 (Unto the Breach update)

This commit is contained in:
Regalis11
2024-10-22 17:29:04 +03:00
parent e74b3cdb17
commit 6e6c17e100
417 changed files with 17166 additions and 5870 deletions
@@ -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);
}
}
}