v1.4.4.1 (Blood in the Water Update)
This commit is contained in:
+2
-2
@@ -12,9 +12,9 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
|
||||
{
|
||||
if ((abilityObject as IAbilityAffliction)?.Affliction is Affliction affliction)
|
||||
if (abilityObject is IAbilityAffliction { Affliction: Affliction affliction })
|
||||
{
|
||||
return afflictions.Any(a => a == affliction.Identifier);
|
||||
return afflictions.Any(a => a == affliction.Identifier || a == affliction.Prefab.AfflictionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+13
-8
@@ -1,16 +1,15 @@
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionItemInSubmarine : AbilityConditionData
|
||||
[TypePreviouslyKnownAs("AbilityConditionItemInSubmarine")]
|
||||
class AbilityConditionInSubmarine : AbilityConditionData
|
||||
{
|
||||
private readonly SubmarineType? submarineType;
|
||||
|
||||
public AbilityConditionItemInSubmarine(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
public AbilityConditionInSubmarine(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
if (conditionElement.GetAttribute("submarinetype") != null)
|
||||
{
|
||||
submarineType = conditionElement.GetAttributeEnum<SubmarineType>("submarinetype", SubmarineType.Player);
|
||||
submarineType = conditionElement.GetAttributeEnum("submarinetype", SubmarineType.Player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +29,15 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAbilityConditionError(abilityObject, typeof(IAbilityItem));
|
||||
return false;
|
||||
return MatchesCondition();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool MatchesCondition()
|
||||
{
|
||||
if (character.Submarine is null) { return false; }
|
||||
|
||||
return character.Submarine?.Info?.Type == submarineType;
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-4
@@ -8,6 +8,7 @@ namespace Barotrauma.Abilities
|
||||
{
|
||||
private readonly Option<int> matchedLevel;
|
||||
private readonly Option<int> minLevel;
|
||||
private readonly Option<int> maxLevel;
|
||||
|
||||
public AbilityConditionHasLevel(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
@@ -18,23 +19,33 @@ namespace Barotrauma.Abilities
|
||||
minLevel = conditionElement.GetAttributeInt("minlevel", 0) is var min and not 0
|
||||
? Option<int>.Some(min)
|
||||
: Option<int>.None();
|
||||
|
||||
maxLevel = conditionElement.GetAttributeInt("maxlevel", 0) is var max and not 0
|
||||
? Option<int>.Some(max)
|
||||
: Option<int>.None();
|
||||
|
||||
if (matchedLevel.IsNone() && minLevel.IsNone())
|
||||
if (matchedLevel.IsNone() && minLevel.IsNone() && maxLevel.IsNone())
|
||||
{
|
||||
throw new Exception($"{nameof(AbilityConditionHasLevel)} must have either \"levelequals\" or \"minlevel\" attribute.");
|
||||
throw new Exception($"{nameof(AbilityConditionHasLevel)} must have either \"levelequals\", \"minlevel\" or \"maxlevel\" attribute.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
var currentLevel = character.Info.GetCurrentLevel();
|
||||
if (matchedLevel.TryUnwrap(out int match))
|
||||
{
|
||||
return character.Info.GetCurrentLevel() == match;
|
||||
return currentLevel == match;
|
||||
}
|
||||
|
||||
if (minLevel.TryUnwrap(out int min))
|
||||
{
|
||||
return character.Info.GetCurrentLevel() >= min;
|
||||
return currentLevel >= min;
|
||||
}
|
||||
|
||||
if (maxLevel.TryUnwrap(out int max))
|
||||
{
|
||||
return currentLevel <= max;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
+3
-1
@@ -12,7 +12,9 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
return character.IsRagdolled || character.Stun > 0f || character.IsIncapacitated;
|
||||
// TODO: Should we only check whether the target is ragdolling here?
|
||||
// Or should we use character.IsKnockedDown instead?
|
||||
return (character.IsRagdolled && !character.AnimController.IsHangingWithRope) || character.Stun > 0f || character.IsIncapacitated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -100,7 +100,7 @@ namespace Barotrauma.Abilities
|
||||
string type = abilityElement.Name.ToString().ToLowerInvariant();
|
||||
try
|
||||
{
|
||||
abilityType = Type.GetType("Barotrauma.Abilities." + type + "", false, true);
|
||||
abilityType = ReflectionUtils.GetTypeWithBackwardsCompatibility("Barotrauma.Abilities", type, false, true);
|
||||
if (abilityType == null)
|
||||
{
|
||||
if (errorMessages) DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")",
|
||||
|
||||
+31
-13
@@ -21,24 +21,42 @@
|
||||
}
|
||||
}
|
||||
|
||||
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
||||
{
|
||||
if (conditionsMatched)
|
||||
{
|
||||
ApplyEffect();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
ApplyAfflictionToCharacter(Character);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if (abilityObject is IAbilityCharacter character)
|
||||
{
|
||||
var afflictionPrefab = AfflictionPrefab.Prefabs.Find(a => a.Identifier == afflictionId);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in CharacterAbilityGiveAffliction - could not find an affliction with the identifier \"{afflictionId}\".",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
float strength = this.strength;
|
||||
if (!string.IsNullOrEmpty(multiplyStrengthBySkill))
|
||||
{
|
||||
strength *= Character.GetSkillLevel(multiplyStrengthBySkill);
|
||||
}
|
||||
character.Character.CharacterHealth.ApplyAffliction(null, afflictionPrefab.Instantiate(strength), allowStacking: !setValue);
|
||||
ApplyAfflictionToCharacter(character.Character);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyAfflictionToCharacter(Character character)
|
||||
{
|
||||
var afflictionPrefab = AfflictionPrefab.Prefabs.Find(a => a.Identifier == afflictionId);
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in CharacterAbilityGiveAffliction - could not find an affliction with the identifier \"{afflictionId}\".",
|
||||
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
||||
return;
|
||||
}
|
||||
float strength = this.strength;
|
||||
if (!string.IsNullOrEmpty(multiplyStrengthBySkill))
|
||||
{
|
||||
strength *= Character.GetSkillLevel(multiplyStrengthBySkill);
|
||||
}
|
||||
character.CharacterHealth.ApplyAffliction(null, afflictionPrefab.Instantiate(strength), allowStacking: !setValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -15,12 +15,13 @@
|
||||
DebugConsole.ThrowError("Error in CharacterAbilityGiveResistance - resistance identifier not set.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
|
||||
// NOTE: The resistance value is a multiplier here, so 1.0 == 0% resistance
|
||||
if (MathUtils.NearlyEqual(multiplier, 1))
|
||||
{
|
||||
DebugConsole.AddWarning($"Possible error in talent {CharacterTalent.DebugIdentifier} - multiplier set to 1, which will do nothing.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void InitializeAbility(bool addingFirstTime)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ namespace Barotrauma.Abilities
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
if (abilityObject is not IAbilityCharacter character) { return; }
|
||||
character.Character.CharacterHealth.ReduceAfflictionOnAllLimbs(afflictionId, amount);
|
||||
character.Character.CharacterHealth.ReduceAfflictionOnAllLimbs(afflictionId, amount, attacker: Character);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
#nullable enable
|
||||
namespace Barotrauma.Abilities;
|
||||
|
||||
internal class CharacterAbilityUpgradeSubmarine : CharacterAbility
|
||||
{
|
||||
private readonly UpgradePrefab? upgradePrefab;
|
||||
private readonly UpgradeCategory? upgradeCategory;
|
||||
public readonly int level;
|
||||
|
||||
public override bool AllowClientSimulation => true;
|
||||
|
||||
public CharacterAbilityUpgradeSubmarine(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
var prefabIdentifier = abilityElement.GetAttributeIdentifier(nameof(upgradePrefab), Identifier.Empty);
|
||||
var categoryIdentifier = abilityElement.GetAttributeIdentifier(nameof(upgradeCategory), Identifier.Empty);
|
||||
|
||||
if (UpgradePrefab.Find(prefabIdentifier) is not { } foundUpgradePrefab)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityUpgradeSubmarine)} - {nameof(upgradePrefab)} not found.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
upgradePrefab = foundUpgradePrefab;
|
||||
}
|
||||
|
||||
if (UpgradeCategory.Find(categoryIdentifier) is not { } foundUpgradeCategory)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}, {nameof(CharacterAbilityUpgradeSubmarine)} - {nameof(upgradeCategory)} not found.",
|
||||
contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
else
|
||||
{
|
||||
upgradeCategory = foundUpgradeCategory;
|
||||
}
|
||||
|
||||
level = abilityElement.GetAttributeInt(nameof(level), 1);
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
{
|
||||
ApplyEffectSpecific();
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
ApplyEffectSpecific();
|
||||
}
|
||||
|
||||
private void ApplyEffectSpecific()
|
||||
{
|
||||
if (upgradePrefab == null || upgradeCategory == null) { return; }
|
||||
if (GameMain.GameSession?.Campaign?.UpgradeManager is not { } upgradeManager) { return; }
|
||||
|
||||
upgradeManager.AddUpgradeExternally(upgradePrefab, upgradeCategory, level);
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
namespace Barotrauma.Abilities;
|
||||
|
||||
/// <summary>
|
||||
/// Hardcoded ability for the "War Stories" talent.
|
||||
/// Spawns an item and sets the health multiplier to the target stat value.
|
||||
///
|
||||
/// The item spawned should have a default health of 1 because we set the multiplier.
|
||||
/// This is because we already had existing Item.HealthMultiplier that gets synced and
|
||||
/// everything but not one for setting the max health directly to some value and I didn't
|
||||
/// want to add a new one just for this.
|
||||
/// </summary>
|
||||
internal class CharacterAbilityWarStories : CharacterAbility
|
||||
{
|
||||
private readonly Identifier targetStat;
|
||||
private readonly float minCondition;
|
||||
|
||||
private readonly ItemPrefab prefab;
|
||||
|
||||
public CharacterAbilityWarStories(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
|
||||
{
|
||||
targetStat = abilityElement.GetAttributeIdentifier("target", Identifier.Empty);
|
||||
minCondition = abilityElement.GetAttributeFloat("mincondition", 1);
|
||||
|
||||
if (targetStat.IsEmpty)
|
||||
{
|
||||
DebugConsole.ThrowError($"{nameof(CharacterAbilityWarStories)}: target stat is not defined", contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
|
||||
Identifier spawnedItem = abilityElement.GetAttributeIdentifier("item", Identifier.Empty);
|
||||
if (!ItemPrefab.Prefabs.TryGet(spawnedItem, out prefab))
|
||||
{
|
||||
DebugConsole.ThrowError($"{nameof(CharacterAbilityWarStories)}: spawned item \"{spawnedItem}\" could not be found.", contentPackage: abilityElement.ContentPackage);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
if (prefab is null || Character is null) { return; }
|
||||
|
||||
float condition = Character.Info?.GetSavedStatValue(StatTypes.None, targetStat) ?? 0;
|
||||
if (condition < minCondition) { return; }
|
||||
|
||||
if (GameMain.GameSession?.RoundEnding ?? true)
|
||||
{
|
||||
Item item = new(prefab, Character.WorldPosition, Character.Submarine)
|
||||
{
|
||||
Condition = condition,
|
||||
HealthMultiplier = condition
|
||||
};
|
||||
Character.Inventory.TryPutItem(item, Character, item.AllowedSlots);
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, Character.Inventory, condition: condition, onSpawned: item =>
|
||||
{
|
||||
item.HealthMultiplier = condition;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyEffect(AbilityObject abilityObject)
|
||||
=> ApplyEffect();
|
||||
}
|
||||
+1
-1
@@ -140,7 +140,7 @@ namespace Barotrauma.Abilities
|
||||
string type = conditionElement.Name.ToString().ToLowerInvariant();
|
||||
try
|
||||
{
|
||||
conditionType = Type.GetType("Barotrauma.Abilities." + type + "", false, true);
|
||||
conditionType = ReflectionUtils.GetTypeWithBackwardsCompatibility("Barotrauma.Abilities", type, false, true);
|
||||
if (conditionType == null)
|
||||
{
|
||||
if (errorMessages)
|
||||
|
||||
@@ -22,6 +22,12 @@ namespace Barotrauma
|
||||
|
||||
public readonly Sprite Icon;
|
||||
|
||||
/// <summary>
|
||||
/// When set to a value the talent tooltip will display a text showing the current value of the stat and the max value.
|
||||
/// For example "Progress: 37/100".
|
||||
/// </summary>
|
||||
public readonly Option<(Identifier PermanentStatIdentifier, int Max)> TrackedStat;
|
||||
|
||||
#if CLIENT
|
||||
public readonly Option<Color> ColorOverride;
|
||||
#endif
|
||||
@@ -44,6 +50,12 @@ namespace Barotrauma
|
||||
|
||||
AbilityEffectsStackWithSameTalent = element.GetAttributeBool("abilityeffectsstackwithsametalent", true);
|
||||
|
||||
var trackedStat = element.GetAttributeIdentifier("trackedstat", Identifier.Empty);
|
||||
var trackedMax = element.GetAttributeInt("trackedmax", 100);
|
||||
TrackedStat = !trackedStat.IsEmpty
|
||||
? Option.Some((trackedStat, trackedMax))
|
||||
: Option.None;
|
||||
|
||||
Identifier nameIdentifier = element.GetAttributeIdentifier("nameidentifier", Identifier.Empty);
|
||||
if (!nameIdentifier.IsEmpty)
|
||||
{
|
||||
|
||||
@@ -82,18 +82,17 @@ namespace Barotrauma
|
||||
TalentSubTree subTree = talentTree!.TalentSubTrees.FirstOrDefault(tst => tst.Identifier == subTreeIdentifier);
|
||||
if (subTree is null) { return TalentStages.Invalid; }
|
||||
|
||||
if (!TalentTreeMeetsRequirements(talentTree, subTree, selectedTalents))
|
||||
{
|
||||
return TalentStages.Locked;
|
||||
}
|
||||
|
||||
TalentOption targetTalentOption = subTree.TalentOptionStages[index];
|
||||
|
||||
if (targetTalentOption.HasEnoughTalents(character.Info))
|
||||
{
|
||||
return TalentStages.Unlocked;
|
||||
}
|
||||
|
||||
if (!TalentTreeMeetsRequirements(talentTree, subTree, selectedTalents))
|
||||
{
|
||||
return TalentStages.Locked;
|
||||
}
|
||||
|
||||
if (targetTalentOption.HasSelectedTalent(selectedTalents))
|
||||
{
|
||||
return TalentStages.Highlighted;
|
||||
|
||||
Reference in New Issue
Block a user