// // 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.GameLogic.Attributes; using MUnique.OpenMU.Persistence.Initialization.Skills; using MUnique.OpenMU.PlugIns; /// /// This update completes the dark lord master tree skills and effects. /// [PlugIn] [Display(Name = PlugInName, Description = PlugInDescription)] [Guid("1A2B3C4D-5E6F-7890-ABCD-EF1234567890")] public class FinishDarkLordMasterTreePlugIn : UpdatePlugInBase { /// /// The plug in name. /// internal const string PlugInName = "Finish Dark Lord Master Tree PlugIn"; /// /// The plug in description. /// internal const string PlugInDescription = "This update completes the dark lord master tree skills and effects."; /// public override UpdateVersion Version => UpdateVersion.FinishDarkLordMasterTree; /// 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, 4, 14, 16, 0, 0, DateTimeKind.Utc); /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { // Create Critical Damage Increase Mastery Skill Effect var critDmgIncMasteryEffect = this.CreateCritDmgIncMasteryEffect(context, gameConfiguration); // Update Stunned effect var stunnedEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.Stunned); stunnedEffect.Duration = context.CreateNew(); stunnedEffect.Duration.ConstantValue.Value = 2; var stunChancePowerUpDefinition = context.CreateNew(); stunnedEffect.PowerUpDefinitions.Add(stunChancePowerUpDefinition); stunChancePowerUpDefinition.TargetAttribute = Stats.MasteryStunChance.GetPersistent(gameConfiguration); stunChancePowerUpDefinition.Boost = context.CreateNew(); stunChancePowerUpDefinition.Boost.ConstantValue.Value = 0; // Map skills to effects this.MapSkillToEffect(gameConfiguration, SkillNumber.FireBurstMastery, stunnedEffect); this.MapSkillToEffect(gameConfiguration, SkillNumber.EarthshakeMastery, stunnedEffect); this.MapSkillToEffect(gameConfiguration, SkillNumber.CritDmgIncPowUp3, critDmgIncMasteryEffect); // Update AreaSkillSettings this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.Earthshake, false, 0, 0, 0, useTargetAreaFilter: true, targetAreaDiameter: 10, minimumHitsPerAttack: 9, maximumHitsPerAttack: 15); this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.EarthshakeStreng, false, 0, 0, 0, useTargetAreaFilter: true, targetAreaDiameter: 10, minimumHitsPerAttack: 9, maximumHitsPerAttack: 15); this.AddAreaSkillSettings(gameConfiguration, context, SkillNumber.EarthshakeMastery, false, 0, 0, 0, useTargetAreaFilter: true, targetAreaDiameter: 10, minimumHitsPerAttack: 9, maximumHitsPerAttack: 15); if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChaoticDiseier) is { } chaoticDiseier) { chaoticDiseier.AreaSkillSettings!.MinimumNumberOfHitsPerAttack = 7; } // Update master skills if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CriticalDmgIncPowUp)?.MasterDefinition is { } fireTomeMastery) { fireTomeMastery.TargetAttribute = Stats.CriticalDamageBonus.GetPersistent(gameConfiguration); fireTomeMastery.Aggregation = AggregateType.AddRaw; } if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.FireBurstMastery)?.MasterDefinition is { } fireBurstMastery) { fireBurstMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.FireBurstStreng); fireBurstMastery.TargetAttribute = Stats.MasteryStunChance.GetPersistent(gameConfiguration); fireBurstMastery.Aggregation = AggregateType.AddRaw; fireBurstMastery.ValueFormula = $"{fireBurstMastery.ValueFormula} / 100"; } if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CritDmgIncPowUp2)?.MasterDefinition is { } critDmgIncPowUp2) { critDmgIncPowUp2.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.CriticalDmgIncPowUp); critDmgIncPowUp2.ExtendsDuration = true; } if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.EarthshakeMastery)?.MasterDefinition is { } earthshakeMastery) { earthshakeMastery.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.EarthshakeStreng); earthshakeMastery.TargetAttribute = Stats.MasteryStunChance.GetPersistent(gameConfiguration); earthshakeMastery.Aggregation = AggregateType.AddRaw; earthshakeMastery.ValueFormula = $"{earthshakeMastery.ValueFormula} / 100"; } if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.CritDmgIncPowUp3)?.MasterDefinition is { } critDmgIncPowUp3) { critDmgIncPowUp3.ReplacedSkill = gameConfiguration.Skills.First(s => s.Number == (short)SkillNumber.CritDmgIncPowUp2); critDmgIncPowUp3.TargetAttribute = Stats.CriticalDamageChance.GetPersistent(gameConfiguration); critDmgIncPowUp3.Aggregation = AggregateType.AddRaw; critDmgIncPowUp3.ValueFormula = $"{critDmgIncPowUp3.ValueFormula} / 100"; } } private MagicEffectDefinition CreateCritDmgIncMasteryEffect(IContext context, GameConfiguration gameConfiguration) { var magicEffect = context.CreateNew(); gameConfiguration.MagicEffects.Add(magicEffect); magicEffect.Number = (byte)MagicEffectNumber.CriticalDamageIncreaseMastery; magicEffect.Name = "Critical Damage Increase Mastery Skill Effect"; var critDmgIncEffect = gameConfiguration.MagicEffects.First(e => e.Number == (short)MagicEffectNumber.CriticalDamageIncrease); critDmgIncEffect.Duration?.MaximumValue = 180; critDmgIncEffect.SubType = 17; magicEffect.InformObservers = critDmgIncEffect.InformObservers; magicEffect.SubType = critDmgIncEffect.SubType; magicEffect.SendDuration = critDmgIncEffect.SendDuration; magicEffect.StopByDeath = critDmgIncEffect.StopByDeath; magicEffect.Duration = context.CreateNew(); magicEffect.Duration.ConstantValue.Value = critDmgIncEffect.Duration!.ConstantValue.Value; magicEffect.Duration.MaximumValue = critDmgIncEffect.Duration.MaximumValue; foreach (var durationRelatedValue in critDmgIncEffect.Duration.RelatedValues) { var durationRelatedValueCopy = context.CreateNew(); durationRelatedValueCopy.InputAttribute = durationRelatedValue.InputAttribute!.GetPersistent(gameConfiguration); durationRelatedValueCopy.InputOperator = durationRelatedValue.InputOperator; durationRelatedValueCopy.InputOperand = durationRelatedValue.InputOperand; magicEffect.Duration.RelatedValues.Add(durationRelatedValueCopy); } foreach (var powerUp in critDmgIncEffect.PowerUpDefinitions) { var powerUpCopy = context.CreateNew(); magicEffect.PowerUpDefinitions.Add(powerUpCopy); powerUpCopy.TargetAttribute = powerUp.TargetAttribute!.GetPersistent(gameConfiguration); powerUpCopy.Boost = context.CreateNew(); powerUpCopy.Boost.ConstantValue.Value = powerUp.Boost!.ConstantValue.Value; foreach (var boostRelatedValue in powerUp.Boost.RelatedValues) { var boostRelatedValueCopy = context.CreateNew(); boostRelatedValueCopy.InputAttribute = boostRelatedValue.InputAttribute!.GetPersistent(gameConfiguration); boostRelatedValueCopy.InputOperator = boostRelatedValue.InputOperator; boostRelatedValueCopy.InputOperand = boostRelatedValue.InputOperand; powerUpCopy.Boost.RelatedValues.Add(boostRelatedValueCopy); } } var critChancePowerUpDefinition = context.CreateNew(); magicEffect.PowerUpDefinitions.Add(critChancePowerUpDefinition); critChancePowerUpDefinition.TargetAttribute = Stats.CriticalDamageChance.GetPersistent(gameConfiguration); critChancePowerUpDefinition.Boost = context.CreateNew(); critChancePowerUpDefinition.Boost.ConstantValue.Value = 0; return magicEffect; } private void MapSkillToEffect(GameConfiguration gameConfiguration, SkillNumber skillNumber, MagicEffectDefinition magicEffect) { if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is { } skill) { skill.MagicEffectDef = magicEffect; } } private void AddAreaSkillSettings( GameConfiguration gameConfiguration, IContext context, SkillNumber skillNumber, bool useFrustumFilter, float frustumStartWidth, float frustumEndWidth, float frustumDistance, bool useDeferredHits = false, TimeSpan delayPerOneDistance = default, TimeSpan delayBetweenHits = default, int minimumHitsPerTarget = 1, int maximumHitsPerTarget = 1, int minimumHitsPerAttack = default, int maximumHitsPerAttack = default, float hitChancePerDistanceMultiplier = 1.0f, bool useTargetAreaFilter = false, float targetAreaDiameter = default, int projectileCount = 1, int effectRange = default) { if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)skillNumber) is not { } skill) { return; } skill.SkillType = SkillType.AreaSkillAutomaticHits; var areaSkillSettings = context.CreateNew(); skill.AreaSkillSettings = areaSkillSettings; areaSkillSettings.UseFrustumFilter = useFrustumFilter; areaSkillSettings.FrustumStartWidth = frustumStartWidth; areaSkillSettings.FrustumEndWidth = frustumEndWidth; areaSkillSettings.FrustumDistance = frustumDistance; areaSkillSettings.UseTargetAreaFilter = useTargetAreaFilter; areaSkillSettings.TargetAreaDiameter = targetAreaDiameter; areaSkillSettings.UseDeferredHits = useDeferredHits; areaSkillSettings.DelayPerOneDistance = delayPerOneDistance; areaSkillSettings.DelayBetweenHits = delayBetweenHits; areaSkillSettings.MinimumNumberOfHitsPerTarget = minimumHitsPerTarget; areaSkillSettings.MaximumNumberOfHitsPerTarget = maximumHitsPerTarget; areaSkillSettings.MinimumNumberOfHitsPerAttack = minimumHitsPerAttack; areaSkillSettings.MaximumNumberOfHitsPerAttack = maximumHitsPerAttack; areaSkillSettings.HitChancePerDistanceMultiplier = hitChancePerDistanceMultiplier; areaSkillSettings.ProjectileCount = projectileCount; areaSkillSettings.EffectRange = effectRange; } }