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
@@ -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);
}
}
}