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..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. // @@ -1606,25 +1606,26 @@ 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; if (extendsDuration && !durationExtended) { - durationElement = new CombinedElement(durationElement, new ConstantElement(masterSkillEntry.CalculateValue())); - } - else if (extendsDuration) - { - continue; + var value = masterSkillEntry.CalculateValue(); + if (value < 1) + { + value *= 100; + } + + durationElement = new CombinedElement(durationElement, new ConstantElement(value)); + durationElementPvp = new CombinedElement(durationElementPvp, new ConstantElement(value)); } - 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 +1633,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 +1651,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 +1724,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..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. // @@ -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,10 +244,13 @@ 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 + 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) @@ -256,7 +258,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/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/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/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/Updates/FinishElfMasterTreePlugIn.cs b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs new file mode 100644 index 000000000..d1341831d --- /dev/null +++ b/src/Persistence/Initialization/Updates/FinishElfMasterTreePlugIn.cs @@ -0,0 +1,157 @@ +// +// 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.Persistence.Initialization.VersionSeasonSix; +using MUnique.OpenMU.PlugIns; + +/// +/// 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)] +[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. It also adds the missing extra projectile (4th) on some higher level (cross)bows."; + + /// + public override UpdateVersion Version => UpdateVersion.FinishElfMasterTree; + + /// + public override string DataInitializationKey => DataInitialization.Id; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2026, 7, 17, 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 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)); + 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; + } + + 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) + { + 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 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..e8d1bb464 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} / 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); + 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.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} / 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