Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/AbilityGroups/CharacterAbilityGroup.cs
T
2024-12-11 13:26:13 +02:00

295 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Barotrauma.Abilities
{
abstract class CharacterAbilityGroup
{
public CharacterTalent CharacterTalent { get; }
public Character Character { get; }
// 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 readonly int maxTriggerCount;
protected int timesTriggered = 0;
// add support for OR conditions?
protected readonly List<AbilityCondition> abilityConditions = new List<AbilityCondition>();
/// <summary>
/// List of abilities that are triggered by this group.
/// Fallback abilities are triggered if the conditional fails
/// </summary>
protected readonly List<CharacterAbility> characterAbilities = new List<CharacterAbility>(),
fallbackAbilities = new List<CharacterAbility>();
public CharacterAbilityGroup(AbilityEffectType abilityEffectType, CharacterTalent characterTalent, ContentXElement abilityElementGroup)
{
AbilityEffectType = abilityEffectType;
CharacterTalent = characterTalent ?? throw new ArgumentNullException(nameof(characterTalent));
Character = CharacterTalent.Character;
maxTriggerCount = abilityElementGroup.GetAttributeInt("maxtriggercount", int.MaxValue);
foreach (var subElement in abilityElementGroup.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "abilities":
LoadAbilities(subElement);
break;
case "fallbackabilities":
LoadFallbackAbilities(subElement);
break;
case "condition":
case "conditions":
LoadConditions(subElement);
break;
default:
DebugConsole.ThrowError($"Error in talent {characterTalent.Prefab.Identifier}: unrecognized xml element \"{subElement.Name}\".");
break;
}
}
switch (abilityEffectType)
{
case AbilityEffectType.OnDieToCharacter:
if (characterAbilities.Any(a => a.RequiresAlive))
{
DebugConsole.AddWarning($"Potential error in talent {characterTalent}: an ability group has the type {AbilityEffectType.OnDieToCharacter}, but includes abilities that require the character to be alive, meaning they will never execute.",
characterTalent.Prefab.ContentPackage);
}
break;
}
}
public void ActivateAbilityGroup(bool addingFirstTime)
{
if (!CheckActivatingCondition()) { return; }
foreach (var characterAbility in characterAbilities)
{
characterAbility.InitializeAbility(addingFirstTime);
}
foreach (var characterAbility in fallbackAbilities)
{
characterAbility.InitializeAbility(addingFirstTime);
}
}
private bool CheckActivatingCondition()
{
if (AbilityEffectType is not AbilityEffectType.None) { return true; }
return !abilityConditions.Any(static abilityCondition => !abilityCondition.MatchesCondition());
}
public void LoadConditions(ContentXElement conditionElements)
{
foreach (ContentXElement conditionElement in conditionElements.Elements())
{
AbilityCondition newCondition = ConstructCondition(CharacterTalent, conditionElement);
if (newCondition == null)
{
DebugConsole.ThrowError($"AbilityCondition was not found in talent {CharacterTalent.DebugIdentifier}!",
contentPackage: conditionElement.ContentPackage);
return;
}
if (!newCondition.AllowClientSimulation && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
IsActive = false;
}
abilityConditions.Add(newCondition);
}
}
public void AddAbility(CharacterAbility characterAbility)
{
if (characterAbility == null)
{
DebugConsole.ThrowError($"Trying to add null ability for talent {CharacterTalent.DebugIdentifier}!",
contentPackage: CharacterTalent.Prefab.ContentPackage);
return;
}
characterAbilities.Add(characterAbility);
}
public void AddFallbackAbility(CharacterAbility characterAbility)
{
if (characterAbility == null)
{
DebugConsole.ThrowError($"Trying to add null ability for talent {CharacterTalent.DebugIdentifier}!",
contentPackage: CharacterTalent.Prefab.ContentPackage);
return;
}
fallbackAbilities.Add(characterAbility);
}
// XML
private AbilityCondition ConstructCondition(CharacterTalent characterTalent, ContentXElement conditionElement, bool errorMessages = true)
{
Type conditionType;
string type = conditionElement.Name.ToString().ToLowerInvariant();
try
{
conditionType = ReflectionUtils.GetTypeWithBackwardsCompatibility(ToolBox.BarotraumaAssembly, "Barotrauma.Abilities", type, false, true);
if (conditionType == null)
{
if (errorMessages)
{
DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + characterTalent.DebugIdentifier + ")",
contentPackage: characterTalent.Prefab.ContentPackage);
}
return null;
}
}
catch (Exception e)
{
if (errorMessages)
{
DebugConsole.ThrowError("Could not find the component \"" + type + "\" (" + characterTalent.DebugIdentifier + ")", e,
contentPackage: characterTalent.Prefab.ContentPackage);
}
return null;
}
object[] args = { characterTalent, conditionElement };
AbilityCondition newCondition;
try
{
newCondition = (AbilityCondition)Activator.CreateInstance(conditionType, args);
}
catch (TargetInvocationException e)
{
DebugConsole.ThrowError("Error while creating an instance of an ability condition of the type " + conditionType + ".", e.InnerException,
contentPackage: characterTalent.Prefab.ContentPackage);
return null;
}
if (newCondition == null)
{
DebugConsole.ThrowError("Error while creating an instance of an ability condition of the type " + conditionType + ", instance was null",
contentPackage: characterTalent.Prefab.ContentPackage);
return null;
}
return newCondition;
}
private void LoadAbilities(ContentXElement abilityElements)
{
foreach (var abilityElementGroup in abilityElements.Elements())
{
AddAbility(ConstructAbility(abilityElementGroup, CharacterTalent));
}
}
private void LoadFallbackAbilities(ContentXElement abilityElements)
{
foreach (var abilityElementGroup in abilityElements.Elements())
{
AddFallbackAbility(ConstructAbility(abilityElementGroup, CharacterTalent));
}
}
private CharacterAbility ConstructAbility(ContentXElement abilityElement, CharacterTalent characterTalent)
{
CharacterAbility newAbility = CharacterAbility.Load(abilityElement, this);
if (newAbility == null)
{
DebugConsole.ThrowError($"Unable to create an ability for {characterTalent.DebugIdentifier}!",
contentPackage: characterTalent.Prefab.ContentPackage);
return null;
}
return newAbility;
}
public static List<StatusEffect> ParseStatusEffects(CharacterTalent characterTalent, ContentXElement statusEffectElements)
{
if (statusEffectElements == null)
{
DebugConsole.ThrowError("StatusEffect list was not found in talent " + characterTalent.DebugIdentifier,
contentPackage: characterTalent.Prefab.ContentPackage);
return null;
}
List<StatusEffect> statusEffects = new List<StatusEffect>();
foreach (var statusEffectElement in statusEffectElements.Elements())
{
var statusEffect = StatusEffect.Load(statusEffectElement, characterTalent.DebugIdentifier);
statusEffects.Add(statusEffect);
}
return statusEffects;
}
public static StatTypes ParseStatType(string statTypeString, string debugIdentifier)
{
//backwards compatibility
if (statTypeString.Equals("MedicalItemDurationMultiplier", StringComparison.OrdinalIgnoreCase))
{
statTypeString = "BuffItemApplyingMultiplier";
}
if (!Enum.TryParse(statTypeString, true, out StatTypes statType))
{
DebugConsole.ThrowError("Invalid stat type type \"" + statTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
}
return statType;
}
public static List<Affliction> ParseAfflictions(CharacterTalent characterTalent, ContentXElement afflictionElements)
{
if (afflictionElements == null)
{
DebugConsole.ThrowError("Affliction list was not found in talent " + characterTalent.DebugIdentifier,
contentPackage: characterTalent.Prefab.ContentPackage);
return null;
}
List<Affliction> afflictions = new List<Affliction>();
// similar logic to affliction creation in statuseffects
// might be worth unifying
foreach (var afflictionElement in afflictionElements.Elements())
{
Identifier afflictionIdentifier = afflictionElement.GetAttributeIdentifier("identifier", "");
AfflictionPrefab afflictionPrefab = AfflictionPrefab.List.FirstOrDefault(ap => ap.Identifier == afflictionIdentifier);
if (afflictionPrefab == null)
{
DebugConsole.ThrowError("Error in CharacterTalent (" + characterTalent.DebugIdentifier + ") - Affliction prefab with the identifier \"" + afflictionIdentifier + "\" not found.",
contentPackage: characterTalent.Prefab.ContentPackage);
continue;
}
Affliction afflictionInstance = afflictionPrefab.Instantiate(afflictionElement.GetAttributeFloat(1.0f, "amount", "strength"));
afflictionInstance.Probability = afflictionElement.GetAttributeFloat(1.0f, "probability");
afflictions.Add(afflictionInstance);
}
return afflictions;
}
public static AbilityFlags ParseFlagType(string flagTypeString, string debugIdentifier)
{
if (!Enum.TryParse(flagTypeString, true, out AbilityFlags flagType))
{
DebugConsole.ThrowError("Invalid flag type type \"" + flagTypeString + "\" in CharacterTalent (" + debugIdentifier + ")");
}
return flagType;
}
}
}