Skip to content
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

Add dark horse options & major dmg calcs refactoring #586

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

ze-dom
Copy link
Contributor

@ze-dom ze-dom commented Feb 13, 2025

To do:

  • Add Update Plugins

Dev changes:

  • Refactored attack damage and defense calculations 😌.
  • Extended Attribute/PowerUp entities to include a Stage property to allow for more flexibility and better mimic original calculations. The idea is to group elements by stages within the same ComposableAttribute, thereby allowing sum and multiplication on different levels, whereas before, all sum/multiplication values were bundled together (a single stage). See here and here.
  • Added InputOperator.Min and InputOperator.Max for AttributeRelationshipElement.
  • Added MasterSkillDefinition.ExtendsDuration to flag master skills which formula applies to a duration increase (there are several such skills, like Soul Barrier Proficiency).
  • Added Stats.AttackSpeedAny as a bucket to feed both Stats.AttackSpeed and Stats.MagicSpeed for simplification.
  • Added Force/Force Wave logic.

Features:

  • Added Dark Horse options.
  • Added some transformation jewelry and some pet options (but not to chat /item command).
  • Added double item option (physical and wizardry) for MG swords.
  • Added Summoner skill Berserk.
  • Added Weakness magic effect (RF's Killing Blow, and (in the future) Summoner's Weakness).
  • Added missing initial CS skills and items for DL and MG.
  • Added ammo (bolts/arrows) damage increase according to item level.

Fixes:

  • Item options (physical/wizardry damage apply to both min and max dmg).
  • Some character base stats and base stats-related attributes.
  • Sometimes it was not possible to add a MST point due to one of the lower rank skills not meeting level threshold (while another did).
  • Shield item option.

