Unstable 1.2.4.0

This commit is contained in:
Markus Isberg
2023-11-30 13:53:00 +02:00
parent 8a2e2ea0ae
commit fb5ea537bf
210 changed files with 4201 additions and 1283 deletions
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
@@ -11,6 +10,12 @@ namespace Barotrauma.Abilities
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
/// <summary>
/// If enabled, the conditional is checked on the target of the ability (e.g. the character that was killed if the effect type is OnKillCharacter).
/// Defaults to true, except in the case of <see cref="AbilityConditionHasPermanentStat"/>, which by default targets the character who has the talent.
/// </summary>
private readonly bool targetAbilityTarget = false;
public AbilityConditionCharacter(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
{
targetTypes = ParseTargetTypes(
@@ -25,30 +30,47 @@ namespace Barotrauma.Abilities
}
}
if (!targetTypes.Any() && !conditionals.Any())
//don't log this error if this is a subclass of AbilityConditionCharacter
//(in that case not having any conditionals here is ok)
if (!targetTypes.Any() && !conditionals.Any() && GetType() == typeof(AbilityConditionCharacter))
{
DebugConsole.ThrowError($"Error in talent \"{characterTalent}\". No target types or conditionals defined - the condition will match any character.",
contentPackage: conditionElement.ContentPackage);
}
targetAbilityTarget = conditionElement.GetAttributeBool(nameof(targetAbilityTarget), this is not AbilityConditionHasPermanentStat);
}
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
public sealed override bool MatchesCondition()
{
if (abilityObject is IAbilityCharacter abilityCharacter)
//by default data-reliant conditions don't accept null, but in this case it's ok,
//because we can assume it's the character who has the talent
return MatchesCondition(abilityObject: null);
}
public sealed override bool MatchesCondition(AbilityObject abilityObject)
{
return invert ? !MatchesConditionSpecific(abilityObject) : MatchesConditionSpecific(abilityObject);
}
protected sealed override bool MatchesConditionSpecific(AbilityObject abilityObject)
{
Character targetCharacter =
targetAbilityTarget ?
(abilityObject as IAbilityCharacter)?.Character ?? character :
character;
if (targetCharacter is null) { return false; }
if (!IsViableTarget(targetTypes, targetCharacter)) { return false; }
foreach (var conditional in conditionals)
{
if (abilityCharacter.Character is not Character character) { return false; }
if (!IsViableTarget(targetTypes, character)) { return false; }
foreach (var conditional in conditionals)
{
if (!conditional.Matches(character)) { return false; }
}
return true;
}
else
{
LogAbilityConditionError(abilityObject, typeof(IAbilityCharacter));
return false;
if (!conditional.Matches(targetCharacter)) { return false; }
}
return MatchesCharacter(targetCharacter);
}
protected virtual bool MatchesCharacter(Character character)
{
return true;
}
}
}
@@ -1,6 +1,6 @@
namespace Barotrauma.Abilities
{
internal sealed class AbilityConditionCharacterNotLooted : AbilityConditionData
internal sealed class AbilityConditionCharacterNotLooted : AbilityConditionCharacter
{
private readonly Identifier identifier;
@@ -9,11 +9,9 @@ namespace Barotrauma.Abilities
identifier = conditionElement.GetAttributeIdentifier("identifier", Identifier.Empty);
}
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
protected override bool MatchesCharacter(Character character)
{
if (abilityObject is not IAbilityCharacter ability) { return false; }
return !ability.Character.MarkedAsLooted.Contains(identifier);
return character != null &&!character.MarkedAsLooted.Contains(identifier);
}
}
}
@@ -2,15 +2,13 @@
namespace Barotrauma.Abilities
{
internal sealed class AbilityConditionCharacterUnconcious : AbilityConditionData
internal sealed class AbilityConditionCharacterUnconcious : AbilityConditionCharacter
{
public AbilityConditionCharacterUnconcious(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement) { }
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
protected override bool MatchesCharacter(Character character)
{
if (abilityObject is not IAbilityCharacter targetCharacter) { return false; }
return targetCharacter.Character.IsUnconscious;
return character is { IsUnconscious: true };
}
}
}
@@ -0,0 +1,19 @@
using Barotrauma.Items.Components;
namespace Barotrauma.Abilities
{
class AbilityConditionItemIsStatic : AbilityConditionData
{
public AbilityConditionItemIsStatic(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement) { }
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
{
if (abilityObject is IAbilityItem { Item: var item })
{
return item.GetComponent<Holdable>() is null && item.GetComponent<Wearable>() is null;
}
return false;
}
}
}
@@ -1,6 +1,8 @@
namespace Barotrauma.Abilities
using System;
namespace Barotrauma.Abilities
{
class AbilityConditionHasPermanentStat : AbilityConditionDataless
class AbilityConditionHasPermanentStat : AbilityConditionCharacter
{
private readonly Identifier statIdentifier;
private readonly StatTypes statType;
@@ -21,8 +23,13 @@
placeholder = conditionElement.GetAttributeEnum("placeholder", PermanentStatPlaceholder.None);
}
protected override bool MatchesConditionSpecific()
protected override bool MatchesCharacter(Character character)
{
if (character?.Info == null)
{
DebugConsole.AddWarning($"Error in {nameof(AbilityConditionHasPermanentStat.MatchesCharacter)}: character {character} has no CharacterInfo. Are you trying to use the condition on a non-player character?\n{Environment.StackTrace.CleanupStackTrace()}");
return false;
}
Identifier identifier = CharacterAbilityGivePermanentStat.HandlePlaceholders(placeholder, statIdentifier);
return character.Info.GetSavedStatValue(statType, identifier) >= min;
}
@@ -2,21 +2,18 @@
namespace Barotrauma.Abilities
{
internal sealed class AbilityConditionLowestLevel : AbilityConditionDataless
internal sealed class AbilityConditionLowestLevel : AbilityConditionCharacter
{
public AbilityConditionLowestLevel(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement) { }
protected override bool MatchesConditionSpecific()
protected override bool MatchesCharacter(Character character)
{
int ownLevel = character.Info.GetCurrentLevel();
foreach (Character crew in GameSession.GetSessionCrewCharacters(CharacterType.Both))
foreach (Character otherCharacter in GameSession.GetSessionCrewCharacters(CharacterType.Both))
{
if (crew == character) { continue; }
if (crew.Info.GetCurrentLevel() < ownLevel) { return false; }
if (otherCharacter == character) { continue; }
if (otherCharacter.Info.GetCurrentLevel() < ownLevel) { return false; }
}
return true;
}
}
@@ -38,7 +38,11 @@ internal sealed class CharacterAbilityGiveExperience : CharacterAbility
protected override void ApplyEffect(AbilityObject abilityObject)
{
if ((abilityObject as IAbilityCharacter)?.Character is { } targetCharacter)
if (abilityObject is AbilityCharacterKill { Killer: { } killer })
{
ApplyEffectSpecific(killer);
}
else if ((abilityObject as IAbilityCharacter)?.Character is { } targetCharacter)
{
ApplyEffectSpecific(targetCharacter);
}
@@ -7,12 +7,14 @@ namespace Barotrauma.Abilities
private readonly ItemTalentStats stat;
private readonly float value;
private readonly bool stackable;
private readonly bool save;
public CharacterAbilityGiveItemStat(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
stat = abilityElement.GetAttributeEnum("stattype", ItemTalentStats.None);
value = abilityElement.GetAttributeFloat("value", 0f);
stackable = abilityElement.GetAttributeBool("stackable", true);
save = abilityElement.GetAttributeBool("save", false);
}
protected override void VerifyState(bool conditionsMatched, float timeSinceLastUpdate)
@@ -27,7 +29,7 @@ namespace Barotrauma.Abilities
{
if (abilityObject is not IAbilityItem ability) { return; }
ability.Item.StatManager.ApplyStat(stat, stackable, value, CharacterTalent);
ability.Item.StatManager.ApplyStat(stat, stackable, save, value, CharacterTalent);
}
}
}
@@ -10,6 +10,7 @@ namespace Barotrauma.Abilities
private readonly float value;
private readonly ImmutableHashSet<Identifier> tags;
private readonly bool stackable;
private readonly bool save;
public CharacterAbilityGiveItemStatToTags(CharacterAbilityGroup characterAbilityGroup, ContentXElement abilityElement) : base(characterAbilityGroup, abilityElement)
{
@@ -17,6 +18,7 @@ namespace Barotrauma.Abilities
value = abilityElement.GetAttributeFloat("value", 0f);
tags = abilityElement.GetAttributeIdentifierImmutableHashSet("tags", ImmutableHashSet<Identifier>.Empty);
stackable = abilityElement.GetAttributeBool("stackable", true);
save = abilityElement.GetAttributeBool("save", false);
}
public override void InitializeAbility(bool addingFirstTime)
@@ -44,7 +46,7 @@ namespace Barotrauma.Abilities
if (item.Submarine?.TeamID != Character.TeamID) { continue; }
if (item.HasTag(tags) || tags.Contains(item.Prefab.Identifier))
{
item.StatManager.ApplyStat(stat, stackable, value, CharacterTalent);
item.StatManager.ApplyStat(stat, stackable, save, value, CharacterTalent);
}
}
}
@@ -1,4 +1,6 @@
namespace Barotrauma.Abilities
using System;
namespace Barotrauma.Abilities
{
public enum PermanentStatPlaceholder
{
@@ -19,7 +21,12 @@
private readonly bool setValue;
private readonly PermanentStatPlaceholder placeholder;
//private readonly float maximumValue;
/// <summary>
/// If enabled, the effect is applied on the target of the ability (e.g. the character that was killed if the effect type is OnKillCharacter).
/// Defaults to false (= targets the character who has the talent).
/// </summary>
private readonly bool targetAbilityTarget = false;
public override bool AllowClientSimulation => true;
public override bool AppliesEffectOnIntervalUpdate => true;
@@ -40,27 +47,28 @@
giveOnAddingFirstTime = abilityElement.GetAttributeBool("giveonaddingfirsttime", characterAbilityGroup.AbilityEffectType == AbilityEffectType.None);
setValue = abilityElement.GetAttributeBool("setvalue", false);
placeholder = abilityElement.GetAttributeEnum("placeholder", PermanentStatPlaceholder.None);
targetAbilityTarget = abilityElement.GetAttributeBool(nameof(targetAbilityTarget), false);
}
public override void InitializeAbility(bool addingFirstTime)
{
if (giveOnAddingFirstTime && addingFirstTime)
{
ApplyEffectSpecific();
ApplyEffectSpecific(abilityObject: null);
}
}
protected override void ApplyEffect(AbilityObject abilityObject)
{
ApplyEffectSpecific();
ApplyEffectSpecific(abilityObject);
}
protected override void ApplyEffect()
{
ApplyEffectSpecific();
ApplyEffectSpecific(abilityObject: null);
}
private void ApplyEffectSpecific()
private void ApplyEffectSpecific(AbilityObject abilityObject)
{
Identifier identifier = HandlePlaceholders(placeholder, statIdentifier);
if (targetAllies)
@@ -72,7 +80,21 @@
}
else
{
Character?.Info?.ChangeSavedStatValue(statType, value, identifier, removeOnDeath, maxValue: maxValue, setValue: setValue);
Character targetCharacter =
targetAbilityTarget ?
(abilityObject as IAbilityCharacter)?.Character ?? Character :
Character;
if (targetCharacter == null)
{
DebugConsole.ThrowError($"Error in {nameof(CharacterAbilityGivePermanentStat.ApplyEffectSpecific)}: character was null.\n{Environment.StackTrace.CleanupStackTrace()}");
return;
}
if (targetCharacter?.Info == null)
{
DebugConsole.AddWarning($"Error in {nameof(CharacterAbilityGivePermanentStat.ApplyEffectSpecific)}: character {targetCharacter} has no CharacterInfo. Are you trying to use the condition on a non-player character?\n{Environment.StackTrace.CleanupStackTrace()}");
return;
}
targetCharacter.Info.ChangeSavedStatValue(statType, value, identifier, removeOnDeath, maxValue: maxValue, setValue: setValue);
}
}
@@ -83,7 +105,7 @@
switch (placeholder)
{
case PermanentStatPlaceholder.LocationName when map.CurrentLocation is { } location:
return original.Replace("[placeholder]", location.Name);
return original.Replace("[placeholder]", location.NameIdentifier.Value);
case PermanentStatPlaceholder.LocationIndex:
return original.Replace("[placeholder]", map.CurrentLocationIndex.ToString());
}
@@ -44,9 +44,13 @@ namespace Barotrauma.Abilities
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;
}
}