139 lines
5.6 KiB
C#
139 lines
5.6 KiB
C#
using System;
|
|
using System.Reflection;
|
|
|
|
namespace Barotrauma.Abilities
|
|
{
|
|
abstract class CharacterAbility
|
|
{
|
|
public CharacterAbilityGroup CharacterAbilityGroup { get; }
|
|
public CharacterTalent CharacterTalent { get; }
|
|
public Character Character { get; }
|
|
|
|
public bool RequiresAlive { get; }
|
|
|
|
public virtual bool AllowClientSimulation => false;
|
|
public virtual bool AppliesEffectOnIntervalUpdate => false;
|
|
|
|
private const float DefaultEffectTime = 1.0f;
|
|
|
|
// currently resets if the character dies. would need to be stored in a dictionary of sorts to maintain through death
|
|
|
|
|
|
/// <summary>
|
|
/// Used primarily for StatusEffects. Default to constant outside interval abilities.
|
|
/// </summary>
|
|
protected float EffectDeltaTime => CharacterAbilityGroup is CharacterAbilityGroupInterval abilityGroupInterval ? abilityGroupInterval.TimeSinceLastUpdate : DefaultEffectTime;
|
|
|
|
public CharacterAbility(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement)
|
|
{
|
|
CharacterAbilityGroup = characterAbilityGroup ?? throw new ArgumentNullException(nameof(characterAbilityGroup));
|
|
CharacterTalent = characterAbilityGroup.CharacterTalent;
|
|
Character = CharacterTalent.Character;
|
|
RequiresAlive = abilityElement.GetAttributeBool("requiresalive", true);
|
|
}
|
|
|
|
public bool IsViable()
|
|
{
|
|
if (!AllowClientSimulation && GameMain.NetworkMember is { IsClient: true }) { return false; }
|
|
if (RequiresAlive && Character.IsDead) { return false; }
|
|
return true;
|
|
}
|
|
|
|
public virtual void InitializeAbility(bool addingFirstTime) { }
|
|
|
|
public virtual void UpdateCharacterAbility(bool conditionsMatched, float timeSinceLastUpdate)
|
|
{
|
|
// may need a separate Update for changing state on non-interval-based abilities
|
|
if (AppliesEffectOnIntervalUpdate)
|
|
{
|
|
if (conditionsMatched)
|
|
{
|
|
ApplyEffect();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VerifyState(conditionsMatched, timeSinceLastUpdate);
|
|
}
|
|
}
|
|
|
|
protected virtual void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
|
|
{
|
|
DebugConsole.ThrowError($"Error in talent {CharacterTalent.DebugIdentifier}: Ability {this} does not have an implementation for VerifyState! This ability does not work in interval ability groups.",
|
|
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
|
}
|
|
|
|
public void ApplyAbilityEffect(AbilityObject abilityObject)
|
|
{
|
|
if (abilityObject is null)
|
|
{
|
|
ApplyEffect();
|
|
}
|
|
else
|
|
{
|
|
ApplyEffect(abilityObject);
|
|
}
|
|
}
|
|
|
|
protected virtual void ApplyEffect()
|
|
{
|
|
DebugConsole.AddWarning($"Ability {this} used improperly! This ability does not have a definition for ApplyEffect in talent {CharacterTalent.DebugIdentifier}",
|
|
CharacterTalent.Prefab.ContentPackage);
|
|
}
|
|
|
|
protected virtual void ApplyEffect(AbilityObject abilityObject)
|
|
{
|
|
DebugConsole.AddWarning($"Ability {this} used improperly! This ability does not take a parameter for ApplyEffect in talent {CharacterTalent.DebugIdentifier}",
|
|
CharacterTalent.Prefab.ContentPackage);
|
|
}
|
|
|
|
protected void LogAbilityObjectMismatch()
|
|
{
|
|
DebugConsole.ThrowError($"Incompatible ability! Ability {this} is incompatitible with this type of ability effect type in talent {CharacterTalent.DebugIdentifier}",
|
|
contentPackage: CharacterTalent.Prefab.ContentPackage);
|
|
}
|
|
|
|
// XML
|
|
public static CharacterAbility Load(ContentXElement abilityElement, CharacterAbilityGroup characterAbilityGroup, bool errorMessages = true)
|
|
{
|
|
Type abilityType;
|
|
string type = abilityElement.Name.ToString().ToLowerInvariant();
|
|
try
|
|
{
|
|
abilityType = ReflectionUtils.GetTypeWithBackwardsCompatibility("Barotrauma.Abilities", type, false, true);
|
|
if (abilityType == null)
|
|
{
|
|
if (errorMessages) DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")",
|
|
contentPackage: abilityElement.ContentPackage);
|
|
return null;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (errorMessages)
|
|
{
|
|
DebugConsole.ThrowError("Could not find the CharacterAbility \"" + type + "\" (" + characterAbilityGroup.CharacterTalent.DebugIdentifier + ")", e,
|
|
contentPackage: abilityElement.ContentPackage);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
object[] args = { characterAbilityGroup, abilityElement };
|
|
CharacterAbility characterAbility;
|
|
|
|
try
|
|
{
|
|
characterAbility = (CharacterAbility)Activator.CreateInstance(abilityType, args);
|
|
}
|
|
catch (TargetInvocationException e)
|
|
{
|
|
DebugConsole.ThrowError("Error while creating an instance of a CharacterAbility of the type " + abilityType + ".", e.InnerException,
|
|
contentPackage: abilityElement.ContentPackage);
|
|
return null;
|
|
}
|
|
|
|
return characterAbility;
|
|
}
|
|
}
|
|
}
|