|| masterSkillTargetAttribute == powerUpDef.TargetAttribute)
{
var additionalValue = new SimpleElement(skillEntry.CalculateValue(), skillEntry.Skill.MasterDefinition?.Aggregation ?? powerUp.AggregateType);
var additionalValue = new SimpleElement(masterSkillEntry.CalculateValue(), masterSkillEntry.Skill.MasterDefinition?.Aggregation ?? powerUp.AggregateType);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before, it would always calculate the value from the formula of the skillEntry and not of the one associated with the current master skill definition. I believe the idea initially was to traverse the related master skill definitions and apply each formula. I have tested it this way with Soul Barrier Strengthener/Soul Barrier Proficiency and works.

.Append(new PowerUpWrapper(
new SimpleElement(1, AggregateType.AddRaw),
pet.IsDarkRaven() ? Stats.RavenLevel : Stats.HorseLevel,
this.Attributes)).ToList();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

To trigger the related attributes upon level up. Before, you had to unequip-equip the item.

@@ -85,7 +85,7 @@ int Write()
packet.Rotation = newPlayer.Rotation.ToPacketByte();
packet.HeroState = selectedCharacter.State.Convert();
packet.AttackSpeed = (ushort)(newPlayer.Attributes?[Stats.AttackSpeed] ?? 0);
packet.MagicSpeed = (ushort)(newPlayer.Attributes?[Stats.AttackSpeed] ?? 0); // TODO: Implement MagicSpeed
packet.MagicSpeed = (ushort)(newPlayer.Attributes?[Stats.MagicSpeed] ?? 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.

Leftover?

definition.PossibleOptions.Add(this.CreateRelatedExcellentOption(5, Stats.MaximumCurseBaseDmg, Stats.Level, 1f / 20f, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.AttackSpeedAny, 7, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateExcellentOption(4, Stats.ExcellentWizTwoPercentInc, 1.02f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentCurse));
definition.PossibleOptions.Add(this.CreateRelatedExcellentOption(5, Stats.ExcellentWizBaseDmg, Stats.TotalLevel, 1f / 20f, ItemOptionDefinitionNumbers.ExcellentCurse));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not used. Just to keep up to date.

@@ -253,7 +264,7 @@ protected void CreateWeapon(byte @group, byte number, byte slot, int skillNumber
item.MaximumItemLevel = isAmmunition ? (byte)0 : Constants.MaximumItemLevel;
item.DropsFromMonsters = dropsFromMonsters;
item.SetGuid(item.Group, item.Number);
if (slot == 0 && knightClass > 0 && width == 1)
if (slot == 0 && (knightClass > 0 || magicGladiatorClass > 0) && width == 1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

MG can wield a staff on RH too! 😃

item.BasePowerUpAttributes.Add(maxDamagePowerUp);

var speedPowerUp = this.CreateItemBasePowerUpDefinition(Stats.AttackSpeed, attackSpeed, AggregateType.AddRaw);
var speedPowerUp = this.CreateItemBasePowerUpDefinition(Stats.AttackSpeedByWeapon, attackSpeed, AggregateType.AddRaw);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this OK for 0.95d?

{
if (ragefighterClass == 0 || number < 3)
if (ragefighterClass == 0 || number < 2)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not relevant. Just for accuracy (only numbers 0 and 1 can be equipped by RF).

@@ -60,12 +60,13 @@ internal class SkillsInitializer : SkillsInitializerBase
{ SkillNumber.Heal, MagicEffectNumber.Heal },
{ SkillNumber.Recovery, MagicEffectNumber.ShieldRecover },
{ SkillNumber.InfinityArrow, MagicEffectNumber.InfiniteArrow },
{ SkillNumber.InfinityArrowStr, MagicEffectNumber.InfiniteArrow },
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 removed because it was unnecessary, as the magic effect is inherited from the replaced skill.

@@ -758,24 +762,25 @@ private void InitializeMasterSkillData()
// DL
this.AddMasterSkillDefinition(SkillNumber.FireBurstStreng, SkillNumber.FireBurst, SkillNumber.Undefined, 2, 2, SkillNumber.FireBurst, 20, Formula502);
this.AddMasterSkillDefinition(SkillNumber.ForceWaveStreng, SkillNumber.Force, SkillNumber.Undefined, 2, 2, SkillNumber.Force, 20, Formula632);
this.AddMasterSkillDefinition((SkillNumber)5090, SkillNumber.Force, SkillNumber.Undefined, 2, 2, SkillNumber.ForceWave, 20, Formula632);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one gets the damage from ForceWave. The one above gets it from Force.

if (isExcellentHit)
if (damageType == DamageType.Physical)
{
if (isExcellentHit)
Copy link
Contributor Author

@ze-dom ze-dom Feb 25, 2025

Choose a reason for hiding this comment

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

// double wield => 110% dmg (55% + 55%)
dmg += dmg;
/* There is an ancient set option critical damage subtraction here*/
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

{
dmg = (int)(dmg / 1.5);
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

classicPvpDuelDmgDec = 1;
}

if (isExcellentHit)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if (attacker.Attributes[Stats.IsTwoHandedWeaponEquipped] > 0)
{
dmg += (int)(dmg * attacker.Attributes[Stats.TwoHandedWeaponDamageIncrease]);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

}
else if (isCriticalHit)

dmg += (int)attacker.Attributes[Stats.GreaterDamageBonus];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

{
dmg = baseMinDamage;
dmg -= (int)(dmg * attacker.Attributes[Stats.WeaknessPhysDmgDecrement]); // check if it's a mob attacking
dmg += GetMasterSkillTreePhysicalPassiveDamageBonus(attacker, 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.

bonusDamage = (int)(bonusDamage == 0
? attacker.Attributes[Stats.MaceBonusDamage]
: (bonusDamage + attacker.Attributes[Stats.MaceBonusDamage]) / 2);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

if (damageType == DamageType.Fenrir)
{
classicPvpDuelDmgDec = 1;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

var defenseAttribute = defender.GetDefenseAttribute(attacker);
var defense = (int)defender.Attributes[defenseAttribute];
dmg -= defense;
dmg = (int)(dmg * 0.3);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

}

dmg = Math.Max(dmg, 0);
dmg -= (int)(dmg * defender.Attributes[Stats.ShieldSkillReceiveDecrement]);
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.AttackRatePvm, 6, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.5f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
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.CreateConditionalRelationship(Stats.PhysicalBaseDmg, Stats.IsGloveWeaponEquipped, Stats.GloveWeaponBonusBaseDamage));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.SkillMultiplier, 0.001f, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.VitalitySkillMultiplier, 0.001f, Stats.TotalVitality));
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.FenrirBaseDmg, 1.0f / 5, Stats.BaseStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.BaseAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.BaseVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.BaseEnergy));
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.BaseAttributeValues.Add(this.CreateConstValueAttribute(57, Stats.MaximumHealth));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(7, Stats.MaximumMana));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.SkillMultiplier));

result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.5f, Stats.SkillMultiplier));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -44,25 +57,27 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name,
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseVitality, 18, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.BaseEnergy, 23, true));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 70, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 20, false));
result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 40, 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.

