0.1500.3.0 (🗿 edition)

This commit is contained in:
Markus Isberg
2021-09-17 22:47:21 +09:00
parent 1231170fce
commit 5a6bbcc79e
75 changed files with 1145 additions and 441 deletions
@@ -161,8 +161,8 @@ namespace Barotrauma
for (int i = 0; i < container.Inventory.Capacity; i++)
{
if (container.Inventory.GetItemAt(i) != null) { continue; }
if (MapEntityPrefab.List.GetRandom(e => e is ItemPrefab i && container.CanBeContained(i) &&
Config.ForbiddenAmmunition.None(id => id.Equals(i.Identifier, StringComparison.OrdinalIgnoreCase)), Rand.RandSync.Server) is ItemPrefab ammoPrefab)
if (MapEntityPrefab.List.GetRandom(e => e is ItemPrefab ip && container.CanBeContained(ip, i) &&
Config.ForbiddenAmmunition.None(id => id.Equals(ip.Identifier, StringComparison.OrdinalIgnoreCase)), Rand.RandSync.Server) is ItemPrefab ammoPrefab)
{
Item ammo = new Item(ammoPrefab, container.Item.WorldPosition, Wreck);
if (!container.Inventory.TryPutItem(ammo, i, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: false))
@@ -1430,6 +1430,7 @@ namespace Barotrauma
//throwing conscious/moving characters around takes more force -> double the flow force
if (character.CanMove) { flowForce *= 2.0f; }
flowForce *= 1 - Math.Clamp(character.GetStatValue(StatTypes.FlowResistance), 0f, 1f);
float flowForceMagnitude = flowForce.Length();
float limbMultipier = limbs.Count(l => l.inWater) / (float)limbs.Length;
@@ -251,6 +251,8 @@ namespace Barotrauma
private readonly List<Attacker> lastAttackers = new List<Attacker>();
public IEnumerable<Attacker> LastAttackers => lastAttackers;
public Character LastAttacker => lastAttackers.LastOrDefault()?.Character;
public Character LastOrderedCharacter { get; private set; }
public Character SecondLastOrderedCharacter { get; private set; }
public Entity LastDamageSource;
@@ -2720,7 +2722,7 @@ namespace Barotrauma
//Do ragdoll shenanigans before Stun because it's still technically a stun, innit? Less network updates for us!
bool allowRagdoll = GameMain.NetworkMember?.ServerSettings?.AllowRagdollButton ?? true;
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 5.0f * 5.0f;
bool tooFastToUnragdoll = AnimController.Collider.LinearVelocity.LengthSquared() > 2.5f * 2.5f;
bool wasRagdolled = false;
bool selfRagdolled = false;
@@ -3131,6 +3133,12 @@ namespace Barotrauma
{
var abilityOrderedCharacter = new AbilityCharacter(this);
orderGiver.CheckTalents(AbilityEffectType.OnGiveOrder, abilityOrderedCharacter);
if (orderGiver.LastOrderedCharacter != this)
{
orderGiver.SecondLastOrderedCharacter = orderGiver.LastOrderedCharacter;
orderGiver.LastOrderedCharacter = this;
}
}
if (AIController is HumanAIController humanAI)
@@ -3406,6 +3414,13 @@ namespace Barotrauma
AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) :
DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration);
if (attacker != null)
{
var abilityAttackResult = new AbilityAttackResult(attackResult);
attacker.CheckTalents(AbilityEffectType.OnAttackResult, abilityAttackResult);
CheckTalents(AbilityEffectType.OnAttackedResult, abilityAttackResult);
}
if (limbHit == null) { return new AttackResult(); }
Vector2 forceWorld = attack.TargetImpulseWorld + attack.TargetForceWorld;
if (attacker != null)
@@ -3623,11 +3638,6 @@ namespace Barotrauma
ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
hitLimb.ApplyStatusEffects(ActionType.OnDamaged, 1.0f);
}
if (attacker != null)
{
var abilityAttackResult = new AbilityAttackResult(attackResult);
attacker.CheckTalents(AbilityEffectType.OnAttackResult, abilityAttackResult);
}
return attackResult;
}
@@ -979,6 +979,8 @@ namespace Barotrauma
increase *= SkillSettings.Current.AssistantSkillIncreaseMultiplier;
}
increase *= 1f + Character.GetStatValue(StatTypes.SkillGainSpeed);
float prevLevel = Job.GetSkillLevel(skillIdentifier);
Job.IncreaseSkillLevel(skillIdentifier, increase, Character.HasAbilityFlag(AbilityFlags.GainSkillPastMaximum));
@@ -1527,6 +1529,17 @@ namespace Barotrauma
return 0f;
}
}
public float GetSavedStatValue(StatTypes statType, string statIdentifier)
{
if (savedStatValues.TryGetValue(statType, out var statValues))
{
return statValues.Where(s => s.StatIdentifier.Equals(statIdentifier, StringComparison.OrdinalIgnoreCase)).Sum(v => v.StatValue);
}
else
{
return 0f;
}
}
public void ChangeSavedStatValue(StatTypes statType, float value, string statIdentifier, bool removeOnDeath, bool removeAfterRound = false, float maxValue = float.MaxValue)
{
@@ -971,7 +971,7 @@ namespace Barotrauma
/// <param name="treatmentSuitability">A dictionary where the key is the identifier of the item and the value the suitability</param>
/// <param name="normalize">If true, the suitability values are normalized between 0 and 1. If not, they're arbitrary values defined in the medical item XML, where negative values are unsuitable, and positive ones suitable.</param>
/// <param name="randomization">Amount of randomization to apply to the values (0 = the values are accurate, 1 = the values are completely random)</param>
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, float randomization = 0.0f)
public void GetSuitableTreatments(Dictionary<string, float> treatmentSuitability, bool normalize, Limb limb = null, bool ignoreHiddenAfflictions = false, float randomization = 0.0f)
{
//key = item identifier
//float = suitability
@@ -980,6 +980,7 @@ namespace Barotrauma
foreach (Affliction affliction in getAfflictions(limb))
{
if (affliction.Strength < affliction.Prefab.TreatmentThreshold) { continue; }
if (ignoreHiddenAfflictions && affliction.Strength < affliction.Prefab.ShowIconThreshold) { continue; }
foreach (KeyValuePair<string, float> treatment in affliction.Prefab.TreatmentSuitability)
{
if (!treatmentSuitability.ContainsKey(treatment.Key))
@@ -7,12 +7,12 @@ namespace Barotrauma.Abilities
{
private enum WeaponType
{
Any = 0,
Melee = 1,
Any = 0,
Melee = 1,
Ranged = 2
};
private WeaponType weapontype;
private readonly WeaponType weapontype;
public AbilityConditionIsAiming(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
switch (conditionElement.GetAttributeString("weapontype", ""))
@@ -43,7 +43,7 @@ namespace Barotrauma.Abilities
break;
default:
aimingCorrectItem |= animController.IsAiming || animController.IsAimingMelee;
break;
break;
}
}
}
@@ -6,12 +6,12 @@ namespace Barotrauma.Abilities
{
class AbilityConditionItem : AbilityConditionData
{
private readonly string identifier;
private readonly string[] identifiers;
private readonly string[] tags;
public AbilityConditionItem(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
identifier = conditionElement.GetAttributeString("identifier", string.Empty).ToLowerInvariant();
identifiers = conditionElement.GetAttributeStringArray("identifiers", Array.Empty<string>(), convertToLowerInvariant: true);
tags = conditionElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
}
@@ -29,15 +29,15 @@ namespace Barotrauma.Abilities
if (itemPrefab != null)
{
if (!string.IsNullOrEmpty(identifier))
if (identifiers.Any())
{
if (itemPrefab.Identifier != identifier)
if (!identifiers.Any(t => itemPrefab.Identifier == t))
{
return false;
}
}
return tags.Any(t => itemPrefab.Tags.Any(p => t == p));
return !tags.Any() || tags.Any(t => itemPrefab.Tags.Any(p => t == p));
}
else
{
@@ -0,0 +1,20 @@
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class AbilityConditionHasVelocity : AbilityConditionDataless
{
private readonly float velocity;
public AbilityConditionHasVelocity(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement)
{
velocity = conditionElement.GetAttributeFloat("velocity", 0f);
}
protected override bool MatchesConditionSpecific()
{
return character.AnimController.Collider.LinearVelocity.LengthSquared() > velocity * velocity;
}
}
}
@@ -13,7 +13,7 @@ namespace Barotrauma.Abilities
protected override bool MatchesConditionSpecific()
{
if (character.Submarine == null || character.Submarine.TeamID != character.TeamID) { return false; }
if (!character.IsInFriendlySub) { return false; }
float currentFloodPercentage = character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
return currentFloodPercentage / 100 > floodPercentage;
}
@@ -10,6 +10,7 @@ namespace Barotrauma.Abilities
protected readonly List<StatusEffect> statusEffects;
private readonly bool nearbyCharactersAppliesToSelf;
private readonly bool applyToSelected;
readonly List<ISerializableEntity> targets = new List<ISerializableEntity>();
@@ -18,6 +19,7 @@ namespace Barotrauma.Abilities
{
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
applyToSelected = abilityElement.GetAttributeBool("applytoselected", false);
nearbyCharactersAppliesToSelf = abilityElement.GetAttributeBool("nearbycharactersappliestoself", true);
}
protected void ApplyEffectSpecific(Character targetCharacter)
@@ -26,7 +28,7 @@ namespace Barotrauma.Abilities
{
if (statusEffect.HasTargetType(StatusEffect.TargetType.UseTarget))
{
// currently used this to spawn items on the targeted character
// currently used to spawn items on the targeted character
statusEffect.SetUser(targetCharacter);
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targetCharacter);
}
@@ -34,6 +36,10 @@ namespace Barotrauma.Abilities
{
targets.Clear();
targets.AddRange(statusEffect.GetNearbyTargets(targetCharacter.WorldPosition, targets));
if (!nearbyCharactersAppliesToSelf)
{
targets.RemoveAll(c => c == Character);
}
statusEffect.SetUser(Character);
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets);
}
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityApplyStatusEffectsToLastOrderedCharacter : CharacterAbilityApplyStatusEffects
{
public CharacterAbilityApplyStatusEffectsToLastOrderedCharacter(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
}
protected override void ApplyEffect()
{
if (IsViableTarget(Character.LastOrderedCharacter))
{
ApplyEffectSpecific(Character.LastOrderedCharacter);
}
if (Character.HasAbilityFlag(AbilityFlags.AllowSecondOrderedTarget) && IsViableTarget(Character.SecondLastOrderedCharacter))
{
ApplyEffectSpecific(Character.SecondLastOrderedCharacter);
}
}
private bool IsViableTarget(Character targetCharacter)
{
if (targetCharacter == null || targetCharacter.Removed) { return false; }
if (targetCharacter == Character) { return false; }
return true;
}
}
}
@@ -27,8 +27,7 @@ namespace Barotrauma.Abilities
targetAllies = abilityElement.GetAttributeBool("targetallies", false);
removeOnDeath = abilityElement.GetAttributeBool("removeondeath", true);
removeAfterRound = abilityElement.GetAttributeBool("removeafterround", false);
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", false);
//maximumValue = abilityElement.GetAttributeFloat("maximumvalue", float.MaxValue);
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
}
public override void InitializeAbility(bool addingFirstTime)
@@ -1,4 +1,5 @@
using Microsoft.Xna.Framework;
using Barotrauma.Extensions;
using Microsoft.Xna.Framework;
using System.Xml.Linq;
namespace Barotrauma.Abilities
@@ -7,13 +8,22 @@ namespace Barotrauma.Abilities
{
public override bool AppliesEffectOnIntervalUpdate => true;
private string skillIdentifier;
private float skillIncrease;
private readonly string skillIdentifier;
private readonly float skillIncrease;
public CharacterAbilityIncreaseSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
skillIdentifier = abilityElement.GetAttributeString("skillidentifier", "").ToLowerInvariant();
skillIncrease = abilityElement.GetAttributeFloat("skillincrease", 0f);
if (string.IsNullOrEmpty(skillIdentifier))
{
DebugConsole.ThrowError($"Error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill identifier not defined in CharacterAbilityIncreaseSkill.");
}
if (MathUtils.NearlyEqual(skillIncrease, 0))
{
DebugConsole.AddWarning($"Possible error in talent \"{characterAbilityGroup.CharacterTalent.DebugIdentifier}\" - skill increase set to 0.");
}
}
protected override void ApplyEffect()
@@ -35,7 +45,17 @@ namespace Barotrauma.Abilities
private void ApplyEffectSpecific(Character character)
{
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
if (skillIdentifier.Equals("random"))
{
var skill = character.Info?.Job?.Skills?.GetRandom();
if (skill == null) { return; }
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
}
else
{
character.Info?.IncreaseSkillLevel(skillIdentifier, skillIncrease, character.Position + Vector2.UnitY * 175.0f);
}
}
}
}
@@ -0,0 +1,34 @@
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityModifyStatToFlooding : CharacterAbility
{
private readonly StatTypes statType;
private readonly float maxValue;
private float lastValue = 0f;
public CharacterAbilityModifyStatToFlooding(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
statType = CharacterAbilityGroup.ParseStatType(abilityElement.GetAttributeString("stattype", ""), CharacterTalent.DebugIdentifier);
maxValue = abilityElement.GetAttributeFloat("maxvalue", 0f);
}
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
{
Character.ChangeStat(statType, -lastValue);
if (conditionsMatched && Character.IsInFriendlySub)
{
float currentFloodPercentage = Character.Submarine.GetHulls(false).Average(h => h.WaterPercentage);
lastValue = currentFloodPercentage / 100f * maxValue;
Character.ChangeStat(statType, lastValue);
}
else
{
lastValue = 0f;
}
}
}
}
@@ -4,8 +4,8 @@ namespace Barotrauma.Abilities
{
class CharacterAbilityModifyValue : CharacterAbility
{
private float addedValue;
private float multiplyValue;
private readonly float addedValue;
private readonly float multiplyValue;
public CharacterAbilityModifyValue(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
@@ -6,12 +6,14 @@ namespace Barotrauma.Abilities
{
class CharacterAbilityByTheBook : CharacterAbility
{
private int moneyAmount;
private int max;
private readonly int moneyAmount;
private readonly int experienceAmount;
private readonly int max;
public CharacterAbilityByTheBook(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
moneyAmount = abilityElement.GetAttributeInt("moneyamount", 0);
experienceAmount = abilityElement.GetAttributeInt("experienceamount", 0);
max = abilityElement.GetAttributeInt("max", 0);
}
@@ -28,6 +30,10 @@ namespace Barotrauma.Abilities
if (!enemyCharacter.LockHands) { continue; }
if (timesGiven > max) { continue; }
Character.GiveMoney(moneyAmount);
foreach (Character character in Character.GetFriendlyCrew(Character))
{
character.Info?.GiveExperience(experienceAmount);
}
timesGiven++;
}
@@ -0,0 +1,43 @@
using System;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityEnigmaMachine : CharacterAbility
{
private readonly float addedValue;
private readonly float multiplyValue;
private readonly string[] tags;
private readonly int maxMultiplyCount;
public CharacterAbilityEnigmaMachine(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
addedValue = abilityElement.GetAttributeFloat("addedvalue", 0f);
multiplyValue = abilityElement.GetAttributeFloat("multiplyvalue", 1f);
tags = abilityElement.GetAttributeStringArray("tags", Array.Empty<string>(), convertToLowerInvariant: true);
maxMultiplyCount = abilityElement.GetAttributeInt("maxmultiplycount", int.MaxValue);
}
protected override void ApplyEffect(AbilityObject abilityObject)
{
if (abilityObject is IAbilityValue abilityValue)
{
int multiplyCount = 0;
foreach (Item item in Item.ItemList)
{
if (item.Prefab.Tags.Any(t => tags.Contains(t)))
{
multiplyCount++;
if (multiplyCount == maxMultiplyCount)
{
break;
}
}
}
abilityValue.Value += addedValue * multiplyCount;
}
}
}
}
@@ -1,30 +0,0 @@
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityIndustrialRevolution : CharacterAbility
{
float addedFabricationSpeed;
public CharacterAbilityIndustrialRevolution(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
addedFabricationSpeed = abilityElement.GetAttributeFloat("addedfabricationspeed", 0f);
}
public override void UpdateCharacterAbility(bool conditionsMatched, float timeSinceLastUpdate)
{
if (conditionsMatched)
{
// not necessarily the cleanest or performant way, but at least this shouldn't break anything.
// must be done every frame in order to work.
if (Character.SelectedConstruction?.GetComponent<Fabricator>() is Fabricator fabricator && fabricator.IsActive)
{
fabricator.FabricationSpeedMultiplier += addedFabricationSpeed;
}
}
}
}
}
@@ -1,39 +0,0 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class CharacterAbilityTaskmaster : CharacterAbility
{
private readonly List<StatusEffect> statusEffects;
private readonly List<StatusEffect> statusEffectsRemove;
private Character lastCharacter;
public CharacterAbilityTaskmaster(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
statusEffects = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffects"));
statusEffectsRemove = CharacterAbilityGroup.ParseStatusEffects(CharacterTalent, abilityElement.GetChildElement("statuseffectsremove"));
}
protected override void ApplyEffect(AbilityObject abilityObject)
{
if ((abilityObject as IAbilityCharacter)?.Character is Character targetCharacter)
{
if (targetCharacter == Character) { return; }
foreach (var statusEffect in statusEffectsRemove)
{
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, lastCharacter);
}
foreach (var statusEffect in statusEffects)
{
statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, targetCharacter);
}
lastCharacter = targetCharacter;
}
}
}
}
@@ -14,6 +14,8 @@ namespace Barotrauma.Abilities
// currently only used to turn off simulation if random conditions are in use
public bool IsActive { get; private set; } = true;
public readonly AbilityEffectType AbilityEffectType;
protected int maxTriggerCount { get; }
protected int timesTriggered = 0;
@@ -24,8 +26,9 @@ namespace Barotrauma.Abilities
// separate dictionaries for each type of characterability?
protected readonly List<CharacterAbility> characterAbilities = new List<CharacterAbility>();
public CharacterAbilityGroup(CharacterTalent characterTalent, XElement abilityElementGroup)
public CharacterAbilityGroup(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup)
{
AbilityEffectType = abilityEffectType;
CharacterTalent = characterTalent;
Character = CharacterTalent.Character;
maxTriggerCount = abilityElementGroup.GetAttributeInt("maxtriggercount", int.MaxValue);
@@ -168,8 +171,7 @@ namespace Barotrauma.Abilities
public static StatTypes ParseStatType(string statTypeString, string debugIdentifier)
{
StatTypes statType;
if (!Enum.TryParse(statTypeString, true, out statType))
if (!Enum.TryParse(statTypeString, true, out StatTypes statType))
{
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
}
@@ -8,7 +8,8 @@ namespace Barotrauma.Abilities
{
class CharacterAbilityGroupEffect : CharacterAbilityGroup
{
public CharacterAbilityGroupEffect(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup) { }
public CharacterAbilityGroupEffect(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup) :
base(abilityEffectType, characterTalent, abilityElementGroup) { }
public void CheckAbilityGroup(AbilityObject abilityObject)
{
@@ -15,7 +15,8 @@ namespace Barotrauma.Abilities
private float effectDelayTimer;
public CharacterAbilityGroupInterval(CharacterTalent characterTalent, XElement abilityElementGroup) : base(characterTalent, abilityElementGroup)
public CharacterAbilityGroupInterval(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, XElement abilityElementGroup) :
base(abilityEffectType, characterTalent, abilityElementGroup)
{
// too many overlapping intervals could cause hitching? maybe randomize a little
interval = abilityElementGroup.GetAttributeFloat("interval", 0f);
@@ -85,14 +85,13 @@ namespace Barotrauma
// XML logic
private void LoadAbilityGroupInterval(XElement abilityGroup)
{
string name = abilityGroup.Name.ToString().ToLowerInvariant();
characterAbilityGroupIntervals.Add(new CharacterAbilityGroupInterval(this, abilityGroup));
characterAbilityGroupIntervals.Add(new CharacterAbilityGroupInterval(AbilityEffectType.Undefined, this, abilityGroup));
}
private void LoadAbilityGroupEffect(XElement abilityGroup)
{
AbilityEffectType abilityEffectType = ParseAbilityEffectType(this, abilityGroup.GetAttributeString("abilityeffecttype", "none"));
AddAbilityGroupEffect(new CharacterAbilityGroupEffect(this, abilityGroup), abilityEffectType);
AddAbilityGroupEffect(new CharacterAbilityGroupEffect(abilityEffectType, this, abilityGroup), abilityEffectType);
}
public void AddAbilityGroupEffect(CharacterAbilityGroupEffect characterAbilityGroup, AbilityEffectType abilityEffectType = AbilityEffectType.None)
@@ -1,5 +1,4 @@
using Microsoft.Xna.Framework;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -8,13 +7,19 @@ namespace Barotrauma
{
class TalentTree
{
public enum TalentTreeStageState
{
Invalid,
Locked,
Unlocked,
Available,
Highlighted
}
public static readonly Dictionary<string, TalentTree> JobTalentTrees = new Dictionary<string, TalentTree>();
public readonly List<TalentSubTree> TalentSubTrees = new List<TalentSubTree>();
private static HashSet<string> subtreeTalents = new HashSet<string>();
private const string PlaceholderTalent = "placeholder";
public XElement ConfigElement
{
get;
@@ -35,14 +40,13 @@ namespace Barotrauma
foreach (XElement subTreeElement in element.GetChildElements("subtree"))
{
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
TalentSubTrees.Add(new TalentSubTree(subTreeElement));
}
// talents found and unlocked using the identifier wihin the talent tree, so no duplicates may occur
HashSet<string> duplicateSet = new HashSet<string>();
foreach (string talent in TalentSubTrees.SelectMany(s => s.TalentOptionStages.SelectMany(o => o.Talents.Select(t => t.Identifier))))
{
if (talent == PlaceholderTalent) { continue; }
TalentPrefab talentPrefab = TalentPrefab.TalentPrefabs.Find(c => c.Identifier.Equals(talent, StringComparison.OrdinalIgnoreCase));
if (talentPrefab == null)
{
@@ -97,7 +101,7 @@ namespace Barotrauma
}
break;
default:
DebugConsole.ThrowError($"Invalid XML root element: '{rootElement.Name.ToString()}' in {file.Path}");
DebugConsole.ThrowError($"Invalid XML root element: '{rootElement.Name}' in {file.Path}");
break;
}
}
@@ -117,10 +121,63 @@ namespace Barotrauma
return IsViableTalentForCharacter(character, talentIdentifier, character?.Info?.UnlockedTalents ?? Enumerable.Empty<string>());
}
// i hate this function - markus
public static TalentTreeStageState GetTalentOptionStageState(Character character, string subTreeIdentifier, int index, List<string> selectedTalents)
{
if (character?.Info?.Job.Prefab is null) { return TalentTreeStageState.Invalid; }
if (!JobTalentTrees.TryGetValue(character.Info.Job.Prefab.Identifier, out TalentTree talentTree)) { return TalentTreeStageState.Invalid; }
TalentSubTree subTree = talentTree.TalentSubTrees.FirstOrDefault(tst => tst.Identifier == subTreeIdentifier);
if (subTree == null) { return TalentTreeStageState.Invalid; }
TalentOption targetTalentOption = subTree.TalentOptionStages[index];
if (targetTalentOption.Talents.Any(t => character.HasTalent(t.Identifier)))
{
return TalentTreeStageState.Unlocked;
}
if (targetTalentOption.Talents.Any(t => selectedTalents.Contains(t.Identifier)))
{
return TalentTreeStageState.Highlighted;
}
bool hasTalentInLastTier = true;
bool isLastTalentPurchased = true;
int lastindex = index - 1;
if (lastindex >= 0)
{
TalentOption lastLatentOption = subTree.TalentOptionStages[lastindex];
hasTalentInLastTier = lastLatentOption.Talents.Any(HasTalent);
isLastTalentPurchased = lastLatentOption.Talents.Any(t => character.HasTalent(t.Identifier));
}
if (!hasTalentInLastTier)
{
return TalentTreeStageState.Locked;
}
bool hasPointsForNewTalent = character.Info.GetTotalTalentPoints() - selectedTalents.Count > 0;
if (hasPointsForNewTalent)
{
return isLastTalentPurchased ? TalentTreeStageState.Highlighted : TalentTreeStageState.Available;
}
return TalentTreeStageState.Locked;
bool HasTalent(TalentPrefab t)
{
return selectedTalents.Contains(t.Identifier);
}
}
public static bool IsViableTalentForCharacter(Character character, string talentIdentifier, IEnumerable<string> selectedTalents)
{
if (talentIdentifier == PlaceholderTalent) { return false; }
if (character?.Info?.Job.Prefab == null) { return false; }
if (character.Info.GetTotalTalentPoints() - selectedTalents.Count() <= 0) { return false; }
@@ -173,12 +230,16 @@ namespace Barotrauma
{
public string Identifier { get; }
public string DisplayName { get; }
public readonly List<TalentOption> TalentOptionStages = new List<TalentOption>();
public TalentSubTree(XElement subTreeElement)
{
Identifier = subTreeElement.GetAttributeString("identifier", "");
DisplayName = TextManager.Get("talenttree." + Identifier, returnNull: true) ?? Identifier;
foreach (XElement talentOptionsElement in subTreeElement.GetChildElements("talentoptions"))
{
TalentOptionStages.Add(new TalentOption(talentOptionsElement, Identifier));
@@ -196,6 +257,7 @@ namespace Barotrauma
foreach (XElement talentOptionElement in talentOptionsElement.GetChildElements("talentoption"))
{
string identifier = talentOptionElement.GetAttributeString("identifier", string.Empty);
if (!TalentPrefab.TalentPrefabs.ContainsKey(identifier))
{
DebugConsole.ThrowError($"Error in talent tree \"{debugIdentifier}\" - could not find a talent with the identifier \"{identifier}\".");