Skip to content

Defense calculations refactoring #602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/AttributeSystem/AttributeRelationshipElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private float CalculateValue()
InputOperator.ExponentiateByAttribute => (float)Math.Pow(
this.InputOperand.Value,
this.InputElements.Sum(a => a.Value)),
_ => throw new InvalidOperationException($"Input operator {this.InputOperator} unknown")
_ => throw new InvalidOperationException($"Input operator {this.InputOperator} unknown"),
};
}
}
2 changes: 1 addition & 1 deletion src/DataModel/Configuration/MagicEffectDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Attributes;

/// <summary>
/// Magic Effect Definition. It can be an effect from an consumed item, or by a buff.
/// Magic Effect Definition. It can be an effect from a consumed item, a buff, or the result of an attack skill.
/// </summary>
[Cloneable]
public partial class MagicEffectDefinition
Expand Down
65 changes: 54 additions & 11 deletions src/GameLogic/Attributes/Stats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ public class Stats
/// <summary>
/// Gets the attack rate PVM attribute definition.
/// </summary>
/// <remarks>
/// <see cref="AggregateType.AddRaw"/> values include:
/// Base attack rate from level and stats, ancient set option bonus, MST PvM attack rate increase bonus.
/// </remarks>
public static AttributeDefinition AttackRatePvm { get; } = new(new Guid("1129442A-E1C7-4240-8866-B781C2838C25"), "Attack Rate (PvM)", string.Empty);

/// <summary>
Expand Down Expand Up @@ -520,21 +524,60 @@ public class Stats
/// <summary>
/// Gets the defense base attribute definition.
/// </summary>
/// <remarks>
/// <see cref="AggregateType.AddRaw"/> values include:
/// Armor, shield, and wings defense plus their item options; dark horse defense bonus.
/// <see cref="AggregateType.Multiplicate"/> values include:
/// Set level bonus; elite transfer skeleton xfm ring bonus.
/// <see cref="AggregateType.AddFinal"/> values include:
/// Harmony defense bonus option; ancient defense bonus option; MST defense increase bonus.
/// </remarks>

Choose a reason for hiding this comment

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

medium

It's great that you've added these remarks to clarify the aggregate types for this attribute. This is very helpful for understanding how the defense is calculated. Consider adding similar remarks to other attribute definitions as well, especially those involved in complex calculations.

public static AttributeDefinition DefenseBase { get; } = new(new Guid("EB098C46-60D4-4CA6-BBD4-5B6270A1407B"), "Base Defense", string.Empty);

/// <summary>
/// Gets the defense final attribute definition.
/// </summary>
/// <remarks>
/// <see cref="AggregateType.AddRaw"/> values include:
/// Halved <see cref="DefenseBase"/>; socket defense bonus option; robot knight xfm ring bonus; panda pet bonus; unicorn pet bonus.
/// <see cref="AggregateType.Multiplicate"/> values include:
/// <see cref="DefenseIncreaseWithEquippedShield"/>.
/// <see cref="AggregateType.AddFinal"/> values include:
/// Greater defense buff; MST bonus defense with shield (shield strengthener); MST dark horse strengthener; Jack O'Lantern Cry bonus (halved).
/// </remarks>
public static AttributeDefinition DefenseFinal { get; } = new(new Guid("0888AD48-0CC8-47CA-B6A3-99F3771AA5FC"), "Final Defense", string.Empty);

/// <summary>
/// Gets the defense PVM attribute definition.
/// </summary>
/// <remarks>
/// <see cref="AggregateType.AddRaw"/> values include:
/// <see cref="DefenseFinal"/>.
/// <see cref="AggregateType.Multiplicate"/> values include:
/// Fire slash defense reduction.
/// </remarks>
public static AttributeDefinition DefensePvm { get; } = new(new Guid("B4201610-2824-4EC1-A145-76B15DB9DEC6"), "Defense (PvM)", string.Empty);

/// <summary>
/// Gets the defense PVP attribute definition.
/// </summary>
/// <remarks>
/// <see cref="AggregateType.AddRaw"/> values include:
/// <see cref="DefenseFinal"/>; pants guardian option (halved).
/// <see cref="AggregateType.Multiplicate"/> values include:
/// Fire slash defense reduction.
/// </remarks>
Comment on lines 562 to +569

Choose a reason for hiding this comment

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

medium

It's great that you've added these remarks to clarify the aggregate types for this attribute. This is very helpful for understanding how the defense is calculated. Consider adding similar remarks to other attribute definitions as well, especially those involved in complex calculations.

public static AttributeDefinition DefensePvp { get; } = new(new Guid("28D14EB7-1049-45BE-A7B7-D5E28E63943B"), "Defense (PvP)", string.Empty);

/// <summary>
/// Gets the defense rate PVM attribute definition.
/// </summary>
/// <remarks>
/// <see cref="AggregateType.AddRaw"/> values include:
/// Base defense rate from agility; MST bonus defense rate with shield (shield mastery).
/// <see cref="AggregateType.Multiplicate"/> values include:
/// Complete set bonus multiplier (+10%); MST PvM defense rate increase multiplier; excellent DR option multiplier.
/// </remarks>