result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 10, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.TotalStrengthAndAgility, Stats.TotalAgility, Stats.TotalStrength, InputOperator.Add));

result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseBase, 1.0f / 3, Stats.TotalAgility));
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.DefenseRatePvm, 0.25f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.1f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 0.5f, Stats.TotalAgility));
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.AttackRatePvp, 0.6f, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.Level));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3.5f, Stats.BaseAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 3, Stats.TotalLevel));
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.MinimumWizBaseDmg, 1.0f / 9, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumWizBaseDmg, 1.0f / 4, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1.0f / 7, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 4, Stats.TotalStrengthAndAgility));
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.MinimumPhysBaseDmg, 1.0f / 7, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1.0f / 4, Stats.TotalStrengthAndAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(statsMinWizAndCurseBaseDmg, 1.0f / 9, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(statsMaxWizAndCurseBaseDmg, 1.0f / 4, Stats.TotalEnergy));
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(berserkerHealthMultiplierDecrement, -0.1f, Stats.BerserkerHealthMultiplierFactor, InputOperator.Minimum)); // At least -10% HP
result.AttributeCombinations.Add(this.CreateAttributeRelationship(isBerserkerBuffed, 1, Stats.BerserkerMinPhysDmgBonus, InputOperator.Minimum));
result.AttributeCombinations.Add(this.CreateConditionalRelationship(berserkerHealthMultiplier, isBerserkerBuffed, berserkerHealthMultiplierDecrement));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, berserkerHealthMultiplier, aggregateType: AggregateType.Multiplicate));
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.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, finalBerserkerManaMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, berserkerHealthMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerManaMultiplier));
result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerHealthMultiplierFactor));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These 0 values I found were necessary because otherwise the magic effect power ups would conflict and not always update.


this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster);

return result;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here is all this mumbo-jumbo in effect:
Screenshot from 2025-02-06 16-52-28

this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.MaximumPhysBaseDmg, ItemOptionDefinitionNumbers.PhysicalAttack));
this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.MaximumWizBaseDmg, ItemOptionDefinitionNumbers.WizardryAttack));
this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.PhysicalBaseDmg, ItemOptionDefinitionNumbers.PhysicalAttack));
this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.WizardryBaseDmg, ItemOptionDefinitionNumbers.WizardryAttack));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

