Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
42ba053
format doc & fix description stat for isStunned
bernatvadell Sep 1, 2024
65bdba2
implement RandomElement
bernatvadell Sep 1, 2024
f00c91b
add new stat IsAsleep and implement logic to manage
bernatvadell Sep 1, 2024
c491b2a
fix Sleep skill initializer
bernatvadell Sep 1, 2024
7bcc87f
add update plugin to fix sleep
bernatvadell Sep 1, 2024
3c5c1f5
fix description for asleep attribute
bernatvadell Sep 1, 2024
90fd3ea
fix update sleep
bernatvadell Sep 1, 2024
6ce6417
fix persistence
bernatvadell Sep 1, 2024
048cf7b
fix persistence stat
bernatvadell Sep 1, 2024
7cca882
Merge branch 'master' into feat/skill-sleep
sven-n Sep 2, 2024
8839dd7
Merge remote-tracking branch 'original/master' into feat/skill-sleep
bernatvadell Sep 7, 2024
81e1c47
remove random element
bernatvadell Sep 7, 2024
b0eae8a
Merge branch 'master' into sleep-followup
ze-dom Dec 2, 2025
1492df9
Merge branch 'skill_multipliers_addition_and_refactoring' into sleep-…
ze-dom Dec 2, 2025
a38c405
Proposal for variable magic effect duration and chance
ze-dom Dec 3, 2025
70e4d36
Added migrations and max values
ze-dom Dec 9, 2025
e32f6af
Added innovation skill and more
ze-dom Dec 23, 2025
959f5a9
Removed update plugin
ze-dom Dec 23, 2025
4722163
Improved MagicEffectNumber comment descriptions
ze-dom Dec 23, 2025
51f812e
Added bugfixes in AreaSkillAttackAction
ze-dom Dec 23, 2025
785575a
Merge branch 'master' into sleep-followup
ze-dom Dec 24, 2025
1ca7e50
Some fixes
ze-dom Dec 24, 2025
11535bf
Added Reflection MagicEffect
ze-dom Dec 24, 2025
9a32658
Fixed reflection exc and wing options
ze-dom Dec 26, 2025
7585b63
Added update plugin
ze-dom Dec 26, 2025
90aaf26
Added max reflect damage for mobs
ze-dom Dec 29, 2025
d25bd42
Added condition for ammo consumption
ze-dom Dec 29, 2025
0d18f33
Added removal of IsAsleep for players when attacked
ze-dom Dec 29, 2025
f60c8c1
Added/changed skill max values
ze-dom Dec 29, 2025
15ef02a
Change comment
ze-dom Dec 30, 2025
7b0610a
Some fixes
ze-dom Dec 30, 2025
2f24715
Fixed damage reflection and added raven dmg reflect to owner
ze-dom Dec 30, 2025
97814a1
Excluded raven from full reflect
ze-dom Dec 30, 2025
1d5cb0d
Added IsAlive condition upfront check to apply magic effects
ze-dom Dec 30, 2025
7ce25e5
reverted leftover
ze-dom Dec 30, 2025
6e40bbf
Reverted int cast to avoid reflected damage lower than 1
ze-dom Dec 30, 2025
72480af
Merge branch 'master' into sleep-followup
ze-dom Dec 31, 2025
c8f130d
Adjusted comments
ze-dom Dec 31, 2025
41e1a4f
Added comment
ze-dom Dec 31, 2025
d8cb307
Reverted extra range for AreaSkillExplicitTarget by request
ze-dom Jan 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/AttributeSystem/ComposableAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@ public class ComposableAttribute : BaseAttribute, IComposableAttribute
{
private readonly IList<IElement> _elementList;

private float? _maximumValue;

private float? _cachedValue;

/// <summary>
/// Initializes a new instance of the <see cref="ComposableAttribute" /> class.
/// </summary>
/// <param name="definition">The definition.</param>
/// <param name="aggregateType">Type of the aggregate.</param>
public ComposableAttribute(AttributeDefinition definition, AggregateType aggregateType = AggregateType.AddRaw)
/// <param name="maximumValue">The inner maximum value.</param>
public ComposableAttribute(AttributeDefinition definition, AggregateType aggregateType = AggregateType.AddRaw, float? maximumValue = null)
: base(definition, aggregateType)
{
this._elementList = new List<IElement>();
this._maximumValue = maximumValue;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -78,6 +82,11 @@ private float GetAndCacheValue()
}

var newValue = (rawValues * multiValues) + finalValues;
if (this._maximumValue.HasValue)
{
newValue = Math.Min(this._maximumValue.Value, newValue);
}

if (this.Definition.MaximumValue.HasValue)
{
newValue = Math.Min(this.Definition.MaximumValue.Value, newValue);
Expand Down
5 changes: 5 additions & 0 deletions src/DataModel/Attributes/PowerUpDefinitionValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public partial class PowerUpDefinitionValue
[MemberOfAggregate]
public virtual ICollection<AttributeRelationship> RelatedValues { get; protected set; } = null!;

/// <summary>
/// Gets or sets the maximum allowable value.
/// </summary>
public float? MaximumValue { get; set; }

/// <inheritdoc />
public override string ToString()
{
Expand Down
51 changes: 51 additions & 0 deletions src/DataModel/Configuration/MagicEffectDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,69 @@ public partial class MagicEffectDefinition
/// </summary>
public bool SendDuration { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the duration of the effect depends on the target's level.
/// </summary>
public bool DurationDependsOnTargetLevel { get; set; }

/// <summary>
/// Gets or sets a value by which the effect target's (monster) level should be divided in case <see cref="DurationDependsOnTargetLevel"/> is <c>true</c>.
/// </summary>
public float MonsterTargetLevelDivisor { get; set; } = 1f;

/// <summary>
/// Gets or sets a value by which the effect target's (player) level should be divided in case <see cref="DurationDependsOnTargetLevel"/> is <c>true</c>.
/// </summary>
public float PlayerTargetLevelDivisor { get; set; } = 1f;
Comment on lines +54 to +67
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now it's okay. I'd like to have this more generic, but have no idea for a better solution yet.


/// <summary>
/// Gets or sets the chance of applying the effect, in decimals.
/// </summary>
/// <remarks>
/// Results in a value of 1.0 if not set.
/// </remarks>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? Chance { get; set; }

/// <summary>
/// Gets or sets the chance of applying the effect in PvP, in decimals.
/// </summary>
/// <remarks>
/// Results in the same value as <see cref="Chance"/> if not set.
/// </remarks>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? ChancePvp { get; set; }

/// <summary>
/// Gets or sets the duration which describes how long the <see cref="PowerUpDefinitions"/> apply, in seconds.
/// </summary>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? Duration { get; set; }

/// <summary>
/// Gets or sets the duration which describes how long the <see cref="PowerUpDefinitions"/> apply to PvP, in seconds.
/// </summary>
/// <remarks>
/// Results in the same value as <see cref="Duration"/> if not set.
/// </remarks>
[MemberOfAggregate]
public virtual PowerUpDefinitionValue? DurationPvp { get; set; }

/// <summary>
/// Gets or sets the power up definitions which are used to create the actual power up element.
/// </summary>
[MemberOfAggregate]
public virtual ICollection<PowerUpDefinition> PowerUpDefinitions { get; protected set; } = null!;

/// <summary>
/// Gets or sets the power up definitions which are used to create the actual power up element for PvP.
/// </summary>
/// <remarks>
/// Results in the same collection as <see cref="PowerUpDefinitions"/> if not set.
/// </remarks>
[MemberOfAggregate]
public virtual ICollection<PowerUpDefinition> PowerUpDefinitionsPvp { get; protected set; } = null!;

/// <inheritdoc />
public override string ToString()
{
Expand Down
33 changes: 33 additions & 0 deletions src/DataModel/Entities/SkillEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ public int Level
[Transient]
public (AttributeDefinition Target, IElement BuffPowerUp)[]? PowerUps { get; set; }

/// <summary>
/// Gets or sets the PvP power up element of this skill of this player. It is a "cached" element which will be created on demand and can be applied multiple times.
/// </summary>
[Transient]
public (AttributeDefinition Target, IElement BuffPowerUp)[]? PowerUpsPvp { get; set; }

/// <summary>
/// Gets or sets the duration of the <see cref="PowerUps"/>.
/// </summary>
Expand All @@ -58,6 +64,33 @@ public int Level
[Transient]
public IElement? PowerUpDuration { get; set; }

/// <summary>
/// Gets or sets the duration of the <see cref="PowerUps"/> for PvP.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpDurationPvp { get; set; }

/// <summary>
/// Gets or sets the chance of applying the <see cref="PowerUps"/>.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpChance { get; set; }

/// <summary>
/// Gets or sets the chance of applying the <see cref="PowerUps"/> for PvP.
/// </summary>
/// <remarks>
/// It is an IElement, because the duration can be dependent from the player attributes.
/// </remarks>
[Transient]
public IElement? PowerUpChancePvp { get; set; }

/// <summary>
/// Gets or sets the attributes, if this skill has attribute relationships.
/// </summary>
Expand Down
6 changes: 5 additions & 1 deletion src/DataModel/GameConfigurationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public static class GameConfigurationHelper
.Concat(c.ItemOptions.SelectMany(o => o.PossibleOptions.Select(p => p.PowerUpDefinition).WhereNotNull()))
.Concat(c.ItemOptions.SelectMany(o => o.PossibleOptions.SelectMany(p => p.LevelDependentOptions.Select(l => l.PowerUpDefinition).WhereNotNull())))
.Concat(c.MagicEffects.SelectMany(m => m.PowerUpDefinitions))
.Concat(c.MagicEffects.SelectMany(m => m.PowerUpDefinitionsPvp))
},
{ typeof(ItemBasePowerUpDefinition), c => c.Items.SelectMany(i => i.BasePowerUpAttributes) },
{ typeof(ItemDropItemGroup), c => c.Items.SelectMany(i => i.DropItems) },
Expand All @@ -65,7 +66,10 @@ public static class GameConfigurationHelper
{ typeof(JewelMix), c => c.JewelMixes },
{ typeof(MagicEffectDefinition), c => c.MagicEffects },
{
typeof(PowerUpDefinitionValue), c => c.MagicEffects.Select(e => e.Duration).WhereNotNull()
typeof(PowerUpDefinitionValue), c => c.MagicEffects.Select(e => e.Duration)
.Concat(c.MagicEffects.Select(e => e.DurationPvp))
.Concat(c.MagicEffects.Select(e => e.Chance))
.Concat(c.MagicEffects.Select(e => e.ChancePvp)).WhereNotNull()
.Concat(Enumerables![typeof(PowerUpDefinition)](c).OfType<PowerUpDefinition>().Select(p => p.Boost).WhereNotNull())
},
{ typeof(SimpleElement), c => Enumerables![typeof(PowerUpDefinitionValue)](c).OfType<PowerUpDefinitionValue>().Select(v => v.ConstantValue).WhereNotNull() },
Expand Down
52 changes: 46 additions & 6 deletions src/GameLogic/AttackableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public static async ValueTask<HitInfo> CalculateDamageAsync(this IAttacker attac
{
var defenseAttribute = defender.GetDefenseAttribute(attacker);
defense = (int)defender.Attributes[defenseAttribute];
defense -= (int)(defense * defender.Attributes[Stats.InnovationDefDecrement]);
if (defense < 0)
{
defense = 0;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

attacker.GetBaseDmg(skill, out int baseMinDamage, out int baseMaxDamage, out DamageType damageType);
Expand Down Expand Up @@ -309,7 +314,15 @@ public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAt
player.CreateMagicEffectPowerUp(skillEntry);
}

await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, skillEntry.PowerUpDuration!, skillEntry.PowerUps!).ConfigureAwait(false);
float chance = target is Player ? skillEntry.PowerUpChancePvp!.Value : skillEntry.PowerUpChance!.Value;
if (!Rand.NextRandomBool(Convert.ToDouble(chance)))
{
return;
}

var duration = target is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!;
var powerUps = target is Player ? skillEntry.PowerUpsPvp! : skillEntry.PowerUps!;
await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, powerUps).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -356,6 +369,11 @@ public static async ValueTask ApplyRegenerationAsync(this IAttackable target, Pl
/// <returns>The success of the appliance.</returns>
public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, SkillEntry skillEntry)
{
if (!target.IsAlive)
{
return false;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not aware of any effect that can be applied when the target is dead. This saves some unnecessary computations 😛

skillEntry.ThrowNotInitializedProperty(skillEntry.Skill is null, nameof(skillEntry.Skill));
var modifier = skillEntry.Skill.ElementalModifierTarget;
if (modifier is null)
Expand Down Expand Up @@ -389,7 +407,7 @@ public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackab
}

/// <summary>
/// Applies the elemental effects of a players skill to the target.
/// Applies the elemental effects of a monster's skill to the target.
/// </summary>
/// <param name="target">The target.</param>
/// <param name="attacker">The attacker.</param>
Expand All @@ -402,6 +420,11 @@ public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackab
/// </returns>
public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, Skill skill, IElement? powerUp, IElement? duration, AttributeDefinition? targetAttribute)
{
if (!target.IsAlive)
{
return false;
}

var modifier = skill.ElementalModifierTarget;
if (modifier is null)
{
Expand Down Expand Up @@ -445,7 +468,7 @@ public static void ApplyAmmunitionConsumption(this IAttacker attacker, HitInfo h
{
if (!hitInfo.Attributes.HasFlag(DamageAttributes.Reflected) && attacker.Attributes[Stats.AmmunitionConsumptionRate] > 0.0)
{
// Every hit needs ammo. Failed hits don't need ammo.
// Every hit needs ammo, missed or not
if (attacker.Attributes[Stats.AmmunitionAmount] < attacker.Attributes[Stats.AmmunitionConsumptionRate])
{
return;
Expand Down Expand Up @@ -773,15 +796,32 @@ private static void GetBaseDmg(this IAttacker attacker, SkillEntry? skill, out i
/// <param name="powerUps">The power ups of the effect.</param>
private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, IElement duration, params (AttributeDefinition Target, IElement Boost)[] powerUps)
{
float finalDuration = duration.Value;

if (magicEffectDefinition.DurationDependsOnTargetLevel)
{
var divisor = target is Player ? magicEffectDefinition.PlayerTargetLevelDivisor : magicEffectDefinition.MonsterTargetLevelDivisor;
if (divisor != 0)
{
finalDuration -= target.Attributes[Stats.Level] / divisor;
}
}
Comment on lines +801 to +808
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Sleep (zTeamS6.3, emu), Weakness (zTeamS6.3, emu) and Innovation (zTeamS6.3, emu) skills.


TimeSpan durationSpan = TimeSpan.FromSeconds(finalDuration);
if (durationSpan < TimeSpan.FromSeconds(1))
{
return;
}
Comment on lines +811 to +814
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw this on Summoner skills (e.g. zTeamS6.3). Think is a good generalization.


var isPoisonEffect = magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsPoisoned);
var magicEffect = isPoisonEffect
? new PoisonMagicEffect(powerUps[0].Boost, magicEffectDefinition, TimeSpan.FromSeconds(duration.Value), attacker, target)
: new MagicEffect(TimeSpan.FromSeconds(duration.Value), magicEffectDefinition, powerUps.Select(p => new MagicEffect.ElementWithTarget(p.Boost, p.Target)).ToArray());
? new PoisonMagicEffect(powerUps[0].Boost, magicEffectDefinition, durationSpan, attacker, target)
: new MagicEffect(durationSpan, magicEffectDefinition, powerUps.Select(p => new MagicEffect.ElementWithTarget(p.Boost, p.Target)).ToArray());

await target.MagicEffectList.AddEffectAsync(magicEffect).ConfigureAwait(false);
if (target is ISupportWalk walkSupporter
&& walkSupporter.IsWalking
&& magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsFrozen || e.TargetAttribute == Stats.IsStunned))
&& magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsFrozen || e.TargetAttribute == Stats.IsStunned || e.TargetAttribute == Stats.IsAsleep))
{
await walkSupporter.StopWalkingAsync().ConfigureAwait(false);

Expand Down
18 changes: 17 additions & 1 deletion src/GameLogic/Attributes/AttributeSystemExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public static class AttributeSystemExtensions
/// </summary>
private static AttributeDefinition DurationDummy { get; } = new(new Guid("23D069C3-24D8-4277-8FDC-D82F0AF64037"), "Duration Dummy", "A dummy attribute to be used internally for durations.");

/// <summary>
/// Gets the attribute for a dummy attribute to be used internally for chances.
/// </summary>
private static AttributeDefinition ChanceDummy { get; } = new(new Guid("E6B9E6A5-5800-40EA-80B7-14C2C06392A6"), "Chance Dummy", "A dummy attribute to be used internally for chances.");

/// <summary>
/// Creates a new element on this attribute system with the specified power up value.
/// </summary>
Expand All @@ -29,6 +34,17 @@ public static IElement CreateDurationElement(this IAttributeSystem attributeSyst
return attributeSystem.CreateElement(powerUpDefinition, DurationDummy);
}

/// <summary>
/// Creates a new element on this attribute system with the specified power up value.
/// </summary>
/// <param name="attributeSystem">The attribute system.</param>
/// <param name="powerUpDefinition">The power up definition.</param>
/// <returns>The added element.</returns>
public static IElement CreateChanceElement(this IAttributeSystem attributeSystem, PowerUpDefinitionValue powerUpDefinition)
{
return attributeSystem.CreateElement(powerUpDefinition, ChanceDummy);
}

/// <summary>
/// Creates a new element on this attribute system with the specified power up value.
/// </summary>
Expand Down Expand Up @@ -70,7 +86,7 @@ public static IElement CreateElement(this IAttributeSystem attributeSystem, Powe
elements = elements.Concat(value.ConstantValue.GetAsEnumerable());
}

var composableResult = new ComposableAttribute(targetDefinition, result.AggregateType);
var composableResult = new ComposableAttribute(targetDefinition, result.AggregateType, value.MaximumValue);

elements.ForEach(element => composableResult.AddElement(element));
return composableResult;
Expand Down
19 changes: 17 additions & 2 deletions src/GameLogic/Attributes/Stats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ public class Stats
/// <summary>
/// Gets the explosion skill MST bonus damage, which rises with fire tome strengthener and is added late stage.
/// </summary>
public static AttributeDefinition ExplosionBonusDmg { get; } = new(new Guid("543E01C2-5C61-4473-ACF9-8A63A987A230"), "Explosion Bonus Damage (MST)", "The explosion skill (book of samut) bonus damage, which rises with fire stome strengthener and is added at a late stage.");
public static AttributeDefinition ExplosionBonusDmg { get; } = new(new Guid("543E01C2-5C61-4473-ACF9-8A63A987A230"), "Explosion Bonus Damage (MST)", "The explosion skill (book of samut) bonus damage, which rises with fire tome strengthener and is added at a late stage.");

/// <summary>
/// Gets the requiem skill MST bonus damage, which rises with wind tome strengthener and is added late stage.
Expand Down Expand Up @@ -920,6 +920,11 @@ public class Stats
/// <remarks>Only applies to physical damage.</remarks>
public static AttributeDefinition WeaknessPhysDmgDecrement { get; } = new(new Guid("37497650-139B-4DA1-9FB6-27AEB8F04CF6"), "Weakness Physical Damage Decrement", "The inflicted physical damage decrement due to the magic effects of weakness or killing blow skills, which is multiplied with the final damage and subtracted from it.");

/// <summary>
/// Gets the innovation defense decrement due to Summoner's innovation skill attribute definition.
/// </summary>
public static AttributeDefinition InnovationDefDecrement { get; } = new(new Guid("D8B3B1C9-B409-4A07-8F4D-8F315DCB173A"), "Innovation Defense Decrement", "The defense decrement due to the magic effect of innovation skill, which is multiplied with the final defense and subtracted from it.");

/// <summary>
/// Gets the 'is shield equipped' attribute definition.
/// </summary>
Expand All @@ -943,7 +948,12 @@ public class Stats
/// <summary>
/// Gets the attribute definition, which defines if a player has stun effect applied.
/// </summary>
public static AttributeDefinition IsStunned { get; } = new(new Guid("22C86BAF-7F27-478D-8075-E4465C2859DD"), "Is stunned", "The player is poisoned and loses health");
public static AttributeDefinition IsStunned { get; } = new(new Guid("22C86BAF-7F27-478D-8075-E4465C2859DD"), "Is stunned", "The player is stunned and can't move.");

/// <summary>
/// Gets the attribute definition, which defines if a player has asleep effect applied.
/// </summary>
public static AttributeDefinition IsAsleep { get; } = new(new Guid("0518F532-7A8F-4491-8A23-98B620608CB3"), "Is asleep", "The player is asleep and can't move until hit.");

/// <summary>
/// Gets the ice resistance attribute definition. Value range from 0 to 1.
Expand Down Expand Up @@ -1226,6 +1236,11 @@ public class Stats
/// </summary>
public static AttributeDefinition FullyRecoverHealthAfterHitChance { get; } = new(new Guid("3CA72C07-9C2C-4FC5-8BCB-9BD737F83664"), "Chance to fully recover health when getting hit", "3rd Wing Option");

/// <summary>
/// Gets the fully reflect damage after hit chance definition.
/// </summary>
public static AttributeDefinition FullyReflectDamageAfterHitChance { get; } = new(new Guid("1F7C1E04-4FBD-4FCB-A6C2-EB51A91D8C3E"), "Chance to fully reflect damage when getting hit", "3rd Wing Option");

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/// <summary>
/// Gets the health loss after hit definition.
/// </summary>
Expand Down
Loading