Choose a reason for hiding this comment

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

medium

It's great that you've added these remarks to clarify the aggregate types for this attribute. This is very helpful for understanding how the defense is calculated. Consider adding similar remarks to other attribute definitions as well, especially those involved in complex calculations.

public static AttributeDefinition DefenseRatePvm { get; } = new(new Guid("C520DD2D-1B06-4392-95EE-3C41F33E68DA"), "Defense Rate (PvM)", string.Empty);

/// <summary>
Expand All @@ -553,14 +596,9 @@ public class Stats
public static AttributeDefinition DamageReceiveHorseDecrement { get; } = new(new Guid("041B2811-05C0-49DE-B083-4D1FBD7E6286"), "Damage Receive From Dark Horse Multiplier", string.Empty);

/// <summary>
/// Gets the shield block damage decrement attribute definition.
/// TODO: Usage in a shield skill handler.
/// </summary>
public static AttributeDefinition ShieldBlockDamageDecrement { get; } = new(new Guid("DAC6690B-5922-4446-BCE5-5E701BE62EC1"), "Shield Block Damage Multiplier", string.Empty);

/// <summary>
/// Gets the defense increase with equipped shield attribute definition.
/// Gets the total defense increase with equipped shield attribute definition.
/// </summary>
/// <remarks>Includes the sum of ancient set and socket options.</remarks>
public static AttributeDefinition DefenseIncreaseWithEquippedShield { get; } = new(new Guid("41BCEC8D-A7A8-4930-AB2E-A07D8BF1B86C"), "Defense Increase Multiplier With Equipped Shield", string.Empty);

/// <summary>
Expand All @@ -574,24 +612,29 @@ public class Stats
public static AttributeDefinition SoulBarrierManaTollPerHit { get; } = new(new Guid("25315C15-C884-4B45-9883-9A92693DC455"), "Soul Barrier Mana Toll Per Received Hit", "A mana requirement which must be met for the soul barrier skill decrement.");

/// <summary>
/// Gets the bonus defense (absolute) with an equipped shield attribute definition.
/// Gets the bonus defense (absolute) with an equipped shield MST attribute definition.
/// </summary>
public static AttributeDefinition BonusDefenseWithShield { get; } = new(new Guid("05F08D89-9BC6-4164-9B30-26EFAF4C0E0F"), "Defense Increase Bonus (absolute) With equipped Shield", string.Empty);
public static AttributeDefinition BonusDefenseWithShield { get; } = new(new Guid("05F08D89-9BC6-4164-9B30-26EFAF4C0E0F"), "Defense Increase Bonus (absolute) With equipped Shield (MST)", string.Empty);

/// <summary>
/// Gets the bonus defense (absolute) with an equipped scepter attribute definition.
/// </summary>
public static AttributeDefinition BonusDefenseWithScepter { get; } = new(new Guid("7977FE44-FC22-4A6B-A4E0-BA522FE807DF"), "Defense Increase Bonus (absolute) with equipped Scepter", string.Empty);

/// <summary>
/// Gets the bonus PvM defense rate (absolute) with an equipped shield MST attribute definition.
/// </summary>
public static AttributeDefinition BonusDefenseRateWithShield { get; } = new(new Guid("C8CF9CE1-F7AC-48DE-BFCC-349ABBFB1FCA"), "Defense Rate (PvM) Increase Bonus (absolute) With equipped Shield (MST)", string.Empty);

/// <summary>
/// Gets the bonus defense (absolute) command/leadership divisor with an equipped shield attribute definition.
/// </summary>
public static AttributeDefinition BonusDefenseWithScepterCmdDiv { get; } = new(new Guid("9A3C99C4-4F94-4CD0-8BFC-C8B870CD5FE4"), "Defense Increase Bonus (absolute) with equipped Scepter", string.Empty);

/// <summary>
/// Gets the bonus defense (absolute) with an equipped dark horse attribute definition.
/// Gets the bonus defense (absolute) with an equipped dark horse MST attribute definition.
/// </summary>
public static AttributeDefinition BonusDefenseWithHorse { get; } = new(new Guid("8D22E36C-EB36-47DB-8CE0-9ABD599C533C"), "Defense Increase Bonus (absolute) With equipped Horse", string.Empty);
public static AttributeDefinition BonusDefenseWithHorse { get; } = new(new Guid("8D22E36C-EB36-47DB-8CE0-9ABD599C533C"), "Defense Increase Bonus (absolute) With equipped Horse (MST)", string.Empty);

/// <summary>
/// Gets the 'is shield equipped' attribute definition.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public async ValueTask RequestBuffAsync(Player player)
await player.MagicEffectList.AddEffectAsync(new MagicEffect(
TimeSpan.FromMinutes(60),
BuffEffect,
new MagicEffect.ElementWithTarget(new ConstantElement(50 + (player.Level / 5)), Stats.DefenseBase),
new MagicEffect.ElementWithTarget(new ConstantElement(50 + (player.Level / 5), AggregateType.AddFinal), Stats.DefenseFinal),
new MagicEffect.ElementWithTarget(new ConstantElement(45 + (player.Level / 3)), Stats.BaseDamageBonus))).ConfigureAwait(false);
}