: base((byte)CharacterClassNumber.DarkLord, (ushort)SkillNumber.FireBlast)
{
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

: base((byte)CharacterClassNumber.MagicGladiator, (ushort)SkillNumber.ManaRays)
{
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

: base((byte)CharacterClassNumber.DarkLord, (byte)ItemGroups.Swords, 1, 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.

: base((byte)CharacterClassNumber.MagicGladiator, (byte)ItemGroups.Swords, 1, 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.

maxDmgPerStrengthAndAgility.InputAttribute = Stats.TotalStrengthAndAgility.GetPersistent(this.GameConfiguration);
maxDmgPerStrengthAndAgility.InputOperator = InputOperator.Multiply;
maxDmgPerStrengthAndAgility.InputOperand = 1.0f / 30;
maxPhysPowerUpDefinition.Boost.RelatedValues.Add(maxDmgPerStrengthAndAgility);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

@sven-n sven-n left a comment

Choose a reason for hiding this comment

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

Can you split this into smaller, independent PRs, please? I have only looked at a very small part of the changes and can hardly process them all at once in my head 😂

So far, some changes are very good and some make the code very complicated (damage calc). I'm not sure if that's all neccessary. In the past I tried to keep it all as clean and simple as possible. For example, my goal wasn't to replicate the original code by 100 % if that blows up complexity to another level.
Also, the referenced sources are not original and of a higher season, so we cannot trust on formulas etc. of that alone.

Comment on lines +146 to +155
/// <summary>
/// Gets or sets the aggregate type with which the relationship should effect the target attribute.
/// </summary>
public AggregateType AggregateType { get; set; }

/// <summary>
/// Gets or sets the calculation stage at which the relationship should effect the target attribute.
/// </summary>
public byte Stage { get; set; }

Copy link
Member

Choose a reason for hiding this comment

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

Why are these needed? Which use cases do you try to solve with them?

Comment on lines +50 to +75

/// <summary>
/// The electric spike (DL) skill damage type.
/// </summary>
ElectricSpike = 6,

/// <summary>
/// The dark horse's earthshake skill damage type.
/// </summary>
Earthshake = 7,

/// <summary>
/// The chaotic diseier (DL) skill damage type.
/// </summary>
ChaoticDiseier = 8,

/// <summary>
/// The generic dark lord offensive skill damage type.
/// </summary>
/// <remarks>Any DL skill other than electric spike, chaotic diseier, and earthshake.</remarks>
DarkLordGenericSkill = 9,

/// <summary>
/// The multishot (Elf) skill damage type.
/// </summary>
MultiShot = 10,
Copy link
Member

Choose a reason for hiding this comment

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

No sorry, these are not damage types

/// <summary>
/// Gets the total strength and agility attribute definition.
/// </summary>
public static AttributeDefinition TotalStrengthAndAgility { get; } = new(new Guid("4DFA4E4A-D185-4BCE-952C-5E78A92DC4AF"), "Total Strength and Agility", string.Empty);
Copy link
Member

Choose a reason for hiding this comment

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

Unneeded Stat. You can add an AttributeRelationship for each Strength and Agility

Copy link
Contributor Author

Choose a reason for hiding this comment

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


// Not all character's skills get multiplied (e.g., elf's TripleShot and MultiShot), so default case should be empty.
// Switch cases ordering kept close to source's for reference, with new season (NS) skills included.
switch (skill.Skill?.Number)
Copy link
Member

Choose a reason for hiding this comment

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

Sorry, cannot accept that. I tried to prevent references to specific skills in the code as much as possible.

{
dmg = (int)(dmg * attacker.Attributes[Stats.TwoHandedWeaponDamageIncrease]);
defender.Attributes[Stats.CurrentMana] -= soulBarrierManaToll;
Copy link
Member

Choose a reason for hiding this comment

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

This method shouldn't modify any attributes

Comment on lines +115 to +122
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.HarmonyPhysBaseDmg, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.HarmonyPhysBaseDmg, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.SocketBaseMinDmgBonus, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.SocketBaseMaxDmgBonus, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.ExcellentPhysBaseDmg, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.ExcellentPhysBaseDmg, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.ExcellentPhysTwoPercentInc, aggregateType: AggregateType.Multiplicate, stage: 2));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.ExcellentPhysTwoPercentInc, aggregateType: AggregateType.Multiplicate, stage: 2));
Copy link
Member

Choose a reason for hiding this comment

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

Why all these new Stats?

Copy link
Contributor Author

@ze-dom ze-dom Feb 27, 2025

Choose a reason for hiding this comment

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

Refer to this comment and the Stat code docs near it. Because originally these are all added and multiplied on different stages.
In other words, (((A + B) * C) + D) * E) will produce a different result than (A + B + D) * (C * E). (For reference, I documented this on src/AttributeSystem/IElement.cs.)

