Unstable 1.2.4.0
This commit is contained in:
+38
-16
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-5
@@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-3
@@ -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;
|
||||
}
|
||||
|
||||
+5
-8
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -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);
|
||||
}
|
||||
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-8
@@ -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());
|
||||
}
|
||||
|
||||
+4
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user