From 983db566e6ac315acdcda5d1ac35cdf12d49c8bf Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 16 Jul 2026 11:04:36 +0100 Subject: [PATCH 1/5] Fixed elf master skill s and added extra projectile attribute --- src/GameLogic/AttackableExtensions.cs | 4 +-- .../Attributes/MonsterAttributeHolder.cs | 4 +-- src/GameLogic/Attributes/Stats.cs | 17 +++++++++-- src/GameLogic/Player.cs | 28 +++++-------------- .../Skills/AreaSkillAttackAction.cs | 6 ++-- src/GameLogic/SkillList.cs | 3 +- .../Skills/GreaterDamageEffectInitializer.cs | 12 ++++++-- .../Skills/GreaterDefenseEffectInitializer.cs | 12 ++++++-- .../VersionSeasonSix/Items/Weapons.cs | 5 ++++ .../VersionSeasonSix/SkillsInitializer.cs | 12 ++++---- 10 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index c641dc950..3678a0b20 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -91,7 +91,7 @@ public static async ValueTask CalculateDamageAsync(this IAttacker attac else { var defenseAttribute = defender.GetDefenseAttribute(attacker); - defense = (int)(defender.Attributes[defenseAttribute] * defender.Attributes[Stats.DefenseDecrement]); + defense = (int)((defender.Attributes[defenseAttribute] + defender.Attributes[Stats.GreaterDefenseBonus]) * defender.Attributes[Stats.DefenseDecrement]); if (defense < 0) { defense = 0; @@ -378,7 +378,7 @@ public static async ValueTask ApplyRegenerationAsync(this IAttackable target, Pl if (regeneration != null) { var regenerationValue = player.Attributes.CreateElement(powerUpDefinition); - var value = skillEntry.Level == 0 ? regenerationValue.Value : regenerationValue.Value + skillEntry.CalculateValue(); + var value = regenerationValue.Value + (skillEntry.Level == 0 ? 0 : regenerationValue.Value * skillEntry.CalculateValue() / 100); target.Attributes[regeneration.CurrentAttribute] = Math.Min( target.Attributes[regeneration.CurrentAttribute] + value, target.Attributes[regeneration.MaximumAttribute]); diff --git a/src/GameLogic/Attributes/MonsterAttributeHolder.cs b/src/GameLogic/Attributes/MonsterAttributeHolder.cs index 84ee16ea7..352ec5752 100644 --- a/src/GameLogic/Attributes/MonsterAttributeHolder.cs +++ b/src/GameLogic/Attributes/MonsterAttributeHolder.cs @@ -17,8 +17,8 @@ public class MonsterAttributeHolder : IAttributeSystem new Dictionary> { { Stats.CurrentHealth, m => m.Health }, - { Stats.DefensePvm, m => m.Attributes.GetValueOfAttribute(Stats.DefenseBase) + ((m as Monster)?.SummonedBy?.Attributes?[Stats.SummonedMonsterDefenseIncrease] ?? 0) }, - { Stats.DefensePvp, m => m.Attributes.GetValueOfAttribute(Stats.DefenseBase) + ((m as Monster)?.SummonedBy?.Attributes?[Stats.SummonedMonsterDefenseIncrease] ?? 0) }, + { Stats.DefensePvm, m => m.Attributes.GetValueOfAttribute(Stats.DefenseBase) * (1 + ((m as Monster)?.SummonedBy?.Attributes?[Stats.SummonedMonsterDefenseIncrease] ?? 0)) }, + { Stats.DefensePvp, m => m.Attributes.GetValueOfAttribute(Stats.DefenseBase) * (1 + ((m as Monster)?.SummonedBy?.Attributes?[Stats.SummonedMonsterDefenseIncrease] ?? 0)) }, { Stats.DamageReceiveDecrement, m => 1.0f }, { Stats.AttackDamageIncrease, m => 1.0f }, { Stats.MovementSpeedFactor, m => 1.0f }, diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index fd4825ec4..54dea3fb0 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1,4 +1,4 @@ -// +// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -524,6 +524,12 @@ public class Stats /// public static AttributeDefinition CrossBowMasteryBonusDamage { get; } = new(new Guid("B3AF7F51-6D6B-42FB-B8FF-689D03890F3E"), "Cross Bow Mastery PvP Bonus Damage (MST)", string.Empty); + /// + /// Gets the extra projectiles attribute definition. + /// + /// Can be increased by equipping certain (cross)bows and/or unlocking triple shot mastery. + public static AttributeDefinition ExtraProjectiles { get; } = new(new Guid("B9E4F3C2-A1D5-4B8E-7F2C-6D3A9E1F5C8B"), "Extra Projectiles", "The number of extra projectiles shot with triple shot skill."); + /// /// Gets elf's melee attack mode attribute definition. /// @@ -554,6 +560,11 @@ public class Stats /// public static AttributeDefinition ArcheryMaxDmg { get; } = new(new Guid("EC807B7C-4004-4D13-BED2-326E13F8EFEB"), "Archery Maximum Damage", "The elf's maximum archery damage, which is added to projectile weapon attacks."); + /// + /// Gets the elf's greater defense buff bonus defense attribute definition. + /// + public static AttributeDefinition GreaterDefenseBonus { get; } = new(new Guid("5A7C3E9B-D1F4-4B82-8A6D-2F3E1C9B7A4D"), "Greater Defense Bonus", string.Empty); + /// /// Gets the elf's greater damage buff bonus damage attribute definition. /// @@ -729,7 +740,7 @@ public class Stats public static AttributeDefinition SummonedMonsterHealthIncrease { get; } = new(new Guid("7B0625C8-DA1A-4A5D-BCA5-26AACDA0BDC6"), "Summoned Monster Health Increase %", string.Empty); /// - /// Gets the summoned monster defense increase, absolute. + /// Gets the summoned monster defense increase, percentage. /// public static AttributeDefinition SummonedMonsterDefenseIncrease { get; } = new(new Guid("0D55CFCA-751F-4E66-B327-635576A9A0B3"), "Summoned Monster Defense Increase", string.Empty); @@ -775,7 +786,7 @@ public class Stats /// values include: /// . /// values include: - /// Greater defense buff; MST bonus defense with shield (shield strengthener); MST dark horse strengthener; Jack O'Lantern Cry bonus (halved); Berserker defense reduction. + /// MST bonus defense with shield (shield strengthener); MST dark horse strengthener; Jack O'Lantern Cry bonus (halved); Berserker defense reduction. /// public static AttributeDefinition DefenseFinal { get; } = new(new Guid("0888AD48-0CC8-47CA-B6A3-99F3771AA5FC"), "Final Defense", string.Empty); diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 99c19fd30..650213b89 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1606,11 +1606,9 @@ void AddSkillPowersToResult(ICollection powerUps, ref (Attrib var durationExtended = false; foreach (var powerUpDef in powerUps) { - IElement powerUp; + IElement powerUp = this.Attributes!.CreateElement(powerUpDef); if (skillEntry.Level > 0) { - powerUp = this.Attributes!.CreateElement(powerUpDef); - foreach (var masterSkillEntry in GetMasterSkillEntries(skillEntry)) { var extendsDuration = masterSkillEntry.Skill?.MasterDefinition?.ExtendsDuration ?? false; @@ -1618,13 +1616,9 @@ void AddSkillPowersToResult(ICollection powerUps, ref (Attrib { durationElement = new CombinedElement(durationElement, new ConstantElement(masterSkillEntry.CalculateValue())); } - else if (extendsDuration) - { - continue; - } - else + + if (masterSkillEntry.Skill?.MasterDefinition?.TargetAttribute is not null) { - // Apply either for all, or just for the specified TargetAttribute of the master skill powerUp = AppedMasterSkillPowerUp(masterSkillEntry, powerUpDef, powerUp); } } @@ -1632,10 +1626,6 @@ void AddSkillPowersToResult(ICollection powerUps, ref (Attrib // After the first iteration all possible duration extensions have been applied durationExtended = true; } - else - { - powerUp = this.Attributes!.CreateElement(powerUpDef); - } result[i] = (powerUpDef.TargetAttribute!, powerUp); i++; @@ -1654,15 +1644,12 @@ IEnumerable GetMasterSkillEntries(SkillEntry masterSkillEntry) IElement AppedMasterSkillPowerUp(SkillEntry masterSkillEntry, PowerUpDefinition powerUpDef, IElement powerUp) { - if (masterSkillEntry.Skill?.MasterDefinition is not { } masterSkillDefinition) - { - return powerUp; - } + var masterSkillDefinition = masterSkillEntry.Skill!.MasterDefinition!; - if (masterSkillDefinition.TargetAttribute is { } masterSkillTargetAttribute - && masterSkillTargetAttribute == powerUpDef.TargetAttribute) + if (masterSkillDefinition.TargetAttribute == powerUpDef.TargetAttribute + && masterSkillDefinition.Aggregation == powerUp.AggregateType) { - var additionalValue = new SimpleElement(masterSkillEntry.CalculateValue(), masterSkillEntry.Skill.MasterDefinition?.Aggregation ?? powerUp.AggregateType); + var additionalValue = new SimpleElement(masterSkillEntry.CalculateValue(), masterSkillDefinition.Aggregation); powerUp = new CombinedElement(powerUp, additionalValue); } @@ -1730,7 +1717,6 @@ public async ValueTask CreateSummonedMonsterAsync(MonsterDefinition definition) area.MaximumHealthOverride = (int)monster.Attributes[Stats.MaximumHealth]; area.MaximumHealthOverride += (int)(monster.Attributes[Stats.MaximumHealth] * this.Attributes?[Stats.SummonedMonsterHealthIncrease] ?? 0); - // todo: Stats.SummonedMonsterDefenseIncrease this.Summon = (monster, intelligence); monster.Initialize(); await gameMap.AddAsync(monster).ConfigureAwait(false); diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 7d9ac46d0..37415aad1 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -237,7 +237,6 @@ private async ValueTask PerformAutomaticHitsAsync(Player player, ushort extraTar var minAttacks = areaSkillSettings.MinimumNumberOfHitsPerAttack == 0 ? maxAttacks : areaSkillSettings.MinimumNumberOfHitsPerAttack; var currentDelay = TimeSpan.Zero; - // Order targets by distance to process nearest targets first var orderedTargets = targets.ToList(); FrustumBasedTargetFilter? filter = null; var projectileCount = 1; @@ -245,9 +244,10 @@ private async ValueTask PerformAutomaticHitsAsync(Player player, ushort extraTar if (areaSkillSettings is { UseFrustumFilter: true, ProjectileCount: > 1 }) { + // Order targets by distance to process nearest targets first orderedTargets.Sort((a, b) => player.GetDistanceTo(a).CompareTo(player.GetDistanceTo(b))); filter = FrustumFilters.GetOrAdd(areaSkillSettings, static s => new FrustumBasedTargetFilter(s.FrustumStartWidth, s.FrustumEndWidth, s.FrustumDistance, s.ProjectileCount)); - projectileCount = areaSkillSettings.ProjectileCount; + projectileCount = areaSkillSettings.ProjectileCount + (int)(player.Attributes?[Stats.ExtraProjectiles] ?? 0); attackRounds = 1; // One attack round per projectile extraTarget = orderedTargets.FirstOrDefault(t => t.Id == extraTargetId); @@ -256,7 +256,7 @@ private async ValueTask PerformAutomaticHitsAsync(Player player, ushort extraTar // In this case we just calculate the angle on server side, so that lags // or desynced positions may not have such a big impact var angle = (float)player.Position.GetAngleDegreeTo(extraTarget.Position); - rotation = (byte)((angle / 360.0f) * 256.0f); + rotation = (byte)(angle / 360.0f * 256.0f); } } diff --git a/src/GameLogic/SkillList.cs b/src/GameLogic/SkillList.cs index 8a6a44d90..225f7ee91 100644 --- a/src/GameLogic/SkillList.cs +++ b/src/GameLogic/SkillList.cs @@ -19,8 +19,9 @@ public sealed class SkillList : ISkillList, IDisposable private const ushort DurabilityReduction1FistMasterSkillId = 578; private const short TwistingSlashMasterySkillId = 332; private const short RagefulBlowMasterySkillId = 333; + private const short TripleShotMasterySkillId = 418; - private readonly short[] _castedSkillsWithPassiveBoost = [TwistingSlashMasterySkillId, RagefulBlowMasterySkillId]; + private readonly short[] _castedSkillsWithPassiveBoost = [TwistingSlashMasterySkillId, RagefulBlowMasterySkillId, TripleShotMasterySkillId]; private readonly IDictionary _availableSkills; diff --git a/src/Persistence/Initialization/Skills/GreaterDamageEffectInitializer.cs b/src/Persistence/Initialization/Skills/GreaterDamageEffectInitializer.cs index f864cfc28..ee2de1152 100644 --- a/src/Persistence/Initialization/Skills/GreaterDamageEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/GreaterDamageEffectInitializer.cs @@ -36,11 +36,11 @@ public override void Initialize() magicEffect.StopByDeath = true; magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 60; // 60 Seconds + + // The buff gives 3 + (energy / 7) damage var powerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(powerUpDefinition); powerUpDefinition.TargetAttribute = Stats.GreaterDamageBonus.GetPersistent(this.GameConfiguration); - - // The buff gives 3 + (energy / 7) damage powerUpDefinition.Boost = this.Context.CreateNew(); powerUpDefinition.Boost.ConstantValue.Value = 3f; powerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw; @@ -50,5 +50,13 @@ public override void Initialize() boostPerEnergy.InputOperator = InputOperator.Multiply; boostPerEnergy.InputOperand = 1f / 7f; // one damage per 7 energy powerUpDefinition.Boost.RelatedValues.Add(boostPerEnergy); + + // Placeholder for AttackIncreaseStr and AttackIncreaseMastery master skills + var powerUpDefinition2 = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(powerUpDefinition2); + powerUpDefinition2.TargetAttribute = Stats.GreaterDamageBonus.GetPersistent(this.GameConfiguration); + powerUpDefinition2.Boost = this.Context.CreateNew(); + powerUpDefinition2.Boost.ConstantValue.Value = 1f; + powerUpDefinition2.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/GreaterDefenseEffectInitializer.cs b/src/Persistence/Initialization/Skills/GreaterDefenseEffectInitializer.cs index 903b742f2..c48616376 100644 --- a/src/Persistence/Initialization/Skills/GreaterDefenseEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/GreaterDefenseEffectInitializer.cs @@ -40,15 +40,23 @@ public override void Initialize() // The buff gives 2 + (energy / 8) defense var powerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(powerUpDefinition); - powerUpDefinition.TargetAttribute = Stats.DefenseFinal.GetPersistent(this.GameConfiguration); + powerUpDefinition.TargetAttribute = Stats.GreaterDefenseBonus.GetPersistent(this.GameConfiguration); powerUpDefinition.Boost = this.Context.CreateNew(); powerUpDefinition.Boost.ConstantValue.Value = 2f; - powerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddFinal; + powerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddRaw; var boostPerEnergy = this.Context.CreateNew(); boostPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); boostPerEnergy.InputOperator = InputOperator.Multiply; boostPerEnergy.InputOperand = 1f / 8f; // one defense per 8 energy powerUpDefinition.Boost.RelatedValues.Add(boostPerEnergy); + + // Placeholder for DefenseIncreaseStr and DefenseIncreaseMastery master skills + var powerUpDefinition2 = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(powerUpDefinition2); + powerUpDefinition2.TargetAttribute = Stats.GreaterDefenseBonus.GetPersistent(this.GameConfiguration); + powerUpDefinition2.Boost = this.Context.CreateNew(); + powerUpDefinition2.Boost.ConstantValue.Value = 1f; + powerUpDefinition2.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/Weapons.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/Weapons.cs index 8979d3aad..9943d625d 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/Weapons.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/Weapons.cs @@ -457,6 +457,11 @@ protected void CreateWeapon( { item.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(Stats.AmmunitionConsumptionRate, 1, AggregateType.AddRaw)); item.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(slot == 0 ? Stats.IsCrossBowEquipped : Stats.IsBowEquipped, 1, AggregateType.AddRaw)); + + if (number == 18 || number == 19 || number == 22 || number == 23 || number == 24) // Divine Crossbow of Archangel, Great Reign Crossbow, Albatross Bow, Stinger Bow, Air Lyn Bow + { + item.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(Stats.ExtraProjectiles, 1, AggregateType.AddRaw)); + } } if (group < (int)ItemGroups.Bows && width == 2) diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 029d95267..53a5790c3 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -818,7 +818,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.TwoHandedStaffMaster, Stats.TwoHandedStaffMasteryBonusDamage, AggregateType.AddRaw, Formula1154, 3, 3, SkillNumber.TwoHandedStaffStrengthener); this.AddPassiveMasterSkillDefinition(SkillNumber.ShieldMasteryGrandMaster, Stats.BonusDefenseRateWithShield, AggregateType.AddRaw, Formula1204, 3, 3, SkillNumber.ShieldStrengthenerGrandMaster); this.AddMasterSkillDefinition(SkillNumber.SoulBarrierStrength, SkillNumber.SoulBarrier, SkillNumber.Undefined, 3, 4, SkillNumber.SoulBarrier, 20, $"{Formula181} / 100", Formula181, Stats.SoulBarrierReceiveDecrement, AggregateType.AddRaw); - this.AddMasterSkillDefinition(SkillNumber.SoulBarrierProficie, SkillNumber.SoulBarrierStrength, SkillNumber.Undefined, 3, 5, SkillNumber.SoulBarrierStrength, 20, Formula803, extendsDuration: true); + this.AddMasterSkillDefinition(SkillNumber.SoulBarrierProficie, SkillNumber.SoulBarrierStrength, SkillNumber.Undefined, 3, 5, SkillNumber.SoulBarrierStrength, 20, Formula803, true); this.AddPassiveMasterSkillDefinition(SkillNumber.MinimumWizardryInc, Stats.MinimumWizBaseDmg, AggregateType.AddRaw, Formula502, 5, 3); // ELF @@ -826,13 +826,13 @@ private void InitializeMasterSkillData() this.AddMasterSkillDefinition(SkillNumber.TripleShotStrengthener, SkillNumber.TripleShot, SkillNumber.Undefined, 2, 2, SkillNumber.TripleShot, 20, Formula502); this.AddPassiveMasterSkillDefinition(SkillNumber.SummonedMonsterStr1, Stats.SummonedMonsterHealthIncrease, AggregateType.AddRaw, Formula6020Value, 2, 2, SkillNumber.SummonGoblin); this.AddMasterSkillDefinition(SkillNumber.PenetrationStrengthener, SkillNumber.Penetration, SkillNumber.Undefined, 2, 3, SkillNumber.Penetration, 20, Formula502); - this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseStr, SkillNumber.GreaterDefense, SkillNumber.Undefined, 2, 3, SkillNumber.GreaterDefense, 20, Formula502); - this.AddMasterSkillDefinition(SkillNumber.TripleShotMastery, SkillNumber.TripleShotStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.TripleShot, 10, Formula1WhenComplete); + this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseStr, SkillNumber.GreaterDefense, SkillNumber.Undefined, 2, 3, SkillNumber.GreaterDefense, 20, Formula502, Formula502, Stats.GreaterDefenseBonus, AggregateType.Multiplicate); + this.AddMasterSkillDefinition(SkillNumber.TripleShotMastery, SkillNumber.TripleShotStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.TripleShotStrengthener, 10, Formula1WhenComplete, Formula1WhenComplete, Stats.ExtraProjectiles, AggregateType.AddRaw); this.AddPassiveMasterSkillDefinition(SkillNumber.SummonedMonsterStr2, Stats.SummonedMonsterDefenseIncrease, AggregateType.AddRaw, Formula6020, 2, 3, SkillNumber.SummonGoblin); - this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseStr, SkillNumber.GreaterDamage, SkillNumber.Undefined, 2, 4, SkillNumber.GreaterDamage, 20, Formula502); + this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseStr, SkillNumber.GreaterDamage, SkillNumber.Undefined, 2, 4, SkillNumber.GreaterDamage, 20, Formula502, Formula502, Stats.GreaterDamageBonus, AggregateType.Multiplicate); this.AddPassiveMasterSkillDefinition(SkillNumber.WeaponMasteryHighElf, Stats.MasterSkillPhysBonusDmg, AggregateType.AddRaw, Formula502, 4, 2); - this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseMastery, SkillNumber.AttackIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.GreaterDamage, 20, Formula502); - this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseMastery, SkillNumber.DefenseIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.GreaterDefense, 20, Formula502); + this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseMastery, SkillNumber.AttackIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.AttackIncreaseStr, 20, Formula502, Formula502, Stats.GreaterDamageBonus, AggregateType.Multiplicate, true); + this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseMastery, SkillNumber.DefenseIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.DefenseIncreaseStr, 20, Formula502, Formula502, Stats.GreaterDefenseBonus, AggregateType.Multiplicate, true); this.AddMasterSkillDefinition(SkillNumber.IceArrowStrengthener, SkillNumber.IceArrow, SkillNumber.Undefined, 2, 5, SkillNumber.IceArrow, 20, Formula502); this.AddPassiveMasterSkillDefinition(SkillNumber.BowStrengthener, Stats.BowStrBonusDamage, AggregateType.AddRaw, Formula502, 2, 3); this.AddPassiveMasterSkillDefinition(SkillNumber.CrossbowStrengthener, Stats.CrossBowStrBonusDamage, AggregateType.AddRaw, Formula632, 2, 3); From daf72d784757ca60a05fc1c897f182a5b82f7665 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 16 Jul 2026 16:34:25 +0100 Subject: [PATCH 2/5] Added updateplugin --- .../Updates/FinishElfMasterTreePlugIn.cs | 142 ++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 9 +- 2 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs diff --git a/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs new file mode 100644 index 000000000..6424ac081 --- /dev/null +++ b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs @@ -0,0 +1,142 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel.Attributes; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.DataModel.Configuration.Items; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.Persistence.Initialization.Items; +using MUnique.OpenMU.Persistence.Initialization.Skills; +using MUnique.OpenMU.PlugIns; + +/// +/// This update completes the elf master tree and fixes some of its skill values. +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("D1E2F3A4-B5C6-7D8E-9F0A-1B2C3D4E5F6A")] +public class FinishElfMasterTreePlugIn : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Finish Elf Master Tree PlugIn"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This update completes the elf master tree and fixes some of its skill values."; + + /// + public override UpdateVersion Version => UpdateVersion.FinishElfMasterTree; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2026, 7, 16, 16, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + // Create new Stats + var extraProjectiles = context.CreateNew(Stats.ExtraProjectiles.Id, Stats.ExtraProjectiles.Designation, Stats.ExtraProjectiles.Description); + gameConfiguration.Attributes.Add(extraProjectiles); + var greaterDefenseBonus = context.CreateNew(Stats.GreaterDefenseBonus.Id, Stats.GreaterDefenseBonus.Designation, Stats.GreaterDefenseBonus.Description); + gameConfiguration.Attributes.Add(greaterDefenseBonus); + + var greaterDamageBonus = Stats.GreaterDamageBonus.GetPersistent(gameConfiguration); + + // Update Greater Damage effect + var greaterDamageEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.GreaterDamage); + + var greaterDamagePowerUp2 = context.CreateNew(); + greaterDamageEffect.PowerUpDefinitions.Add(greaterDamagePowerUp2); + greaterDamagePowerUp2.TargetAttribute = greaterDamageBonus; + greaterDamagePowerUp2.Boost = context.CreateNew(); + greaterDamagePowerUp2.Boost.ConstantValue.Value = 1f; + greaterDamagePowerUp2.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + // Update Greater Defense effect + var greaterDefenseEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.GreaterDefense); + + if (greaterDefenseEffect.PowerUpDefinitions.FirstOrDefault() is PowerUpDefinition defensePowerUp) + { + defensePowerUp.TargetAttribute = greaterDefenseBonus; + defensePowerUp.Boost?.ConstantValue.AggregateType = AggregateType.AddRaw; + } + + var greaterDefensePowerUp2 = context.CreateNew(); + greaterDefenseEffect.PowerUpDefinitions.Add(greaterDefensePowerUp2); + greaterDefensePowerUp2.TargetAttribute = greaterDefenseBonus; + greaterDefensePowerUp2.Boost = context.CreateNew(); + greaterDefensePowerUp2.Boost.ConstantValue.Value = 1f; + greaterDefensePowerUp2.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + // Update 4-arrow (cross)bows + var affectedBows = new short[] { 18, 19, 22, 23, 24 }; + var bows = gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Bows && affectedBows.Contains(i.Number)); + foreach (var bow in bows) + { + bow.BasePowerUpAttributes.Add(this.CreateItemBasePowerUpDefinition(context, gameConfiguration, extraProjectiles, 1, AggregateType.AddRaw)); + } + + // Update master skills + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DefenseIncreaseStr)?.MasterDefinition is { } defenseIncreaseStr) + { + defenseIncreaseStr.TargetAttribute = greaterDefenseBonus; + defenseIncreaseStr.Aggregation = AggregateType.Multiplicate; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.TripleShotStrengthener)?.MasterDefinition is { } tripleShotStrengthener) + { + tripleShotStrengthener.TargetAttribute = extraProjectiles; + tripleShotStrengthener.Aggregation = AggregateType.AddRaw; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.AttackIncreaseStr)?.MasterDefinition is { } attackIncreaseStr) + { + attackIncreaseStr.TargetAttribute = greaterDamageBonus; + attackIncreaseStr.Aggregation = AggregateType.Multiplicate; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.AttackIncreaseMastery)?.MasterDefinition is { } attackIncreaseMastery) + { + attackIncreaseMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.AttackIncreaseStr); + attackIncreaseMastery.TargetAttribute = greaterDamageBonus; + attackIncreaseMastery.Aggregation = AggregateType.Multiplicate; + attackIncreaseMastery.ExtendsDuration = true; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DefenseIncreaseMastery)?.MasterDefinition is { } defenseIncreaseMastery) + { + defenseIncreaseMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.DefenseIncreaseStr); + defenseIncreaseMastery.TargetAttribute = greaterDefenseBonus; + defenseIncreaseMastery.Aggregation = AggregateType.Multiplicate; + defenseIncreaseMastery.ExtendsDuration = true; + } + } + + private ItemBasePowerUpDefinition CreateItemBasePowerUpDefinition(IContext context, GameConfiguration gameConfiguration, AttributeDefinition attributeDefinition, float value, AggregateType aggregateType) + { + var powerUpDefinition = context.CreateNew(); + powerUpDefinition.TargetAttribute = attributeDefinition.GetPersistent(gameConfiguration); + powerUpDefinition.BaseValue = value; + powerUpDefinition.AggregateType = aggregateType; + return powerUpDefinition; + } +} diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index fe017472e..9e187a460 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -479,14 +479,19 @@ public enum UpdateVersion /// The version of the . /// AddIsQuestItemFlag = 94, - + /// /// The version of the . /// AddElfSoldierBuff = 95, - + /// /// The version of the . /// FinishDarkWizardMasterTree = 96, + + /// + /// The version of the . + /// + FinishElfMasterTree = 97, } \ No newline at end of file From 60fc09689d498c6d2fca94a0ef00bdbfa3027042 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 16 Jul 2026 16:41:14 +0100 Subject: [PATCH 3/5] Improved plugin description --- .../Initialization/Updates/FinishElfMasterTreePlugIn.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs index 6424ac081..1d4e2944f 100644 --- a/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs +++ b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs @@ -15,7 +15,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using MUnique.OpenMU.PlugIns; /// -/// This update completes the elf master tree and fixes some of its skill values. +/// This update completes the elf master tree and fixes some of its skill values. It also adds the missing extra projectile (4th) on some higher level (cross)bows. /// [PlugIn] [Display(Name = PlugInName, Description = PlugInDescription)] @@ -30,7 +30,7 @@ public class FinishElfMasterTreePlugIn : UpdatePlugInBase /// /// The plug in description. /// - internal const string PlugInDescription = "This update completes the elf master tree and fixes some of its skill values."; + internal const string PlugInDescription = "This update completes the elf master tree and fixes some of its skill values. It also adds the missing extra projectile (4th) on some higher level (cross)bows."; /// public override UpdateVersion Version => UpdateVersion.FinishElfMasterTree; From 2f552938d7057b09210e8fc77ff641b304b7f3f7 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 17 Jul 2026 17:06:40 +0100 Subject: [PATCH 4/5] Fixed durations and extra projectiles logic --- src/GameLogic/Player.cs | 11 +++++++++-- .../PlayerActions/Skills/AreaSkillAttackAction.cs | 6 ++++-- .../Skills/SoulBarrierStrengSkillAction.cs | 2 ++ .../Skills/InfiniteArrowEffectInitializer.cs | 2 ++ .../VersionSeasonSix/SkillsInitializer.cs | 10 +++++----- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 650213b89..20de13d08 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1,4 +1,4 @@ -// +// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -1614,7 +1614,14 @@ void AddSkillPowersToResult(ICollection powerUps, ref (Attrib var extendsDuration = masterSkillEntry.Skill?.MasterDefinition?.ExtendsDuration ?? false; if (extendsDuration && !durationExtended) { - durationElement = new CombinedElement(durationElement, new ConstantElement(masterSkillEntry.CalculateValue())); + var value = masterSkillEntry.CalculateValue(); + if (value < 1) + { + value *= 100; + } + + durationElement = new CombinedElement(durationElement, new ConstantElement(value)); + durationElementPvp = new CombinedElement(durationElementPvp, new ConstantElement(value)); } if (masterSkillEntry.Skill?.MasterDefinition?.TargetAttribute is not null) diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 37415aad1..f5bbe746b 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -1,4 +1,4 @@ -// +// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -246,9 +246,11 @@ private async ValueTask PerformAutomaticHitsAsync(Player player, ushort extraTar { // Order targets by distance to process nearest targets first orderedTargets.Sort((a, b) => player.GetDistanceTo(a).CompareTo(player.GetDistanceTo(b))); - filter = FrustumFilters.GetOrAdd(areaSkillSettings, static s => new FrustumBasedTargetFilter(s.FrustumStartWidth, s.FrustumEndWidth, s.FrustumDistance, s.ProjectileCount)); projectileCount = areaSkillSettings.ProjectileCount + (int)(player.Attributes?[Stats.ExtraProjectiles] ?? 0); attackRounds = 1; // One attack round per projectile + filter = FrustumFilters.GetOrAdd(areaSkillSettings, s => new FrustumBasedTargetFilter(s.FrustumStartWidth, s.FrustumEndWidth, s.FrustumDistance, projectileCount)); + minAttacks = projectileCount; + maxAttacks = projectileCount; extraTarget = orderedTargets.FirstOrDefault(t => t.Id == extraTargetId); if (extraTarget is not null) diff --git a/src/GameLogic/PlayerActions/Skills/SoulBarrierStrengSkillAction.cs b/src/GameLogic/PlayerActions/Skills/SoulBarrierStrengSkillAction.cs index a02104873..1bcf58ec5 100644 --- a/src/GameLogic/PlayerActions/Skills/SoulBarrierStrengSkillAction.cs +++ b/src/GameLogic/PlayerActions/Skills/SoulBarrierStrengSkillAction.cs @@ -86,6 +86,8 @@ public override async ValueTask PerformSkillAsync(Player player, IAttackable tar new ConstantElement(strengSkillLevel!.Level * 0.001f), // extra 0.1% per streng skill level InputOperator.Multiply)) ]; + + skillEntry.PowerUpsPvp = skillEntry.PowerUps; } await AttackableExtensions.ApplyMagicEffectAsync(target, player, skillEntry).ConfigureAwait(false); diff --git a/src/Persistence/Initialization/Skills/InfiniteArrowEffectInitializer.cs b/src/Persistence/Initialization/Skills/InfiniteArrowEffectInitializer.cs index ae3a31ecf..88eb63543 100644 --- a/src/Persistence/Initialization/Skills/InfiniteArrowEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InfiniteArrowEffectInitializer.cs @@ -50,5 +50,7 @@ public override void Initialize() magicEffect.PowerUpDefinitions.Add(damageIncreaseByMasterSkill); damageIncreaseByMasterSkill.TargetAttribute = Stats.AttackDamageIncrease.GetPersistent(this.GameConfiguration); damageIncreaseByMasterSkill.Boost = this.Context.CreateNew(); + damageIncreaseByMasterSkill.Boost.ConstantValue.Value = 1f; + damageIncreaseByMasterSkill.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 53a5790c3..e8d1bb464 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -826,13 +826,13 @@ private void InitializeMasterSkillData() this.AddMasterSkillDefinition(SkillNumber.TripleShotStrengthener, SkillNumber.TripleShot, SkillNumber.Undefined, 2, 2, SkillNumber.TripleShot, 20, Formula502); this.AddPassiveMasterSkillDefinition(SkillNumber.SummonedMonsterStr1, Stats.SummonedMonsterHealthIncrease, AggregateType.AddRaw, Formula6020Value, 2, 2, SkillNumber.SummonGoblin); this.AddMasterSkillDefinition(SkillNumber.PenetrationStrengthener, SkillNumber.Penetration, SkillNumber.Undefined, 2, 3, SkillNumber.Penetration, 20, Formula502); - this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseStr, SkillNumber.GreaterDefense, SkillNumber.Undefined, 2, 3, SkillNumber.GreaterDefense, 20, Formula502, Formula502, Stats.GreaterDefenseBonus, AggregateType.Multiplicate); + this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseStr, SkillNumber.GreaterDefense, SkillNumber.Undefined, 2, 3, SkillNumber.GreaterDefense, 20, $"{Formula502} / 100", Formula502, Stats.GreaterDefenseBonus, AggregateType.Multiplicate); this.AddMasterSkillDefinition(SkillNumber.TripleShotMastery, SkillNumber.TripleShotStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.TripleShotStrengthener, 10, Formula1WhenComplete, Formula1WhenComplete, Stats.ExtraProjectiles, AggregateType.AddRaw); this.AddPassiveMasterSkillDefinition(SkillNumber.SummonedMonsterStr2, Stats.SummonedMonsterDefenseIncrease, AggregateType.AddRaw, Formula6020, 2, 3, SkillNumber.SummonGoblin); - this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseStr, SkillNumber.GreaterDamage, SkillNumber.Undefined, 2, 4, SkillNumber.GreaterDamage, 20, Formula502, Formula502, Stats.GreaterDamageBonus, AggregateType.Multiplicate); + this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseStr, SkillNumber.GreaterDamage, SkillNumber.Undefined, 2, 4, SkillNumber.GreaterDamage, 20, $"{Formula502} / 100", Formula502, Stats.GreaterDamageBonus, AggregateType.Multiplicate); this.AddPassiveMasterSkillDefinition(SkillNumber.WeaponMasteryHighElf, Stats.MasterSkillPhysBonusDmg, AggregateType.AddRaw, Formula502, 4, 2); - this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseMastery, SkillNumber.AttackIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.AttackIncreaseStr, 20, Formula502, Formula502, Stats.GreaterDamageBonus, AggregateType.Multiplicate, true); - this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseMastery, SkillNumber.DefenseIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.DefenseIncreaseStr, 20, Formula502, Formula502, Stats.GreaterDefenseBonus, AggregateType.Multiplicate, true); + this.AddMasterSkillDefinition(SkillNumber.AttackIncreaseMastery, SkillNumber.AttackIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.AttackIncreaseStr, 20, $"{Formula502} / 100", Formula502, Stats.GreaterDamageBonus, AggregateType.Multiplicate, true); + this.AddMasterSkillDefinition(SkillNumber.DefenseIncreaseMastery, SkillNumber.DefenseIncreaseStr, SkillNumber.Undefined, 2, 5, SkillNumber.DefenseIncreaseStr, 20, $"{Formula502} / 100", Formula502, Stats.GreaterDefenseBonus, AggregateType.Multiplicate, true); this.AddMasterSkillDefinition(SkillNumber.IceArrowStrengthener, SkillNumber.IceArrow, SkillNumber.Undefined, 2, 5, SkillNumber.IceArrow, 20, Formula502); this.AddPassiveMasterSkillDefinition(SkillNumber.BowStrengthener, Stats.BowStrBonusDamage, AggregateType.AddRaw, Formula502, 2, 3); this.AddPassiveMasterSkillDefinition(SkillNumber.CrossbowStrengthener, Stats.CrossBowStrBonusDamage, AggregateType.AddRaw, Formula632, 2, 3); @@ -840,7 +840,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.BowMastery, Stats.WeaponMasteryAttackSpeed, AggregateType.AddRaw, Formula1, 3, 3, SkillNumber.BowStrengthener, SkillNumber.Undefined, 10); this.AddPassiveMasterSkillDefinition(SkillNumber.CrossbowMastery, Stats.CrossBowMasteryBonusDamage, AggregateType.AddRaw, Formula1154, 3, 3, SkillNumber.CrossbowStrengthener); this.AddPassiveMasterSkillDefinition(SkillNumber.ShieldMasteryHighElf, Stats.BonusDefenseRateWithShield, AggregateType.AddRaw, Formula1806, 3, 3, SkillNumber.ShieldStrengthenerHighElf); - this.AddMasterSkillDefinition(SkillNumber.InfinityArrowStr, SkillNumber.InfinityArrow, SkillNumber.Undefined, 3, 5, SkillNumber.InfinityArrow, 20, $"1 + {Formula120} / 100", Formula120, Stats.AttackDamageIncrease, AggregateType.Multiplicate); + this.AddMasterSkillDefinition(SkillNumber.InfinityArrowStr, SkillNumber.InfinityArrow, SkillNumber.Undefined, 3, 5, SkillNumber.InfinityArrow, 20, $"{Formula120} / 100", Formula120, Stats.AttackDamageIncrease, AggregateType.Multiplicate); this.AddPassiveMasterSkillDefinition(SkillNumber.MinimumAttPowerInc, Stats.MinimumPhysBaseDmg, AggregateType.AddRaw, Formula502, 5, 3); // SUM From 681b0e70e2d22204adba493c0b38369994e8afcc Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 17 Jul 2026 17:23:07 +0100 Subject: [PATCH 5/5] Updated updateplugin --- .../Updates/FinishElfMasterTreePlugIn.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs index 1d4e2944f..d1341831d 100644 --- a/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs +++ b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs @@ -12,6 +12,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.Persistence.Initialization.Items; using MUnique.OpenMU.Persistence.Initialization.Skills; +using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix; using MUnique.OpenMU.PlugIns; /// @@ -36,7 +37,7 @@ public class FinishElfMasterTreePlugIn : UpdatePlugInBase public override UpdateVersion Version => UpdateVersion.FinishElfMasterTree; /// - public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + public override string DataInitializationKey => DataInitialization.Id; /// public override string Name => PlugInName; @@ -48,7 +49,7 @@ public class FinishElfMasterTreePlugIn : UpdatePlugInBase public override bool IsMandatory => true; /// - public override DateTime CreatedAt => new(2026, 7, 16, 16, 0, 0, DateTimeKind.Utc); + public override DateTime CreatedAt => new(2026, 7, 17, 16, 0, 0, DateTimeKind.Utc); /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) @@ -87,6 +88,15 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio greaterDefensePowerUp2.Boost.ConstantValue.Value = 1f; greaterDefensePowerUp2.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + // Update Infinity Arrow effect + var infinityArrowEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.InfiniteArrow); + + if (infinityArrowEffect.PowerUpDefinitions.FirstOrDefault(pud => pud.TargetAttribute == Stats.AttackDamageIncrease) is PowerUpDefinition infinityArrowPowerUp) + { + infinityArrowPowerUp.Boost?.ConstantValue.Value = 1; + infinityArrowPowerUp.Boost?.ConstantValue.AggregateType = AggregateType.Multiplicate; + } + // Update 4-arrow (cross)bows var affectedBows = new short[] { 18, 19, 22, 23, 24 }; var bows = gameConfiguration.Items.Where(i => i.Group == (byte)ItemGroups.Bows && affectedBows.Contains(i.Number)); @@ -129,6 +139,11 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio defenseIncreaseMastery.Aggregation = AggregateType.Multiplicate; defenseIncreaseMastery.ExtendsDuration = true; } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.InfinityArrowStr)?.MasterDefinition is { } infinityArrowStr) + { + infinityArrowStr.ValueFormula = $"{SkillsInitializer.Formula120} / 100"; + } } private ItemBasePowerUpDefinition CreateItemBasePowerUpDefinition(IContext context, GameConfiguration gameConfiguration, AttributeDefinition attributeDefinition, float value, AggregateType aggregateType)