Expand Down
7 changes: 6 additions & 1 deletion src/GameLogic/SkillList.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="SkillList.cs" company="MUnique">
// <copyright file="SkillList.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

Expand Down Expand Up @@ -122,6 +122,11 @@ public bool ContainsSkill(ushort skillId)

private async ValueTask AddItemSkillAsync(Skill skill)
{
if (!skill.QualifiedCharacters.Contains(this._player.SelectedCharacter!.CharacterClass!))
{
return;
}

Copy link
Contributor Author

@ze-dom ze-dom May 12, 2025

Choose a reason for hiding this comment

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

For example, shield defense skill was being listed for DW, elf.
Screenshot from 2025-05-13 14-26-41

var skillEntry = new SkillEntry
{
Skill = skill,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,9 @@ private void AddCommonAttributeRelationships(ICollection<AttributeRelationship>
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalVitality, 1, Stats.BaseVitality));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.TotalEnergy, 1, Stats.BaseEnergy));

attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvm, 1, Stats.DefenseBase));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvp, 1, Stats.DefenseBase));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseFinal, 0.5f, Stats.DefenseBase));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvm, 1, Stats.DefenseFinal));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvp, 1, Stats.DefenseFinal));

attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeedAny, 1, Stats.AttackSpeedByWeapon));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeed, 1, Stats.AttackSpeedAny));
Expand All @@ -113,13 +114,13 @@ private void AddCommonAttributeRelationships(ICollection<AttributeRelationship>
attributeRelationships.Add(this.CreateAttributeRelationship(tempSpeed, -0.5f, Stats.AttackSpeedByWeapon));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.AreTwoWeaponsEquipped, tempSpeed));

attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseBase, Stats.IsShieldEquipped, Stats.BonusDefenseWithShield));

var tempDefense = this.Context.CreateNew<AttributeDefinition>(Guid.NewGuid(), "Temp Defense Bonus multiplier with Shield", string.Empty);
this.GameConfiguration.Attributes.Add(tempDefense);
attributeRelationships.Add(this.CreateAttributeRelationship(tempDefense, Stats.IsShieldEquipped, Stats.DefenseIncreaseWithEquippedShield));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvm, tempDefense, Stats.DefenseBase));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefensePvp, tempDefense, Stats.DefenseBase));
attributeRelationships.Add(this.CreateConditionalRelationship(tempDefense, Stats.IsShieldEquipped, Stats.DefenseIncreaseWithEquippedShield));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseFinal, 1, tempDefense, InputOperator.Add, aggregateType: AggregateType.Multiplicate));

attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, Stats.IsShieldEquipped, Stats.BonusDefenseWithShield, AggregateType.AddFinal));
attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseRatePvm, Stats.IsShieldEquipped, Stats.BonusDefenseRateWithShield));

attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.01f, Stats.IsInSafezone));
if (this.UseClassicPvp)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="ClassDarkKnight.cs" company="MUnique">
// <copyright file="ClassDarkKnight.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

Expand Down Expand Up @@ -96,7 +96,7 @@ protected CharacterClass CreateDarkKnight(CharacterClassNumber number, string na
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 0.5f, Stats.DefenseBase));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name,
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.PhysicalBaseDmg, Stats.IsScepterEquipped, Stats.ScepterBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.RavenBaseDamage, Stats.IsScepterEquipped, Stats.ScepterPetBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefenseBase, Stats.IsScepterEquipped, Stats.BonusDefenseWithScepter));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, Stats.IsHorseEquipped, Stats.BonusDefenseWithHorse, aggregateType: AggregateType.AddFinal));

result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BonusDefenseWithScepter, Stats.BonusDefenseWithScepterCmdDiv, Stats.TotalLeadership));

Expand All @@ -127,7 +128,7 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name,
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 0.5f, Stats.DefenseBase));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="ClassDarkWizard.cs" company="MUnique">
// <copyright file="ClassDarkWizard.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

Expand Down Expand Up @@ -97,7 +97,7 @@ protected CharacterClass CreateDarkWizard(CharacterClassNumber number, string na
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 0.5f, Stats.DefenseBase));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="ClassFairyElf.cs" company="MUnique">
// <copyright file="ClassFairyElf.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

Expand Down Expand Up @@ -93,7 +93,7 @@ protected CharacterClass CreateFairyElf(CharacterClassNumber number, string name
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 0.5f, Stats.DefenseBase));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="ClassMagicGladiator.cs" company="MUnique">
// <copyright file="ClassMagicGladiator.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

Expand Down Expand Up @@ -108,7 +108,7 @@ protected CharacterClass CreateMagicGladiator(CharacterClassNumber number, strin
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1.2f, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 0.5f, Stats.DefenseBase));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f, Stats.DefenseFinal));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShieldTemp, 2f, Stats.TotalLevel, InputOperator.Exponentiate));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumShield, 1f / 30f, Stats.MaximumShieldTemp));

Expand Down
Loading