I wanted to emulate the original calculations, so this was the rationale for the introduction of stages. Another approach would be to add Stats add infinitum, but that would be even more confusing 😅. But I understand if this may not be acceptable to you.

Comment on lines +123 to +124
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MinimumPhysBaseDmg, 1, Stats.BaseDamageBonus, stage: 4));
attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumPhysBaseDmg, 1, Stats.BaseDamageBonus, stage: 4));
Copy link
Member

Choose a reason for hiding this comment

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

Why stage 4 and not 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the sources, the damage bonus from special item consumables is the last thing added (Stats.BaseDamageBonus here contains JackO'lanternWrath and CherryBlossomFlowerPetal power ups - I left it documented on the Stat docs).

result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 1.0f / 6, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 5, Stats.TotalLevel));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 3, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.AttackRatePvm, 0.25f, Stats.TotalStrength));
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if your linked source is using the original formula here...

Comment on lines -92 to +93
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.TotalAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.TotalVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.TotalLeadership));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.BaseStrength));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 5, Stats.BaseAgility));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 7, Stats.BaseVitality));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.BaseEnergy));
result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.FenrirBaseDmg, 1.0f / 3, Stats.BaseLeadership));
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure this is not based on the total values?

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 mini robot transformation ring option number.
/// </summary>
public static short MiniRobotTransformationRing => 0x76;
Copy link
Member

Choose a reason for hiding this comment

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

Never seen this before in season 6

Copy link
Contributor Author

@ze-dom ze-dom Feb 27, 2025

Choose a reason for hiding this comment

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

That may very well be so, I only added because there was already code in src/Persistence/Initialization/VersionSeasonSix/Items/Jewelery.cs for these.

manaMultiplier.InputAttribute = Stats.BaseEnergy.GetPersistent(this.GameConfiguration); // BaseEnergy and not TotalEnergy
manaMultiplier.InputOperator = InputOperator.Multiply;
manaMultiplier.InputOperand = 1f / 3000f;
manaPowerUpDefinition.Boost.RelatedValues.Add(manaMultiplier);
Copy link
Contributor Author

healthMultiplier.InputAttribute = Stats.BaseEnergy.GetPersistent(this.GameConfiguration); // BaseEnergy and not TotalEnergy
healthMultiplier.InputOperator = InputOperator.Multiply;
healthMultiplier.InputOperand = 1f / 6000f;
healthPowerUpDefinition.Boost.RelatedValues.Add(healthMultiplier);
Copy link
Contributor Author

Choose a reason for hiding this comment

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


// Always a 50 % damage reduction
powerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
powerUpDefinition.Boost.ConstantValue.Value = 0.50f;
powerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate;
powerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

powerUpDefinition.Boost = this.Context.CreateNew<PowerUpDefinitionValue>();
powerUpDefinition.Boost.ConstantValue.Value = 2f;
powerUpDefinition.Boost.ConstantValue.Value = 1f;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@@ -35,7 +35,7 @@ public override void Initialize()
magicEffect.SendDuration = true;
magicEffect.StopByDeath = true;
magicEffect.Duration = this.Context.CreateNew<PowerUpDefinitionValue>();
magicEffect.Duration.ConstantValue.Value = (float)TimeSpan.FromHours(8).TotalSeconds;
magicEffect.Duration.ConstantValue.Value = 600;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.05f;
decDmgPowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw;
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

https://github.com/kyleruss/emu-server/blob/202856a74c905c203b9b2795fd161f564ca8b257/GameServer/Source/ObjUseSkill.cpp#L6214
However, this depends on a chance (10%). I have noticed more RF and Summoner skills also have effects that depend on a chance rate. To be addressed later!

/// The weakness effect, which decreases inflicted damage.
/// </summary>
Weakness = 0x4C,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/// The berserker buff effect.
/// </summary>
Berserker = 0x51,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants