From 42ba05304187109f353814386a754f70f09c1e87 Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 13:21:18 +0200 Subject: [PATCH 01/34] format doc & fix description stat for isStunned --- src/GameLogic/Attributes/Stats.cs | 2 +- .../VersionSeasonSix/SkillsInitializer.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 6a28bbf6f..c04bbdf6c 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -550,7 +550,7 @@ public class Stats /// /// Gets the attribute definition, which defines if a player has stun effect applied. /// - public static AttributeDefinition IsStunned { get; } = new (new Guid("22C86BAF-7F27-478D-8075-E4465C2859DD"), "Is stunned", "The player is poisoned and loses health"); + public static AttributeDefinition IsStunned { get; } = new (new Guid("22C86BAF-7F27-478D-8075-E4465C2859DD"), "Is stunned", "The player is stunned and can't move."); /// /// Gets the ice resistance attribute definition. Value range from 0 to 1. diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 142cc96a9..63de3c490 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -226,7 +226,7 @@ public override void Initialize() this.CreateSkill(SkillNumber.MonsterAttackSdInc, "Monster Attack SD Inc", CharacterClasses.AllMastersExceptFistMaster, damage: 11, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.MonsterAttackLifeInc, "Monster Attack Life Inc", CharacterClasses.AllMastersExceptFistMaster, damage: 6, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.SwellLifeProficiency, "Swell Life Proficiency", CharacterClasses.BladeMaster, damage: 7, abilityConsumption: 28, manaConsumption: 26, levelRequirement: 120); - this.CreateSkill(SkillNumber.MinimumAttackPowerInc, "Minimum Attack Power Inc", CharacterClasses.BladeMaster | CharacterClasses.DuelMaster |CharacterClasses.LordEmperor, DamageType.Physical, 22, skillType: SkillType.PassiveBoost); + this.CreateSkill(SkillNumber.MinimumAttackPowerInc, "Minimum Attack Power Inc", CharacterClasses.BladeMaster | CharacterClasses.DuelMaster | CharacterClasses.LordEmperor, DamageType.Physical, 22, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.MonsterAttackManaInc, "Monster Attack Mana Inc", CharacterClasses.AllMastersExceptFistMaster, damage: 6, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.PvPAttackRate, "PvP Attack Rate", CharacterClasses.AllMastersExceptFistMaster, damage: 14, skillType: SkillType.PassiveBoost); @@ -427,7 +427,7 @@ private void InitializeNextSeasonMasterSkills() this.CreateSkill(SkillNumber.WingofStormAbsPowUp, "Wing of Storm Abs PowUp", CharacterClasses.BladeMaster, damage: 1, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.WingofStormDefPowUp, "Wing of Storm Def PowUp", CharacterClasses.BladeMaster, damage: 17, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.IronDefense, "Iron Defense", CharacterClasses.AllMasters, damage: 1); - this.CreateSkill(SkillNumber.WingofStormAttPowUp, "Wing of Storm Att PowUp", CharacterClasses.BladeMaster, damage: 17, skillType: SkillType.PassiveBoost); + this.CreateSkill(SkillNumber.WingofStormAttPowUp, "Wing of Storm Att PowUp", CharacterClasses.BladeMaster, damage: 17, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.DeathStabProficiency, "Death Stab Proficiency", CharacterClasses.BladeMaster, DamageType.Physical, 7, 2, 26, 30, 160, elementalModifier: ElementalType.Wind); this.CreateSkill(SkillNumber.StrikeofDestrProf, "Strike of Destr Prof", CharacterClasses.BladeMaster, DamageType.Physical, 7, 5, 24, 30, 100, elementalModifier: ElementalType.Ice); this.CreateSkill(SkillNumber.MaximumAgIncrease, "Maximum AG Increase", CharacterClasses.AllMastersExceptFistMaster, damage: 8, skillType: SkillType.PassiveBoost); @@ -649,7 +649,7 @@ private void InitializeMasterSkillData() this.AddMasterSkillDefinition(SkillNumber.SpearMastery, SkillNumber.SpearStrengthener, SkillNumber.Undefined, 3, 3, SkillNumber.Undefined, 20, Formula120); this.AddMasterSkillDefinition(SkillNumber.SwellLifeStrengt, SkillNumber.SwellLife, SkillNumber.Undefined, 3, 4, SkillNumber.SwellLife, 20, Formula181); - this.AddPassiveMasterSkillDefinition(SkillNumber.ManaReduction, Stats.ManaUsageReduction, AggregateType.AddRaw, Formula722Value, Formula722, 4, 3); + this.AddPassiveMasterSkillDefinition(SkillNumber.ManaReduction, Stats.ManaUsageReduction, AggregateType.AddRaw, Formula722Value, Formula722, 4, 3); this.AddPassiveMasterSkillDefinition(SkillNumber.MonsterAttackSdInc, Stats.ShieldAfterMonsterKillMultiplier, AggregateType.AddFinal, Formula914, 4, 3); this.AddPassiveMasterSkillDefinition(SkillNumber.MonsterAttackLifeInc, Stats.HealthAfterMonsterKillMultiplier, AggregateType.AddFinal, Formula4319, 4, 3); this.AddMasterSkillDefinition(SkillNumber.SwellLifeProficiency, SkillNumber.SwellLifeStrengt, SkillNumber.Undefined, 3, 5, SkillNumber.SwellLife, 20, Formula181); @@ -755,7 +755,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.ScepterMastery, Stats.ScepterBonusBaseDamage, AggregateType.AddRaw, Formula1154, 3, 3, SkillNumber.ScepterStrengthener); // todo pvp this.AddPassiveMasterSkillDefinition(SkillNumber.ShieldMastery, Stats.BonusDefenseWithShield, AggregateType.AddRaw, Formula1204, 3, 3, SkillNumber.ShieldStrengthenerLordEmperor); this.AddPassiveMasterSkillDefinition(SkillNumber.CommandAttackInc, Stats.BonusDefenseWithScepterCmdDiv, AggregateType.AddRaw, $"1 / ({Formula3822})", Formula3822, 3, 3); - this.AddPassiveMasterSkillDefinition(SkillNumber.DarkSpiritStr3, Stats.RavenExcDamageChanceBonus, AggregateType.AddRaw, $"{Formula120} / 100", Formula120,5, 3, SkillNumber.DarkSpiritStr2); + this.AddPassiveMasterSkillDefinition(SkillNumber.DarkSpiritStr3, Stats.RavenExcDamageChanceBonus, AggregateType.AddRaw, $"{Formula120} / 100", Formula120, 5, 3, SkillNumber.DarkSpiritStr2); this.AddPassiveMasterSkillDefinition(SkillNumber.PetDurabilityStr, Stats.PetDurationIncrease, AggregateType.Multiplicate, Formula1204, 5, 3); // RF @@ -764,7 +764,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseMaximumSd, Stats.MaximumShield, AggregateType.AddRaw, Formula30704, 2, 1); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseManaRecoveryRate, Stats.ManaRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease181, Formula181, 2, 1, SkillNumber.Undefined, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreasePoisonResistance, Stats.PoisonResistance, AggregateType.AddRaw, Formula120Value, Formula120, 2, 1); - this.AddPassiveMasterSkillDefinition(SkillNumber.DurabilityReduction2FistMaster, Stats.ItemDurationIncrease, AggregateType.Multiplicate,Formula1204, 3, 1, SkillNumber.DurabilityReduction1FistMaster); + this.AddPassiveMasterSkillDefinition(SkillNumber.DurabilityReduction2FistMaster, Stats.ItemDurationIncrease, AggregateType.Multiplicate, Formula1204, 3, 1, SkillNumber.DurabilityReduction1FistMaster); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseSdRecoveryRate, Stats.ShieldRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseMaximumSd, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseHpRecoveryRate, Stats.HealthRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseManaRecoveryRate, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseLightningResistance, Stats.LightningResistance, AggregateType.AddRaw, Formula120Value, Formula120, 3, 1, requiredSkill1: SkillNumber.IncreasePoisonResistance); From 65bdba239a22f51a83329893dc5377d5af0d19b1 Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 14:32:16 +0200 Subject: [PATCH 02/34] implement RandomElement --- src/AttributeSystem/RandomElement.cs | 69 +++++++++++++++++++ .../RandomElementTests.cs | 26 +++++++ 2 files changed, 95 insertions(+) create mode 100644 src/AttributeSystem/RandomElement.cs create mode 100644 tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs diff --git a/src/AttributeSystem/RandomElement.cs b/src/AttributeSystem/RandomElement.cs new file mode 100644 index 000000000..52150863f --- /dev/null +++ b/src/AttributeSystem/RandomElement.cs @@ -0,0 +1,69 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.AttributeSystem; + +/// +/// An element with a constant value. +/// +public class RandomElement : IElement +{ + /// + /// Initializes a new instance of the class. + /// + /// The min value. + /// The max value. + /// Type of the aggregate. + public RandomElement(float minValue, float maxValue, AggregateType aggregateType = AggregateType.AddRaw) + { + this.MinValue = minValue; + this.MaxValue = maxValue; + this.AggregateType = aggregateType; + } + + /// + /// Never occurs, so the implementation is empty. + /// + public event EventHandler? ValueChanged + { + add + { + // do nothing, as the value never changes. + } + + remove + { + // do nothing, as the value never changes. + } + } + + /// + /// Gets or sets the min value. + /// + public float MinValue { get; set; } + + /// + /// Gets or sets the max value. + /// + public float MaxValue { get; set; } + + /// + public float Value + { + get + { + double randomValue = Random.Shared.NextDouble(); + return (float)(this.MinValue + (randomValue * (this.MaxValue - this.MinValue))); + } + } + + /// + public AggregateType AggregateType { get; } + + /// + public override string ToString() + { + return $"{this.Value} ({this.AggregateType})"; + } +} \ No newline at end of file diff --git a/tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs b/tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs new file mode 100644 index 000000000..0786786c8 --- /dev/null +++ b/tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs @@ -0,0 +1,26 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.AttributeSystem.Tests; + +/// +/// Tests for the . +/// +[TestFixture] +public class RandomElementTests +{ + /// + /// Tests if the value of the element is as defined in the constructor. + /// + [Test] + public void ValueAsDefined() + { + const int minValue = 4; + const int maxValue = 10; + var element = new RandomElement(minValue, maxValue); + var randomValue = element.Value; + Assert.That(randomValue, Is.GreaterThanOrEqualTo(minValue)); + Assert.That(randomValue, Is.LessThanOrEqualTo(maxValue)); + } +} \ No newline at end of file From f00c91bfbf7aa5176a4ab7f58ad24d6707563c9a Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 14:40:30 +0200 Subject: [PATCH 03/34] add new stat IsAsleep and implement logic to manage --- src/GameLogic/AttackableExtensions.cs | 2 +- src/GameLogic/Attributes/Stats.cs | 5 ++++ src/GameLogic/MagicEffectsList.cs | 23 +++++++++++++++++-- src/GameLogic/NPC/AttackableNpcBase.cs | 5 ++++ src/GameLogic/NPC/BasicMonsterIntelligence.cs | 2 +- src/GameLogic/Player.cs | 8 +++---- src/GameLogic/PlayerActions/HitAction.cs | 6 +++++ .../Skills/AreaSkillAttackAction.cs | 6 +++++ .../Skills/TargetedSkillAction.cs | 8 ++++++- 9 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 04942f473..57ff48c2c 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -553,7 +553,7 @@ private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IA await target.MagicEffectList.AddEffectAsync(magicEffect).ConfigureAwait(false); if (target is ISupportWalk walkSupporter && walkSupporter.IsWalking - && magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsFrozen || e.TargetAttribute == Stats.IsStunned)) + && magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsFrozen || e.TargetAttribute == Stats.IsStunned || e.TargetAttribute == Stats.IsAsleep)) { await walkSupporter.StopWalkingAsync().ConfigureAwait(false); diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index c04bbdf6c..96c0699a0 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -552,6 +552,11 @@ public class Stats /// public static AttributeDefinition IsStunned { get; } = new (new Guid("22C86BAF-7F27-478D-8075-E4465C2859DD"), "Is stunned", "The player is stunned and can't move."); + /// + /// Gets the attribute definition, which defines if a player has stun effect applied. + /// + public static AttributeDefinition IsAsleep { get; } = new(new Guid("0518F532-7A8F-4491-8A23-98B620608CB3"), "Is asleep", "The player is asleep and cannot move until hit."); + /// /// Gets the ice resistance attribute definition. Value range from 0 to 1. /// diff --git a/src/GameLogic/MagicEffectsList.cs b/src/GameLogic/MagicEffectsList.cs index 365c9001d..da9a9babe 100644 --- a/src/GameLogic/MagicEffectsList.cs +++ b/src/GameLogic/MagicEffectsList.cs @@ -7,6 +7,8 @@ namespace MUnique.OpenMU.GameLogic; using System.Collections; using Nito.AsyncEx; using MUnique.OpenMU.GameLogic.Views.World; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.AttributeSystem; /// /// The list of magic effects of a player instance. Automatically applies the power-ups of the effects to the player. @@ -14,9 +16,9 @@ namespace MUnique.OpenMU.GameLogic; public class MagicEffectsList : AsyncDisposable { private const byte InvisibleEffectStartIndex = 200; - private readonly BitArray _contains = new (0x100); + private readonly BitArray _contains = new(0x100); private readonly IAttackable _owner; - private readonly AsyncLock _addLock = new (); + private readonly AsyncLock _addLock = new(); /// /// Initializes a new instance of the class. @@ -93,6 +95,23 @@ public async ValueTask ClearAllEffectsAsync() } } + /// + /// Clear the effects that produce a specific stat. + /// + /// The stat produced by effect + public async ValueTask ClearAllEffectsProducingSpecificStatAsync(AttributeDefinition stat) + { + var effects = this.ActiveEffects.Values.ToArray(); + + foreach (var effect in effects) + { + if (effect.PowerUpElements.Any(p => p.Target == stat)) + { + await effect.DisposeAsync().ConfigureAwait(false); + } + } + } + /// /// Clears the effects after death of the player. /// diff --git a/src/GameLogic/NPC/AttackableNpcBase.cs b/src/GameLogic/NPC/AttackableNpcBase.cs index 5dc20de6b..646a33813 100644 --- a/src/GameLogic/NPC/AttackableNpcBase.cs +++ b/src/GameLogic/NPC/AttackableNpcBase.cs @@ -105,6 +105,11 @@ public int Health return null; } + if (this.Attributes[Stats.IsAsleep] > 0) + { + await this.MagicEffectList.ClearAllEffectsProducingSpecificStatAsync(Stats.IsAsleep).ConfigureAwait(false); + } + var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); await this.HitAsync(hitInfo, attacker, skill?.Skill).ConfigureAwait(false); if (hitInfo.HealthDamage > 0) diff --git a/src/GameLogic/NPC/BasicMonsterIntelligence.cs b/src/GameLogic/NPC/BasicMonsterIntelligence.cs index b248728cf..311ca9de6 100644 --- a/src/GameLogic/NPC/BasicMonsterIntelligence.cs +++ b/src/GameLogic/NPC/BasicMonsterIntelligence.cs @@ -210,7 +210,7 @@ private async ValueTask TickAsync() return; } - if (this.Monster.Attributes[Stats.IsStunned] > 0) + if (this.Monster.Attributes[Stats.IsStunned] > 0 || this.Monster.Attributes[Stats.IsAsleep] > 0) { return; } diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index f5b7f83c5..9487a166b 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -234,7 +234,7 @@ public Account? Account public NonPlayerCharacter? OpenedNpc { get; set; } /// - public StateMachine PlayerState { get; } = new (GameLogic.PlayerState.Initial); + public StateMachine PlayerState { get; } = new(GameLogic.PlayerState.Initial); // TODO: TradeContext-object? @@ -278,7 +278,7 @@ private set public ISet Observers { get; } = new HashSet(); /// - public AsyncReaderWriterLock ObserverLock { get; } = new (); + public AsyncReaderWriterLock ObserverLock { get; } = new(); /// public IPartyMember? LastPartyRequester { get; set; } @@ -327,7 +327,7 @@ private set /// public Point Position { - get => new (this.SelectedCharacter?.PositionX ?? 0, this.SelectedCharacter?.PositionY ?? 0); + get => new(this.SelectedCharacter?.PositionX ?? 0, this.SelectedCharacter?.PositionY ?? 0); set { @@ -1153,7 +1153,7 @@ public async ValueTask WalkToAsync(Point target, Memory steps) return; } - if (attributes[Stats.IsFrozen] > 0 || attributes[Stats.IsStunned] > 0) + if (attributes[Stats.IsFrozen] > 0 || attributes[Stats.IsStunned] > 0 || attributes[Stats.IsAsleep] > 0) { return; } diff --git a/src/GameLogic/PlayerActions/HitAction.cs b/src/GameLogic/PlayerActions/HitAction.cs index 6d092181e..b0799a035 100644 --- a/src/GameLogic/PlayerActions/HitAction.cs +++ b/src/GameLogic/PlayerActions/HitAction.cs @@ -32,6 +32,12 @@ public async ValueTask HitAsync(Player player, IAttackable target, byte attackAn return; } + if (attributes[Stats.IsAsleep] > 0) + { + player.Logger.LogWarning($"Probably Hacker - player {player} is attacking in asleep state"); + return; + } + if (player.IsAtSafezone()) { player.Logger.LogWarning($"Probably Hacker - player {player} is attacking from safezone"); diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 012e8cb0c..cdafa2a3c 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -60,6 +60,12 @@ private async ValueTask PerformAutomaticHitsAsync(Player player, ushort extraTar return; } + if (attributes[Stats.IsAsleep] > 0) + { + player.Logger.LogWarning("Probably Hacker - player {player} is attacking in sleep state", player); + return; + } + if (player.IsAtSafezone()) { player.Logger.LogWarning("Probably Hacker - player {player} is attacking from safezone", player); diff --git a/src/GameLogic/PlayerActions/Skills/TargetedSkillAction.cs b/src/GameLogic/PlayerActions/Skills/TargetedSkillAction.cs index e0f18be0c..523a2b7d3 100644 --- a/src/GameLogic/PlayerActions/Skills/TargetedSkillAction.cs +++ b/src/GameLogic/PlayerActions/Skills/TargetedSkillAction.cs @@ -14,7 +14,7 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; /// public class TargetedSkillAction { - private static readonly Dictionary SummonSkillToMonsterMapping = new () + private static readonly Dictionary SummonSkillToMonsterMapping = new() { { 30, 26 }, // Goblin { 31, 32 }, // Stone Golem @@ -51,6 +51,12 @@ public async ValueTask PerformSkillAsync(Player player, IAttackable target, usho return; } + if (attributes[Stats.IsAsleep] > 0) + { + player.Logger.LogWarning($"Probably Hacker - player {player} is attacking in asleep state"); + return; + } + var skillEntry = player.SkillList?.GetSkill(skillId); var skill = skillEntry?.Skill; if (skill is null || skill.SkillType == SkillType.PassiveBoost) From c491b2a598823cfe1354853403f2b7de4e035dd3 Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 16:25:59 +0200 Subject: [PATCH 04/34] fix Sleep skill initializer --- .../Initialization/Skills/SkillsInitializerBase.cs | 6 ++++++ .../Initialization/VersionSeasonSix/SkillsInitializer.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs b/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs index 838afa540..82181a619 100644 --- a/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs +++ b/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs @@ -112,6 +112,12 @@ private void ApplyElementalModifier(ElementalType elementalModifier, Skill skill return; } + if ((SkillNumber)skill.Number is SkillNumber.Sleep) + { + skill.MagicEffectDef = this.CreateEffect(ElementalType.Undefined, MagicEffectNumber.Sleep, Stats.IsAsleep, 5); + return; + } + switch (elementalModifier) { case ElementalType.Ice: diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 63de3c490..1b866714e 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -175,7 +175,7 @@ public override void Initialize() this.CreateSkill(SkillNumber.ChainLightning, "Chain Lightning", CharacterClasses.AllSummoners, DamageType.Curse, 70, 6, manaConsumption: 85, energyRequirement: 245, skillType: SkillType.AreaSkillExplicitTarget, skillTarget: SkillTarget.Explicit); this.CreateSkill(SkillNumber.DamageReflection, "Damage Reflection", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 10, manaConsumption: 40, energyRequirement: 375); this.CreateSkill(SkillNumber.Berserker, "Berserker", CharacterClasses.AllSummoners, DamageType.Curse, distance: 5, abilityConsumption: 50, manaConsumption: 100, energyRequirement: 620); - this.CreateSkill(SkillNumber.Sleep, "Sleep", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 3, manaConsumption: 20, energyRequirement: 180); + this.CreateSkill(SkillNumber.Sleep, "Sleep", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 3, manaConsumption: 20, energyRequirement: 180, skillType: SkillType.Buff); this.CreateSkill(SkillNumber.Weakness, "Weakness", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 50, energyRequirement: 663); this.CreateSkill(SkillNumber.Innovation, "Innovation", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 70, energyRequirement: 912); this.CreateSkill(SkillNumber.Explosion223, "Explosion", CharacterClasses.AllSummoners, DamageType.Curse, 40, 6, 5, 90, energyRequirement: 100, elementalModifier: ElementalType.Fire); From 7bcc87f7a1647d0c6b9a9e8248b86a0a9830cce1 Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 16:26:07 +0200 Subject: [PATCH 05/34] add update plugin to fix sleep --- .../Updates/FixSleepSkillUpdate.cs | 79 +++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 5 ++ 2 files changed, 84 insertions(+) create mode 100644 src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs diff --git a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs new file mode 100644 index 000000000..d6a4d3e5c --- /dev/null +++ b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs @@ -0,0 +1,79 @@ +// +// 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.DataModel.Attributes; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.DataModel.Configuration.Items; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.Persistence.Initialization.Skills; +using MUnique.OpenMU.PlugIns; + +/// +/// This adds the items required to enter the kalima map. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("A8827A3C-7F52-47CF-9EA5-562A9C06B986")] +public class FixSleepSkillUpdate : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Fix Sleep Skill"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "Updates the Sleep skill definition to correct it and implement the new stat."; + + /// + public override UpdateVersion Version => UpdateVersion.FixSleepSkillSeason6; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => false; + + /// + public override DateTime CreatedAt => new(2024, 09, 01, 18, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + var magicEffectSleep = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Sleep); + + if (magicEffectSleep == null) + { + magicEffectSleep = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffectSleep); + magicEffectSleep.Name = "Sleep"; + magicEffectSleep.InformObservers = true; + magicEffectSleep.Number = (short)MagicEffectNumber.Sleep; + magicEffectSleep.StopByDeath = true; + magicEffectSleep.SubType = 255; + magicEffectSleep.Duration = context.CreateNew(); + magicEffectSleep.Duration.ConstantValue.Value = 5; + var powerUp = context.CreateNew(); + magicEffectSleep.PowerUpDefinitions.Add(powerUp); + var boost = context.CreateNew(); + powerUp.Boost = boost; + boost.ConstantValue.Value = 1; + powerUp.TargetAttribute = Stats.IsAsleep.GetPersistent(gameConfiguration); + } + + var sleepSkill = gameConfiguration.Skills.First(x => x.Number == (short)SkillNumber.Sleep); + sleepSkill.SkillType = SkillType.Buff; + sleepSkill.MagicEffectDef = magicEffectSleep; + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index a273401f2..946a3c915 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -159,4 +159,9 @@ public enum UpdateVersion /// The version of the . /// AddItemDropGroupForJewelsSeason6 = 30, + + /// + /// The version of the . + /// + FixSleepSkillSeason6 = 31, } \ No newline at end of file From 3c5c1f5e7fb70d3e2313ae1b154d762c5a0c722d Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 16:35:30 +0200 Subject: [PATCH 06/34] fix description for asleep attribute --- src/GameLogic/Attributes/Stats.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 96c0699a0..32b63ada4 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -553,9 +553,9 @@ public class Stats public static AttributeDefinition IsStunned { get; } = new (new Guid("22C86BAF-7F27-478D-8075-E4465C2859DD"), "Is stunned", "The player is stunned and can't move."); /// - /// Gets the attribute definition, which defines if a player has stun effect applied. + /// Gets the attribute definition, which defines if a player has asleep effect applied. /// - public static AttributeDefinition IsAsleep { get; } = new(new Guid("0518F532-7A8F-4491-8A23-98B620608CB3"), "Is asleep", "The player is asleep and cannot move until hit."); + public static AttributeDefinition IsAsleep { get; } = new(new Guid("0518F532-7A8F-4491-8A23-98B620608CB3"), "Is asleep", "The player is asleep and can't move until hit."); /// /// Gets the ice resistance attribute definition. Value range from 0 to 1. From 90fd3ea56375dae61bf7ced626c65b7c737a3a37 Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 19:51:50 +0200 Subject: [PATCH 07/34] fix update sleep --- .../Initialization/Updates/FixSleepSkillUpdate.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs index d6a4d3e5c..e205001a4 100644 --- a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs +++ b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs @@ -51,6 +51,13 @@ public class FixSleepSkillUpdate : UpdatePlugInBase /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { + var sleepAttribute = gameConfiguration.Attributes.FirstOrDefault(a => a.Id == Stats.IsAsleep.Id); + + if (sleepAttribute == null) + { + gameConfiguration.Attributes.Add(Stats.IsAsleep); + } + var magicEffectSleep = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Sleep); if (magicEffectSleep == null) From 6ce6417e277d6e6bfe3ac60e3458b674c8c726d1 Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 20:10:57 +0200 Subject: [PATCH 08/34] fix persistence --- .../Initialization/Updates/FixSleepSkillUpdate.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs index e205001a4..1ff2bb6c7 100644 --- a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs +++ b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs @@ -55,7 +55,12 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio if (sleepAttribute == null) { - gameConfiguration.Attributes.Add(Stats.IsAsleep); + gameConfiguration.Attributes.Add(new AttributeSystem.AttributeDefinition + { + Id = Stats.IsAsleep.Id, + Designation = Stats.IsAsleep.Designation, + Description = Stats.IsAsleep.Description, + }); } var magicEffectSleep = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Sleep); From 048cf7b4fdf30b3a1b3f6ca1516e44d711e14b4f Mon Sep 17 00:00:00 2001 From: bernat Date: Sun, 1 Sep 2024 20:42:38 +0200 Subject: [PATCH 09/34] fix persistence stat --- .../Initialization/Updates/FixSleepSkillUpdate.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs index 1ff2bb6c7..eb70bcba4 100644 --- a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs +++ b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs @@ -7,8 +7,6 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using System.Runtime.InteropServices; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; -using MUnique.OpenMU.DataModel.Configuration.Items; -using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.Persistence.Initialization.Skills; using MUnique.OpenMU.PlugIns; @@ -55,12 +53,11 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio if (sleepAttribute == null) { - gameConfiguration.Attributes.Add(new AttributeSystem.AttributeDefinition - { - Id = Stats.IsAsleep.Id, - Designation = Stats.IsAsleep.Designation, - Description = Stats.IsAsleep.Description, - }); + var attr = context.CreateNew(); + attr.Id = Stats.IsAsleep.Id; + attr.Designation = Stats.IsAsleep.Designation; + attr.Description = Stats.IsAsleep.Description; + gameConfiguration.Attributes.Add(attr); } var magicEffectSleep = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Sleep); From 81e1c47602c9850def9922c63fa9af96ba29194e Mon Sep 17 00:00:00 2001 From: bernat Date: Sat, 7 Sep 2024 16:42:28 +0200 Subject: [PATCH 10/34] remove random element --- src/AttributeSystem/RandomElement.cs | 69 ------------------- .../RandomElementTests.cs | 26 ------- 2 files changed, 95 deletions(-) delete mode 100644 src/AttributeSystem/RandomElement.cs delete mode 100644 tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs diff --git a/src/AttributeSystem/RandomElement.cs b/src/AttributeSystem/RandomElement.cs deleted file mode 100644 index 52150863f..000000000 --- a/src/AttributeSystem/RandomElement.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace MUnique.OpenMU.AttributeSystem; - -/// -/// An element with a constant value. -/// -public class RandomElement : IElement -{ - /// - /// Initializes a new instance of the class. - /// - /// The min value. - /// The max value. - /// Type of the aggregate. - public RandomElement(float minValue, float maxValue, AggregateType aggregateType = AggregateType.AddRaw) - { - this.MinValue = minValue; - this.MaxValue = maxValue; - this.AggregateType = aggregateType; - } - - /// - /// Never occurs, so the implementation is empty. - /// - public event EventHandler? ValueChanged - { - add - { - // do nothing, as the value never changes. - } - - remove - { - // do nothing, as the value never changes. - } - } - - /// - /// Gets or sets the min value. - /// - public float MinValue { get; set; } - - /// - /// Gets or sets the max value. - /// - public float MaxValue { get; set; } - - /// - public float Value - { - get - { - double randomValue = Random.Shared.NextDouble(); - return (float)(this.MinValue + (randomValue * (this.MaxValue - this.MinValue))); - } - } - - /// - public AggregateType AggregateType { get; } - - /// - public override string ToString() - { - return $"{this.Value} ({this.AggregateType})"; - } -} \ No newline at end of file diff --git a/tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs b/tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs deleted file mode 100644 index 0786786c8..000000000 --- a/tests/MUnique.OpenMU.AttributeSystem.Tests/RandomElementTests.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -// Licensed under the MIT License. See LICENSE file in the project root for full license information. -// - -namespace MUnique.OpenMU.AttributeSystem.Tests; - -/// -/// Tests for the . -/// -[TestFixture] -public class RandomElementTests -{ - /// - /// Tests if the value of the element is as defined in the constructor. - /// - [Test] - public void ValueAsDefined() - { - const int minValue = 4; - const int maxValue = 10; - var element = new RandomElement(minValue, maxValue); - var randomValue = element.Value; - Assert.That(randomValue, Is.GreaterThanOrEqualTo(minValue)); - Assert.That(randomValue, Is.LessThanOrEqualTo(maxValue)); - } -} \ No newline at end of file From a38c4056d3be2df17a0f119d64ff7d65ae698d54 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 3 Dec 2025 17:37:49 +0000 Subject: [PATCH 11/34] Proposal for variable magic effect duration and chance --- .../Configuration/MagicEffectDefinition.cs | 33 +++++++ src/DataModel/Entities/SkillEntry.cs | 27 ++++++ src/GameLogic/AttackableExtensions.cs | 27 +++++- .../Attributes/AttributeSystemExtensions.cs | 16 ++++ src/GameLogic/Player.cs | 6 ++ .../Skills/SleepEffectInitializer.cs | 95 +++++++++++++++++++ 6 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 src/Persistence/Initialization/Skills/SleepEffectInitializer.cs diff --git a/src/DataModel/Configuration/MagicEffectDefinition.cs b/src/DataModel/Configuration/MagicEffectDefinition.cs index 4d5598294..7a4d47882 100644 --- a/src/DataModel/Configuration/MagicEffectDefinition.cs +++ b/src/DataModel/Configuration/MagicEffectDefinition.cs @@ -51,12 +51,45 @@ public partial class MagicEffectDefinition /// public bool SendDuration { get; set; } + /// + /// Gets or sets a value indicating whether the duration of the effect depends on the target's level. + /// + public bool DurationDependsOnTargetLevel { get; set; } = false; + + /// + /// Gets or sets a value by which the effect target's (monster) level should be divided in case is true. + /// + public float TargetLevelDivisor { get; set; } = 1f; + + /// + /// Gets or sets a value by which the effect target's (player) level should be divided in case is true. + /// + public float TargetLevelDivisorPvp { get; set; } = 1f; + + /// + /// Gets or sets the chance of applying the effect, in decimals. + /// + [MemberOfAggregate] + public PowerUpDefinitionValue? Chance { get; set; } + + /// + /// Gets or sets the chance of applying the effect in PvP, in decimals. + /// + [MemberOfAggregate] + public PowerUpDefinitionValue? ChancePvp { get; set; } + /// /// Gets or sets the duration which describes how long the apply, in seconds. /// [MemberOfAggregate] public virtual PowerUpDefinitionValue? Duration { get; set; } + /// + /// Gets or sets the duration which describes how long the apply to PVP, in seconds. + /// + [MemberOfAggregate] + public virtual PowerUpDefinitionValue? DurationPvp { get; set; } + /// /// Gets or sets the power up definitions which are used to create the actual power up element. /// diff --git a/src/DataModel/Entities/SkillEntry.cs b/src/DataModel/Entities/SkillEntry.cs index 29a498700..f8f38fe3c 100644 --- a/src/DataModel/Entities/SkillEntry.cs +++ b/src/DataModel/Entities/SkillEntry.cs @@ -58,6 +58,33 @@ public int Level [Transient] public IElement? PowerUpDuration { get; set; } + /// + /// Gets or sets the duration of the for PvP. + /// + /// + /// It is an IElement, because the duration can be dependent from the player attributes. + /// + [Transient] + public IElement? PowerUpDurationPvp { get; set; } + + /// + /// Gets or sets the chance of applying the . + /// + /// + /// It is an IElement, because the duration can be dependent from the player attributes. + /// + [Transient] + public IElement? PowerUpChance { get; set; } + + /// + /// Gets or sets the chance of applying the for PvP. + /// + /// + /// It is an IElement, because the duration can be dependent from the player attributes. + /// + [Transient] + public IElement? PowerUpChancePvp { get; set; } + /// /// Gets or sets the attributes, if this skill has attribute relationships. /// diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 1b64b216a..64c1b725b 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -8,6 +8,7 @@ namespace MUnique.OpenMU.GameLogic; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Configuration.Items; +using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.NPC; using MUnique.OpenMU.GameLogic.Pet; @@ -309,7 +310,13 @@ public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAt player.CreateMagicEffectPowerUp(skillEntry); } - await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, skillEntry.PowerUpDuration!, skillEntry.PowerUps!).ConfigureAwait(false); + float chance = attacker is Player ? skillEntry.PowerUpChancePvp!.Value : skillEntry.PowerUpChance!.Value; + if (!Rand.NextRandomBool(Convert.ToDouble(chance))) + { + return; + } + + await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, attacker is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!, skillEntry.PowerUps!).ConfigureAwait(false); } /// @@ -773,10 +780,24 @@ private static void GetBaseDmg(this IAttacker attacker, SkillEntry? skill, out i /// The power ups of the effect. private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, IElement duration, params (AttributeDefinition Target, IElement Boost)[] powerUps) { + float levelDeduction = 0; + if (magicEffectDefinition.DurationDependsOnTargetLevel) + { + levelDeduction = target is Player + ? target.Attributes[Stats.Level] / magicEffectDefinition.TargetLevelDivisorPvp + : target.Attributes[Stats.Level] / magicEffectDefinition.TargetLevelDivisor; + } + + TimeSpan durationSpan = TimeSpan.FromSeconds(duration.Value - levelDeduction); + if (durationSpan < TimeSpan.FromSeconds(1)) + { + return; + } + var isPoisonEffect = magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsPoisoned); var magicEffect = isPoisonEffect - ? new PoisonMagicEffect(powerUps[0].Boost, magicEffectDefinition, TimeSpan.FromSeconds(duration.Value), attacker, target) - : new MagicEffect(TimeSpan.FromSeconds(duration.Value), magicEffectDefinition, powerUps.Select(p => new MagicEffect.ElementWithTarget(p.Boost, p.Target)).ToArray()); + ? new PoisonMagicEffect(powerUps[0].Boost, magicEffectDefinition, durationSpan, attacker, target) + : new MagicEffect(durationSpan, magicEffectDefinition, powerUps.Select(p => new MagicEffect.ElementWithTarget(p.Boost, p.Target)).ToArray()); await target.MagicEffectList.AddEffectAsync(magicEffect).ConfigureAwait(false); if (target is ISupportWalk walkSupporter diff --git a/src/GameLogic/Attributes/AttributeSystemExtensions.cs b/src/GameLogic/Attributes/AttributeSystemExtensions.cs index bb75c8116..24af9343c 100644 --- a/src/GameLogic/Attributes/AttributeSystemExtensions.cs +++ b/src/GameLogic/Attributes/AttributeSystemExtensions.cs @@ -18,6 +18,11 @@ public static class AttributeSystemExtensions /// private static AttributeDefinition DurationDummy { get; } = new(new Guid("23D069C3-24D8-4277-8FDC-D82F0AF64037"), "Duration Dummy", "A dummy attribute to be used internally for durations."); + /// + /// Gets the attribute for a dummy attribute to be used internally for chances. + /// + private static AttributeDefinition ChanceDummy { get; } = new(new Guid("E6B9E6A5-5800-40EA-80B7-14C2C06392A6"), "Chance Dummy", "A dummy attribute to be used internally for chances."); + /// /// Creates a new element on this attribute system with the specified power up value. /// @@ -29,6 +34,17 @@ public static IElement CreateDurationElement(this IAttributeSystem attributeSyst return attributeSystem.CreateElement(powerUpDefinition, DurationDummy); } + /// + /// Creates a new element on this attribute system with the specified power up value. + /// + /// The attribute system. + /// The power up definition. + /// The added element. + public static IElement CreateChanceElement(this IAttributeSystem attributeSystem, PowerUpDefinitionValue powerUpDefinition) + { + return attributeSystem.CreateElement(powerUpDefinition, ChanceDummy); + } + /// /// Creates a new element on this attribute system with the specified power up value. /// diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index cf701bdd6..0b294a252 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1464,8 +1464,14 @@ public void CreateMagicEffectPowerUp(SkillEntry skillEntry) int i = 0; var result = new (AttributeDefinition Target, IElement BuffPowerUp)[skill.MagicEffectDef.PowerUpDefinitions.Count]; var durationElement = this.Attributes!.CreateDurationElement(skill.MagicEffectDef.Duration); + var durationElementPvp = skill.MagicEffectDef.DurationPvp is { } durationPvp ? this.Attributes!.CreateDurationElement(durationPvp) : durationElement; + var chanceElement = skillEntry.Skill.MagicEffectDef!.Chance is { } chance ? this.Attributes!.CreateChanceElement(chance) : new ConstantElement(1.0f); + var chanceElementPvp = skillEntry.Skill.MagicEffectDef.ChancePvp is { } chancePvp ? this.Attributes!.CreateChanceElement(chancePvp) : chanceElement; AddSkillPowersToResult(skill); skillEntry.PowerUpDuration = durationElement; + skillEntry.PowerUpDurationPvp = durationElementPvp; + skillEntry.PowerUpChance = chanceElement; + skillEntry.PowerUpChancePvp = chanceElementPvp; skillEntry.PowerUps = result; void AddSkillPowersToResult(Skill skill) diff --git a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs new file mode 100644 index 000000000..7600b85ad --- /dev/null +++ b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs @@ -0,0 +1,95 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Skills; + +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel.Attributes; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Attributes; + +/// +/// Initializer for the sleep buff effect. +/// +public class SleepEffectInitializer : InitializerBase +{ + /// + /// Initializes a new instance of the class. + /// + /// The context. + /// The game configuration. + public SleepEffectInitializer(IContext context, GameConfiguration gameConfiguration) + : base(context, gameConfiguration) + { + } + + /// + public override void Initialize() + { + var magicEffect = this.Context.CreateNew(); + this.GameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Berserker; + magicEffect.Name = "Sleep Buff Skill Effect"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.DurationDependsOnTargetLevel = true; + magicEffect.TargetLevelDivisor = 20; + magicEffect.TargetLevelDivisorPvp = 100; + + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.2f; // 20% + + var chancePerEnergy = this.Context.CreateNew(); + chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + chancePerEnergy.InputOperator = InputOperator.Multiply; + chancePerEnergy.InputOperand = 1f / 3000f; // 30 energy adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerEnergy); + + var chancePerBookRise = this.Context.CreateNew(); + chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(this.GameConfiguration); + chancePerBookRise.InputOperator = InputOperator.Multiply; + chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + + magicEffect.ChancePvp = this.Context.CreateNew(); + magicEffect.ChancePvp.ConstantValue.Value = 0.15f; // 15% + + var chancePerEnergyPvp = this.Context.CreateNew(); + chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + chancePerEnergyPvp.InputOperator = InputOperator.Multiply; + chancePerEnergyPvp.InputOperand = 1f / 3700f; // 37 energy adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp); + + var chancePerBookRisePvp = this.Context.CreateNew(); + chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(this.GameConfiguration); + chancePerBookRisePvp.InputOperator = InputOperator.Multiply; + chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + + magicEffect.Duration = this.Context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 5; // 5 Seconds + + var durationPerEnergy = this.Context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + magicEffect.DurationPvp = this.Context.CreateNew(); + magicEffect.DurationPvp.ConstantValue.Value = 4; // 4 Seconds + + var durationPerEnergyPvp = this.Context.CreateNew(); + durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergyPvp.InputOperator = InputOperator.Multiply; + durationPerEnergyPvp.InputOperand = 1f / 250f; // 250 energy adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp); + + var durationPerLevelPvp = this.Context.CreateNew(); + durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + durationPerLevelPvp.InputOperator = InputOperator.Multiply; + durationPerLevelPvp.InputOperand = 1f / 100f; // 100 levels adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + } +} \ No newline at end of file From 70e4d36f0795ada8c0edad086e356bbe0d0ba545 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 9 Dec 2025 11:29:57 +0000 Subject: [PATCH 12/34] Added migrations and max values --- .../Attributes/PowerUpDefinitionValue.cs | 5 + .../Configuration/MagicEffectDefinition.cs | 21 +- src/DataModel/Entities/SkillEntry.cs | 4 +- src/DataModel/GameConfigurationHelper.cs | 5 +- src/GameLogic/AttackableExtensions.cs | 21 +- src/GameLogic/Player.cs | 8 +- .../MagicEffectDefinition.Generated.cs | 54 + .../ModelBuilder/SkillExtensions.cs | 3 + ..._AddMagicEffectChanceAndOthers.Designer.cs | 5163 +++++++++++++++++ ...208181437_AddMagicEffectChanceAndOthers.cs | 186 + .../EntityDataContextModelSnapshot.cs | 51 + .../Model/ExtendedTypeContext.Generated.cs | 3 + .../Model/MagicEffectDefinition.Generated.cs | 78 + .../Skills/SkillsInitializerBase.cs | 6 - .../Skills/SleepEffectInitializer.cs | 18 +- .../VersionSeasonSix/SkillsInitializer.cs | 2 + 16 files changed, 5595 insertions(+), 33 deletions(-) create mode 100644 src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs create mode 100644 src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs diff --git a/src/DataModel/Attributes/PowerUpDefinitionValue.cs b/src/DataModel/Attributes/PowerUpDefinitionValue.cs index 3ebbc5854..c8227fed0 100644 --- a/src/DataModel/Attributes/PowerUpDefinitionValue.cs +++ b/src/DataModel/Attributes/PowerUpDefinitionValue.cs @@ -27,6 +27,11 @@ public partial class PowerUpDefinitionValue [MemberOfAggregate] public virtual ICollection RelatedValues { get; protected set; } = null!; + /// + /// Gets or sets the maximum allowable value. + /// + public float? MaximumValue { get; set; } + /// public override string ToString() { diff --git a/src/DataModel/Configuration/MagicEffectDefinition.cs b/src/DataModel/Configuration/MagicEffectDefinition.cs index 7a4d47882..19dc2157f 100644 --- a/src/DataModel/Configuration/MagicEffectDefinition.cs +++ b/src/DataModel/Configuration/MagicEffectDefinition.cs @@ -54,29 +54,35 @@ public partial class MagicEffectDefinition /// /// Gets or sets a value indicating whether the duration of the effect depends on the target's level. /// - public bool DurationDependsOnTargetLevel { get; set; } = false; + public bool DurationDependsOnTargetLevel { get; set; } /// /// Gets or sets a value by which the effect target's (monster) level should be divided in case is true. /// - public float TargetLevelDivisor { get; set; } = 1f; + public float MonsterTargetLevelDivisor { get; set; } = 1f; /// /// Gets or sets a value by which the effect target's (player) level should be divided in case is true. /// - public float TargetLevelDivisorPvp { get; set; } = 1f; + public float PlayerTargetLevelDivisor { get; set; } = 1f; /// /// Gets or sets the chance of applying the effect, in decimals. /// + /// + /// Results in a value of 1.0 if not set. + /// [MemberOfAggregate] - public PowerUpDefinitionValue? Chance { get; set; } + public virtual PowerUpDefinitionValue? Chance { get; set; } /// /// Gets or sets the chance of applying the effect in PvP, in decimals. /// + /// + /// Results in the same value as if not set. + /// [MemberOfAggregate] - public PowerUpDefinitionValue? ChancePvp { get; set; } + public virtual PowerUpDefinitionValue? ChancePvp { get; set; } /// /// Gets or sets the duration which describes how long the apply, in seconds. @@ -85,8 +91,11 @@ public partial class MagicEffectDefinition public virtual PowerUpDefinitionValue? Duration { get; set; } /// - /// Gets or sets the duration which describes how long the apply to PVP, in seconds. + /// Gets or sets the duration which describes how long the apply to PvP, in seconds. /// + /// + /// Results in the same value as if not set. + /// [MemberOfAggregate] public virtual PowerUpDefinitionValue? DurationPvp { get; set; } diff --git a/src/DataModel/Entities/SkillEntry.cs b/src/DataModel/Entities/SkillEntry.cs index f8f38fe3c..ebc2bf6d9 100644 --- a/src/DataModel/Entities/SkillEntry.cs +++ b/src/DataModel/Entities/SkillEntry.cs @@ -56,7 +56,7 @@ public int Level /// It is an IElement, because the duration can be dependent from the player attributes. /// [Transient] - public IElement? PowerUpDuration { get; set; } + public (IElement PowerUpDuration, float? MaximumDuration)? PowerUpDuration { get; set; } /// /// Gets or sets the duration of the for PvP. @@ -65,7 +65,7 @@ public int Level /// It is an IElement, because the duration can be dependent from the player attributes. /// [Transient] - public IElement? PowerUpDurationPvp { get; set; } + public (IElement PowerUpDuration, float? MaximumDuration)? PowerUpDurationPvp { get; set; } /// /// Gets or sets the chance of applying the . diff --git a/src/DataModel/GameConfigurationHelper.cs b/src/DataModel/GameConfigurationHelper.cs index a3110f95d..c6e4f1225 100644 --- a/src/DataModel/GameConfigurationHelper.cs +++ b/src/DataModel/GameConfigurationHelper.cs @@ -65,7 +65,10 @@ public static class GameConfigurationHelper { typeof(JewelMix), c => c.JewelMixes }, { typeof(MagicEffectDefinition), c => c.MagicEffects }, { - typeof(PowerUpDefinitionValue), c => c.MagicEffects.Select(e => e.Duration).WhereNotNull() + typeof(PowerUpDefinitionValue), c => c.MagicEffects.Select(e => e.Duration) + .Concat(c.MagicEffects.Select(e => e.DurationPvp)) + .Concat(c.MagicEffects.Select(e => e.Chance)) + .Concat(c.MagicEffects.Select(e => e.ChancePvp)).WhereNotNull() .Concat(Enumerables![typeof(PowerUpDefinition)](c).OfType().Select(p => p.Boost).WhereNotNull()) }, { typeof(SimpleElement), c => Enumerables![typeof(PowerUpDefinitionValue)](c).OfType().Select(v => v.ConstantValue).WhereNotNull() }, diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 64c1b725b..7637d3f2f 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -310,13 +310,14 @@ public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAt player.CreateMagicEffectPowerUp(skillEntry); } - float chance = attacker is Player ? skillEntry.PowerUpChancePvp!.Value : skillEntry.PowerUpChance!.Value; + float chance = target is Player ? skillEntry.PowerUpChancePvp!.Value : skillEntry.PowerUpChance!.Value; if (!Rand.NextRandomBool(Convert.ToDouble(chance))) { return; } - await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, attacker is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!, skillEntry.PowerUps!).ConfigureAwait(false); + var duration = ((IElement, float?))(target is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!); + await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, skillEntry.PowerUps!).ConfigureAwait(false); } /// @@ -430,7 +431,7 @@ public static async ValueTask TryApplyElementalEffectsAsync(this IAttackab && targetAttribute is not null) { // power-up is the wrong term here... it's more like a power-down ;-) - await target.ApplyMagicEffectAsync(attacker, effectDefinition, duration, (targetAttribute, powerUp)).ConfigureAwait(false); + await target.ApplyMagicEffectAsync(attacker, effectDefinition, (duration, null), (targetAttribute, powerUp)).ConfigureAwait(false); applied = true; } @@ -778,17 +779,19 @@ private static void GetBaseDmg(this IAttacker attacker, SkillEntry? skill, out i /// The magic effect definition. /// The duration. /// The power ups of the effect. - private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, IElement duration, params (AttributeDefinition Target, IElement Boost)[] powerUps) + private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, (IElement DurElement, float? MaxDur) duration, params (AttributeDefinition Target, IElement Boost)[] powerUps) { - float levelDeduction = 0; + float finalDuration = duration.DurElement.Value; + if (magicEffectDefinition.DurationDependsOnTargetLevel) { - levelDeduction = target is Player - ? target.Attributes[Stats.Level] / magicEffectDefinition.TargetLevelDivisorPvp - : target.Attributes[Stats.Level] / magicEffectDefinition.TargetLevelDivisor; + finalDuration -= target is Player + ? target.Attributes[Stats.Level] / magicEffectDefinition.PlayerTargetLevelDivisor + : target.Attributes[Stats.Level] / magicEffectDefinition.MonsterTargetLevelDivisor; } - TimeSpan durationSpan = TimeSpan.FromSeconds(duration.Value - levelDeduction); + finalDuration = Math.Min(finalDuration, duration.MaxDur ?? float.MaxValue); + TimeSpan durationSpan = TimeSpan.FromSeconds(finalDuration); if (durationSpan < TimeSpan.FromSeconds(1)) { return; diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 0b294a252..82402b044 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1465,11 +1465,11 @@ public void CreateMagicEffectPowerUp(SkillEntry skillEntry) var result = new (AttributeDefinition Target, IElement BuffPowerUp)[skill.MagicEffectDef.PowerUpDefinitions.Count]; var durationElement = this.Attributes!.CreateDurationElement(skill.MagicEffectDef.Duration); var durationElementPvp = skill.MagicEffectDef.DurationPvp is { } durationPvp ? this.Attributes!.CreateDurationElement(durationPvp) : durationElement; - var chanceElement = skillEntry.Skill.MagicEffectDef!.Chance is { } chance ? this.Attributes!.CreateChanceElement(chance) : new ConstantElement(1.0f); - var chanceElementPvp = skillEntry.Skill.MagicEffectDef.ChancePvp is { } chancePvp ? this.Attributes!.CreateChanceElement(chancePvp) : chanceElement; + var chanceElement = skill.MagicEffectDef.Chance is { } chance ? this.Attributes!.CreateChanceElement(chance) : new ConstantElement(1.0f); + var chanceElementPvp = skill.MagicEffectDef.ChancePvp is { } chancePvp ? this.Attributes!.CreateChanceElement(chancePvp) : chanceElement; AddSkillPowersToResult(skill); - skillEntry.PowerUpDuration = durationElement; - skillEntry.PowerUpDurationPvp = durationElementPvp; + skillEntry.PowerUpDuration = (durationElement, skill.MagicEffectDef.Duration.MaximumValue); + skillEntry.PowerUpDurationPvp = (durationElementPvp, skill.MagicEffectDef.DurationPvp?.MaximumValue); skillEntry.PowerUpChance = chanceElement; skillEntry.PowerUpChancePvp = chanceElementPvp; skillEntry.PowerUps = result; diff --git a/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs b/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs index 74c97cdc1..130f8ef0d 100644 --- a/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs +++ b/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs @@ -46,6 +46,42 @@ protected set } } + /// + /// Gets the raw object of . + /// + [System.Text.Json.Serialization.JsonPropertyName("chance")] + public PowerUpDefinitionValue RawChance + { + get => base.Chance as PowerUpDefinitionValue; + set => base.Chance = value; + } + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue Chance + { + get => base.Chance; + set => base.Chance = value; + } + + /// + /// Gets the raw object of . + /// + [System.Text.Json.Serialization.JsonPropertyName("chancePvp")] + public PowerUpDefinitionValue RawChancePvp + { + get => base.ChancePvp as PowerUpDefinitionValue; + set => base.ChancePvp = value; + } + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue ChancePvp + { + get => base.ChancePvp; + set => base.ChancePvp = value; + } + /// /// Gets the raw object of . /// @@ -64,6 +100,24 @@ public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue Durat set => base.Duration = value; } + /// + /// Gets the raw object of . + /// + [System.Text.Json.Serialization.JsonPropertyName("durationPvp")] + public PowerUpDefinitionValue RawDurationPvp + { + get => base.DurationPvp as PowerUpDefinitionValue; + set => base.DurationPvp = value; + } + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue DurationPvp + { + get => base.DurationPvp; + set => base.DurationPvp = value; + } + /// public override MUnique.OpenMU.DataModel.Configuration.MagicEffectDefinition Clone(MUnique.OpenMU.DataModel.Configuration.GameConfiguration gameConfiguration) { diff --git a/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs b/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs index a3d588695..4aad25b2a 100644 --- a/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs +++ b/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs @@ -20,6 +20,9 @@ public static void Apply(this EntityTypeBuilder builder) { builder.Ignore(s => s.PowerUps); builder.Ignore(s => s.PowerUpDuration); + builder.Ignore(s => s.PowerUpDurationPvp); + builder.Ignore(s => s.PowerUpChance); + builder.Ignore(s => s.PowerUpChancePvp); builder.Ignore(s => s.Attributes); } diff --git a/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs b/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs new file mode 100644 index 000000000..9469fcea7 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs @@ -0,0 +1,5163 @@ +// +using System; +using MUnique.OpenMU.Persistence.EntityFramework; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + [DbContext(typeof(EntityDataContext))] + [Migration("20251208181437_AddMagicEffectChanceAndOthers")] + partial class AddMagicEffectChanceAndOthers + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChatBanUntil") + .HasColumnType("timestamp with time zone"); + + b.Property("EMail") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsTemplate") + .HasColumnType("boolean"); + + b.Property("IsVaultExtended") + .HasColumnType("boolean"); + + b.Property("LoginName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RegistrationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TimeZone") + .HasColumnType("smallint"); + + b.Property("VaultId") + .HasColumnType("uuid"); + + b.Property("VaultPassword") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("LoginName") + .IsUnique(); + + b.HasIndex("VaultId") + .IsUnique(); + + b.ToTable("Account", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountCharacterClass", b => + { + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.HasKey("AccountId", "CharacterClassId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("AccountCharacterClass", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("FullAncientSetEquipped") + .HasColumnType("boolean"); + + b.Property("Pose") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("AppearanceData", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AreaSkillSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DelayBetweenHits") + .HasColumnType("interval"); + + b.Property("DelayPerOneDistance") + .HasColumnType("interval"); + + b.Property("FrustumDistance") + .HasColumnType("real"); + + b.Property("FrustumEndWidth") + .HasColumnType("real"); + + b.Property("FrustumStartWidth") + .HasColumnType("real"); + + b.Property("HitChancePerDistanceMultiplier") + .HasColumnType("real"); + + b.Property("MaximumNumberOfHitsPerAttack") + .HasColumnType("integer"); + + b.Property("MaximumNumberOfHitsPerTarget") + .HasColumnType("integer"); + + b.Property("MinimumNumberOfHitsPerTarget") + .HasColumnType("integer"); + + b.Property("TargetAreaDiameter") + .HasColumnType("real"); + + b.Property("UseDeferredHits") + .HasColumnType("boolean"); + + b.Property("UseFrustumFilter") + .HasColumnType("boolean"); + + b.Property("UseTargetAreaFilter") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("AreaSkillSettings", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Designation") + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MaximumValue") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("AttributeDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRelationship", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("InputAttributeId") + .HasColumnType("uuid"); + + b.Property("InputOperand") + .HasColumnType("real"); + + b.Property("InputOperator") + .HasColumnType("integer"); + + b.Property("OperandAttributeId") + .HasColumnType("uuid"); + + b.Property("PowerUpDefinitionValueId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("InputAttributeId"); + + b.HasIndex("OperandAttributeId"); + + b.HasIndex("PowerUpDefinitionValueId"); + + b.HasIndex("SkillId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("AttributeRelationship", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("MinimumValue") + .HasColumnType("integer"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("SkillId1") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AttributeId"); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("SkillId"); + + b.HasIndex("SkillId1"); + + b.ToTable("AttributeRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GroundId") + .HasColumnType("uuid"); + + b.Property("LeftGoalId") + .HasColumnType("uuid"); + + b.Property("LeftTeamSpawnPointX") + .HasColumnType("smallint"); + + b.Property("LeftTeamSpawnPointY") + .HasColumnType("smallint"); + + b.Property("RightGoalId") + .HasColumnType("uuid"); + + b.Property("RightTeamSpawnPointX") + .HasColumnType("smallint"); + + b.Property("RightTeamSpawnPointY") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GroundId") + .IsUnique(); + + b.HasIndex("LeftGoalId") + .IsUnique(); + + b.HasIndex("RightGoalId") + .IsUnique(); + + b.ToTable("BattleZoneDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("CharacterSlot") + .HasColumnType("smallint"); + + b.Property("CharacterStatus") + .HasColumnType("integer"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CurrentMapId") + .HasColumnType("uuid"); + + b.Property("Experience") + .HasColumnType("bigint"); + + b.Property("InventoryExtensions") + .HasColumnType("integer"); + + b.Property("InventoryId") + .HasColumnType("uuid"); + + b.Property("IsStoreOpened") + .HasColumnType("boolean"); + + b.Property("KeyConfiguration") + .HasColumnType("bytea"); + + b.Property("LevelUpPoints") + .HasColumnType("integer"); + + b.Property("MasterExperience") + .HasColumnType("bigint"); + + b.Property("MasterLevelUpPoints") + .HasColumnType("integer"); + + b.Property("MuHelperConfiguration") + .HasColumnType("bytea"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("PlayerKillCount") + .HasColumnType("integer"); + + b.Property("Pose") + .HasColumnType("smallint"); + + b.Property("PositionX") + .HasColumnType("smallint"); + + b.Property("PositionY") + .HasColumnType("smallint"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("StateRemainingSeconds") + .HasColumnType("integer"); + + b.Property("StoreName") + .HasColumnType("text"); + + b.Property("UsedFruitPoints") + .HasColumnType("integer"); + + b.Property("UsedNegFruitPoints") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("CurrentMapId"); + + b.HasIndex("InventoryId") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Character", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CanGetCreated") + .HasColumnType("boolean"); + + b.Property("ComboDefinitionId") + .HasColumnType("uuid"); + + b.Property("CreationAllowedFlag") + .HasColumnType("smallint"); + + b.Property("FruitCalculation") + .HasColumnType("integer"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("HomeMapId") + .HasColumnType("uuid"); + + b.Property("IsMasterClass") + .HasColumnType("boolean"); + + b.Property("LevelRequirementByCreation") + .HasColumnType("smallint"); + + b.Property("LevelWarpRequirementReductionPercent") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NextGenerationClassId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ComboDefinitionId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("HomeMapId"); + + b.HasIndex("NextGenerationClassId"); + + b.ToTable("CharacterClass", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterDropItemGroup", b => + { + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.HasKey("CharacterId", "DropItemGroupId"); + + b.HasIndex("DropItemGroupId"); + + b.ToTable("CharacterDropItemGroup", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ActiveQuestId") + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("ClientActionPerformed") + .HasColumnType("boolean"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("LastFinishedQuestId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ActiveQuestId"); + + b.HasIndex("CharacterId"); + + b.HasIndex("LastFinishedQuestId"); + + b.ToTable("CharacterQuestState", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientCleanUpInterval") + .HasColumnType("interval"); + + b.Property("ClientTimeout") + .HasColumnType("interval"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaximumConnections") + .HasColumnType("integer"); + + b.Property("RoomCleanUpInterval") + .HasColumnType("interval"); + + b.Property("ServerId") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("ChatServerDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChatServerDefinitionId") + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("NetworkPort") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChatServerDefinitionId"); + + b.HasIndex("ClientId"); + + b.ToTable("ChatServerEndpoint", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CombinationBonusRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemOptionCombinationBonusId") + .HasColumnType("uuid"); + + b.Property("MinimumCount") + .HasColumnType("integer"); + + b.Property("OptionTypeId") + .HasColumnType("uuid"); + + b.Property("SubOptionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemOptionCombinationBonusId"); + + b.HasIndex("OptionTypeId"); + + b.ToTable("CombinationBonusRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConfigurationUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InstalledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ConfigurationUpdate", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConfigurationUpdateState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CurrentInstalledVersion") + .HasColumnType("integer"); + + b.Property("InitializationKey") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("ConfigurationUpdateState", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConnectServerDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CheckMaxConnectionsPerAddress") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("ClientListenerPort") + .HasColumnType("integer"); + + b.Property("CurrentPatchVersion") + .HasColumnType("bytea"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("DisconnectOnUnknownPacket") + .HasColumnType("boolean"); + + b.Property("ListenerBacklog") + .HasColumnType("integer"); + + b.Property("MaxConnections") + .HasColumnType("integer"); + + b.Property("MaxConnectionsPerAddress") + .HasColumnType("integer"); + + b.Property("MaxFtpRequests") + .HasColumnType("integer"); + + b.Property("MaxIpRequests") + .HasColumnType("integer"); + + b.Property("MaxServerListRequests") + .HasColumnType("integer"); + + b.Property("MaximumReceiveSize") + .HasColumnType("smallint"); + + b.Property("PatchAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("ServerId") + .HasColumnType("smallint"); + + b.Property("Timeout") + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ConnectServerDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConstValueAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("DefinitionId"); + + b.ToTable("ConstValueAttribute", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Chance") + .HasColumnType("double precision"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("ItemLevel") + .HasColumnType("smallint"); + + b.Property("ItemType") + .HasColumnType("integer"); + + b.Property("MaximumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MinimumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MonsterId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MonsterId"); + + b.ToTable("DropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroupItemDefinition", b => + { + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("DropItemGroupId", "ItemDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("DropItemGroupItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelArea", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DuelConfigurationId") + .HasColumnType("uuid"); + + b.Property("FirstPlayerGateId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("smallint"); + + b.Property("SecondPlayerGateId") + .HasColumnType("uuid"); + + b.Property("SpectatorsGateId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DuelConfigurationId"); + + b.HasIndex("FirstPlayerGateId"); + + b.HasIndex("SecondPlayerGateId"); + + b.HasIndex("SpectatorsGateId"); + + b.ToTable("DuelArea", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EntranceFee") + .HasColumnType("integer"); + + b.Property("ExitId") + .HasColumnType("uuid"); + + b.Property("MaximumScore") + .HasColumnType("integer"); + + b.Property("MaximumSpectatorsPerDuelRoom") + .HasColumnType("integer"); + + b.Property("MinimumCharacterLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ExitId"); + + b.ToTable("DuelConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.EnterGate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("LevelRequirement") + .HasColumnType("smallint"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("TargetGateId") + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("TargetGateId"); + + b.ToTable("EnterGate", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("IsSpawnGate") + .HasColumnType("boolean"); + + b.Property("MapId") + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("MapId"); + + b.ToTable("ExitGate", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Friend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Accepted") + .HasColumnType("boolean"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("FriendId") + .HasColumnType("uuid"); + + b.Property("RequestOpen") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasAlternateKey("CharacterId", "FriendId"); + + b.ToTable("Friend", "friend"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Episode") + .HasColumnType("smallint"); + + b.Property("Language") + .HasColumnType("integer"); + + b.Property("Season") + .HasColumnType("smallint"); + + b.Property("Serial") + .HasColumnType("bytea"); + + b.Property("Version") + .HasColumnType("bytea"); + + b.HasKey("Id"); + + b.ToTable("GameClientDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AreaSkillHitsPlayer") + .HasColumnType("boolean"); + + b.Property("CharacterNameRegex") + .HasColumnType("text"); + + b.Property("ClampMoneyOnPickup") + .HasColumnType("boolean"); + + b.Property("DamagePerOneItemDurability") + .HasColumnType("double precision"); + + b.Property("DamagePerOnePetDurability") + .HasColumnType("double precision"); + + b.Property("DuelConfigurationId") + .HasColumnType("uuid"); + + b.Property("ExperienceFormula") + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("if(level == 0, 0, if(level < 256, 10 * (level + 8) * (level - 1) * (level - 1), (10 * (level + 8) * (level - 1) * (level - 1)) + (1000 * (level - 247) * (level - 256) * (level - 256))))"); + + b.Property("ExperienceRate") + .HasColumnType("real"); + + b.Property("HitsPerOneItemDurability") + .HasColumnType("double precision"); + + b.Property("InfoRange") + .HasColumnType("smallint"); + + b.Property("ItemDropDuration") + .ValueGeneratedOnAdd() + .HasColumnType("interval") + .HasDefaultValue(new TimeSpan(0, 0, 1, 0, 0)); + + b.Property("LetterSendPrice") + .HasColumnType("integer"); + + b.Property("MasterExperienceFormula") + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("(505 * level * level * level) + (35278500 * level) + (228045 * level * level)"); + + b.Property("MaximumCharactersPerAccount") + .HasColumnType("smallint"); + + b.Property("MaximumInventoryMoney") + .HasColumnType("integer"); + + b.Property("MaximumItemOptionLevelDrop") + .HasColumnType("smallint"); + + b.Property("MaximumLetters") + .HasColumnType("integer"); + + b.Property("MaximumLevel") + .HasColumnType("smallint"); + + b.Property("MaximumMasterLevel") + .HasColumnType("smallint"); + + b.Property("MaximumPartySize") + .HasColumnType("smallint"); + + b.Property("MaximumPasswordLength") + .HasColumnType("integer"); + + b.Property("MaximumVaultMoney") + .HasColumnType("integer"); + + b.Property("MinimumMonsterLevelForMasterExperience") + .HasColumnType("smallint"); + + b.Property("PreventExperienceOverflow") + .HasColumnType("boolean"); + + b.Property("RecoveryInterval") + .HasColumnType("integer"); + + b.Property("ShouldDropMoney") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("DuelConfigurationId") + .IsUnique(); + + b.ToTable("GameConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BattleZoneId") + .HasColumnType("uuid"); + + b.Property("Discriminator") + .HasColumnType("integer"); + + b.Property("ExpMultiplier") + .HasColumnType("double precision"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("SafezoneMapId") + .HasColumnType("uuid"); + + b.Property("TerrainData") + .HasColumnType("bytea"); + + b.HasKey("Id"); + + b.HasIndex("BattleZoneId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("SafezoneMapId"); + + b.ToTable("GameMapDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinitionDropItemGroup", b => + { + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.HasKey("GameMapDefinitionId", "DropItemGroupId"); + + b.HasIndex("DropItemGroupId"); + + b.ToTable("GameMapDefinitionDropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MaximumPlayers") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("GameServerConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfigurationGameMapDefinition", b => + { + b.Property("GameServerConfigurationId") + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("GameServerConfigurationId", "GameMapDefinitionId"); + + b.HasIndex("GameMapDefinitionId"); + + b.ToTable("GameServerConfigurationGameMapDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExperienceRate") + .HasColumnType("real"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("PvpEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ServerConfigurationId") + .HasColumnType("uuid"); + + b.Property("ServerID") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("ServerConfigurationId"); + + b.ToTable("GameServerDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AlternativePublishedPort") + .HasColumnType("integer"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("GameServerDefinitionId") + .HasColumnType("uuid"); + + b.Property("NetworkPort") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("GameServerDefinitionId"); + + b.ToTable("GameServerEndpoint", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllianceGuildId") + .HasColumnType("uuid"); + + b.Property("HostilityId") + .HasColumnType("uuid"); + + b.Property("Logo") + .HasColumnType("bytea"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Notice") + .HasColumnType("text"); + + b.Property("Score") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AllianceGuildId"); + + b.HasIndex("HostilityId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Guild", "guild"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GuildMember", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildMember", "guild"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemOptionDefinitionId") + .HasColumnType("uuid"); + + b.Property("LevelType") + .HasColumnType("integer"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("OptionTypeId") + .HasColumnType("uuid"); + + b.Property("PowerUpDefinitionId") + .HasColumnType("uuid"); + + b.Property("SubOptionType") + .HasColumnType("integer"); + + b.Property("Weight") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ItemOptionDefinitionId"); + + b.HasIndex("OptionTypeId"); + + b.HasIndex("PowerUpDefinitionId") + .IsUnique(); + + b.ToTable("IncreasableItemOption", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("Durability") + .HasColumnType("double precision"); + + b.Property("HasSkill") + .HasColumnType("boolean"); + + b.Property("ItemSlot") + .HasColumnType("smallint"); + + b.Property("ItemStorageId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("PetExperience") + .HasColumnType("integer"); + + b.Property("SocketCount") + .HasColumnType("integer"); + + b.Property("StorePrice") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("DefinitionId"); + + b.HasIndex("ItemStorageId"); + + b.ToTable("Item", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppearanceDataId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemSlot") + .HasColumnType("smallint"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AppearanceDataId"); + + b.HasIndex("DefinitionId"); + + b.ToTable("ItemAppearance", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearanceItemOptionType", b => + { + b.Property("ItemAppearanceId") + .HasColumnType("uuid"); + + b.Property("ItemOptionTypeId") + .HasColumnType("uuid"); + + b.HasKey("ItemAppearanceId", "ItemOptionTypeId"); + + b.HasIndex("ItemOptionTypeId"); + + b.ToTable("ItemAppearanceItemOptionType", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemBasePowerUpDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("BaseValue") + .HasColumnType("real"); + + b.Property("BonusPerLevelTableId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BonusPerLevelTableId"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("ItemBasePowerUpDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCrafting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemCraftingHandlerClassName") + .IsRequired() + .HasColumnType("text"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("SimpleCraftingSettingsId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MonsterDefinitionId"); + + b.HasIndex("SimpleCraftingSettingsId") + .IsUnique(); + + b.ToTable("ItemCrafting", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddPercentage") + .HasColumnType("smallint"); + + b.Property("FailResult") + .HasColumnType("integer"); + + b.Property("MaximumAmount") + .HasColumnType("smallint"); + + b.Property("MaximumItemLevel") + .HasColumnType("smallint"); + + b.Property("MinimumAmount") + .HasColumnType("smallint"); + + b.Property("MinimumItemLevel") + .HasColumnType("smallint"); + + b.Property("NpcPriceDivisor") + .HasColumnType("integer"); + + b.Property("Reference") + .HasColumnType("smallint"); + + b.Property("SimpleCraftingSettingsId") + .HasColumnType("uuid"); + + b.Property("SuccessResult") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SimpleCraftingSettingsId"); + + b.ToTable("ItemCraftingRequiredItem", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemDefinition", b => + { + b.Property("ItemCraftingRequiredItemId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("ItemCraftingRequiredItemId", "ItemDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("ItemCraftingRequiredItemItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemOptionType", b => + { + b.Property("ItemCraftingRequiredItemId") + .HasColumnType("uuid"); + + b.Property("ItemOptionTypeId") + .HasColumnType("uuid"); + + b.HasKey("ItemCraftingRequiredItemId", "ItemOptionTypeId"); + + b.HasIndex("ItemOptionTypeId"); + + b.ToTable("ItemCraftingRequiredItemItemOptionType", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingResultItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddLevel") + .HasColumnType("smallint"); + + b.Property("Durability") + .HasColumnType("smallint"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("RandomMaximumLevel") + .HasColumnType("smallint"); + + b.Property("RandomMinimumLevel") + .HasColumnType("smallint"); + + b.Property("Reference") + .HasColumnType("smallint"); + + b.Property("SimpleCraftingSettingsId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("SimpleCraftingSettingsId"); + + b.ToTable("ItemCraftingResultItem", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConsumeEffectId") + .HasColumnType("uuid"); + + b.Property("DropLevel") + .HasColumnType("smallint"); + + b.Property("DropsFromMonsters") + .HasColumnType("boolean"); + + b.Property("Durability") + .HasColumnType("smallint"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("Height") + .HasColumnType("smallint"); + + b.Property("IsAmmunition") + .HasColumnType("boolean"); + + b.Property("IsBoundToCharacter") + .HasColumnType("boolean"); + + b.Property("ItemSlotId") + .HasColumnType("uuid"); + + b.Property("MaximumItemLevel") + .HasColumnType("smallint"); + + b.Property("MaximumSockets") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("PetExperienceFormula") + .HasColumnType("text"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("StorageLimitPerCharacter") + .HasColumnType("integer"); + + b.Property("Value") + .HasColumnType("integer"); + + b.Property("Width") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ConsumeEffectId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("ItemSlotId"); + + b.HasIndex("SkillId"); + + b.ToTable("ItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionCharacterClass", b => + { + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.HasKey("ItemDefinitionId", "CharacterClassId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("ItemDefinitionCharacterClass", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemOptionDefinition", b => + { + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemOptionDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("ItemDefinitionId", "ItemOptionDefinitionId"); + + b.HasIndex("ItemOptionDefinitionId"); + + b.ToTable("ItemDefinitionItemOptionDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemSetGroup", b => + { + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemSetGroupId") + .HasColumnType("uuid"); + + b.HasKey("ItemDefinitionId", "ItemSetGroupId"); + + b.HasIndex("ItemSetGroupId"); + + b.ToTable("ItemDefinitionItemSetGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Chance") + .HasColumnType("double precision"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("DropEffect") + .HasColumnType("integer"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemLevel") + .HasColumnType("smallint"); + + b.Property("ItemType") + .HasColumnType("integer"); + + b.Property("MaximumLevel") + .HasColumnType("smallint"); + + b.Property("MaximumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MinimumLevel") + .HasColumnType("smallint"); + + b.Property("MinimumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MoneyAmount") + .HasColumnType("integer"); + + b.Property("MonsterId") + .HasColumnType("uuid"); + + b.Property("RequiredCharacterLevel") + .HasColumnType("smallint"); + + b.Property("SourceItemLevel") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("MonsterId"); + + b.ToTable("ItemDropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroupItemDefinition", b => + { + b.Property("ItemDropItemGroupId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("ItemDropItemGroupId", "ItemDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("ItemDropItemGroupItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemItemOfItemSet", b => + { + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("ItemOfItemSetId") + .HasColumnType("uuid"); + + b.HasKey("ItemId", "ItemOfItemSetId"); + + b.HasIndex("ItemOfItemSetId"); + + b.ToTable("ItemItemOfItemSet", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemLevelBonusTable", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOfItemSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AncientSetDiscriminator") + .HasColumnType("integer"); + + b.Property("BonusOptionId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemSetGroupId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BonusOptionId"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("ItemSetGroupId"); + + b.ToTable("ItemOfItemSet", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("OptionTypeId") + .HasColumnType("uuid"); + + b.Property("PowerUpDefinitionId") + .HasColumnType("uuid"); + + b.Property("SubOptionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OptionTypeId"); + + b.HasIndex("PowerUpDefinitionId") + .IsUnique(); + + b.ToTable("ItemOption", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppliesMultipleTimes") + .HasColumnType("boolean"); + + b.Property("BonusId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BonusId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemOptionCombinationBonus", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddChance") + .HasColumnType("real"); + + b.Property("AddsRandomly") + .HasColumnType("boolean"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MaximumOptionsPerItem") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemOptionDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("ItemOptionId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemId"); + + b.HasIndex("ItemOptionId"); + + b.ToTable("ItemOptionLink", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionOfLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IncreasableItemOptionId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("PowerUpDefinitionId") + .HasColumnType("uuid"); + + b.Property("RequiredItemLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IncreasableItemOptionId"); + + b.HasIndex("PowerUpDefinitionId") + .IsUnique(); + + b.ToTable("ItemOptionOfLevel", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IsVisible") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemOptionType", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AlwaysApplies") + .HasColumnType("boolean"); + + b.Property("CountDistinct") + .HasColumnType("boolean"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MinimumItemCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OptionsId") + .HasColumnType("uuid"); + + b.Property("SetLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("OptionsId"); + + b.ToTable("ItemSetGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSlotType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("RawItemSlots") + .HasColumnType("text") + .HasColumnName("ItemSlots") + .HasAnnotation("Relational:JsonPropertyName", "itemSlots"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemSlotType", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Money") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ItemStorage", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.JewelMix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MixedJewelId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("SingleJewelId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MixedJewelId"); + + b.HasIndex("SingleJewelId"); + + b.ToTable("JewelMix", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterBody", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Animation") + .HasColumnType("smallint"); + + b.Property("HeaderId") + .HasColumnType("uuid"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rotation") + .HasColumnType("smallint"); + + b.Property("SenderAppearanceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("HeaderId"); + + b.HasIndex("SenderAppearanceId") + .IsUnique(); + + b.ToTable("LetterBody", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("LetterDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReadFlag") + .HasColumnType("boolean"); + + b.Property("ReceiverId") + .HasColumnType("uuid"); + + b.Property("SenderName") + .HasColumnType("text"); + + b.Property("Subject") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReceiverId"); + + b.ToTable("LetterHeader", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LevelBonus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdditionalValue") + .HasColumnType("real"); + + b.Property("ItemLevelBonusTableId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemLevelBonusTableId"); + + b.ToTable("LevelBonus", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChanceId") + .HasColumnType("uuid"); + + b.Property("ChancePvpId") + .HasColumnType("uuid"); + + b.Property("DurationDependsOnTargetLevel") + .HasColumnType("boolean"); + + b.Property("DurationId") + .HasColumnType("uuid"); + + b.Property("DurationPvpId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("InformObservers") + .HasColumnType("boolean"); + + b.Property("MonsterTargetLevelDivisor") + .HasColumnType("real"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("PlayerTargetLevelDivisor") + .HasColumnType("real"); + + b.Property("SendDuration") + .HasColumnType("boolean"); + + b.Property("StopByDeath") + .HasColumnType("boolean"); + + b.Property("SubType") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ChanceId") + .IsUnique(); + + b.HasIndex("ChancePvpId") + .IsUnique(); + + b.HasIndex("DurationId") + .IsUnique(); + + b.HasIndex("DurationPvpId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("MagicEffectDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aggregation") + .HasColumnType("integer"); + + b.Property("DisplayValueFormula") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExtendsDuration") + .HasColumnType("boolean"); + + b.Property("MaximumLevel") + .HasColumnType("smallint"); + + b.Property("MinimumLevel") + .HasColumnType("smallint"); + + b.Property("Rank") + .HasColumnType("smallint"); + + b.Property("ReplacedSkillId") + .HasColumnType("uuid"); + + b.Property("RootId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.Property("ValueFormula") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedSkillId"); + + b.HasIndex("RootId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("MasterSkillDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinitionSkill", b => + { + b.Property("MasterSkillDefinitionId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.HasKey("MasterSkillDefinitionId", "SkillId"); + + b.HasIndex("SkillId"); + + b.ToTable("MasterSkillDefinitionSkill", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillRoot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("MasterSkillRoot", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("MiniGameDefinitionId") + .HasColumnType("uuid"); + + b.Property("MinimumTargetLevel") + .HasColumnType("smallint"); + + b.Property("MultiplyKillsByPlayers") + .HasColumnType("boolean"); + + b.Property("NumberOfKills") + .HasColumnType("smallint"); + + b.Property("SpawnAreaId") + .HasColumnType("uuid"); + + b.Property("Target") + .HasColumnType("integer"); + + b.Property("TargetDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MiniGameDefinitionId"); + + b.HasIndex("SpawnAreaId") + .IsUnique(); + + b.HasIndex("TargetDefinitionId"); + + b.ToTable("MiniGameChangeEvent", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowParty") + .HasColumnType("boolean"); + + b.Property("ArePlayerKillersAllowedToEnter") + .HasColumnType("boolean"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("EnterDuration") + .HasColumnType("interval"); + + b.Property("EntranceFee") + .HasColumnType("integer"); + + b.Property("EntranceId") + .HasColumnType("uuid"); + + b.Property("ExitDuration") + .HasColumnType("interval"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("GameDuration") + .HasColumnType("interval"); + + b.Property("GameLevel") + .HasColumnType("smallint"); + + b.Property("MapCreationPolicy") + .HasColumnType("integer"); + + b.Property("MaximumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MaximumPlayerCount") + .HasColumnType("integer"); + + b.Property("MaximumSpecialCharacterLevel") + .HasColumnType("integer"); + + b.Property("MinimumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MinimumSpecialCharacterLevel") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("RequiresMasterClass") + .HasColumnType("boolean"); + + b.Property("SaveRankingStatistics") + .HasColumnType("boolean"); + + b.Property("TicketItemId") + .HasColumnType("uuid"); + + b.Property("TicketItemLevel") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EntranceId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("TicketItemId"); + + b.ToTable("MiniGameDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameRankingEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("GameInstanceId") + .HasColumnType("uuid"); + + b.Property("MiniGameId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("MiniGameId"); + + b.ToTable("MiniGameRankingEntry", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemRewardId") + .HasColumnType("uuid"); + + b.Property("MiniGameDefinitionId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("RequiredKillId") + .HasColumnType("uuid"); + + b.Property("RequiredSuccess") + .HasColumnType("integer"); + + b.Property("RewardAmount") + .HasColumnType("integer"); + + b.Property("RewardType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemRewardId"); + + b.HasIndex("MiniGameDefinitionId"); + + b.HasIndex("RequiredKillId"); + + b.ToTable("MiniGameReward", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameSpawnWave", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("interval"); + + b.Property("Message") + .HasColumnType("text"); + + b.Property("MiniGameDefinitionId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("interval"); + + b.Property("WaveNumber") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("MiniGameDefinitionId"); + + b.ToTable("MiniGameSpawnWave", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameTerrainChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EndX") + .HasColumnType("smallint"); + + b.Property("EndY") + .HasColumnType("smallint"); + + b.Property("IsClientUpdateRequired") + .HasColumnType("boolean"); + + b.Property("MiniGameChangeEventId") + .HasColumnType("uuid"); + + b.Property("SetTerrainAttribute") + .HasColumnType("boolean"); + + b.Property("StartX") + .HasColumnType("smallint"); + + b.Property("StartY") + .HasColumnType("smallint"); + + b.Property("TerrainAttribute") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MiniGameChangeEventId"); + + b.ToTable("MiniGameTerrainChange", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeDefinitionId") + .HasColumnType("uuid"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("AttributeDefinitionId"); + + b.HasIndex("MonsterDefinitionId"); + + b.ToTable("MonsterAttribute", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttackDelay") + .HasColumnType("interval"); + + b.Property("AttackRange") + .HasColumnType("smallint"); + + b.Property("AttackSkillId") + .HasColumnType("uuid"); + + b.Property("Attribute") + .HasColumnType("smallint"); + + b.Property("Designation") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IntelligenceTypeName") + .HasColumnType("text"); + + b.Property("MerchantStoreId") + .HasColumnType("uuid"); + + b.Property("MoveDelay") + .HasColumnType("interval"); + + b.Property("MoveRange") + .HasColumnType("smallint"); + + b.Property("NpcWindow") + .HasColumnType("integer"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("NumberOfMaximumItemDrops") + .HasColumnType("integer"); + + b.Property("ObjectKind") + .HasColumnType("integer"); + + b.Property("RespawnDelay") + .HasColumnType("interval"); + + b.Property("ViewRange") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AttackSkillId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MerchantStoreId") + .IsUnique(); + + b.ToTable("MonsterDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinitionDropItemGroup", b => + { + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.HasKey("MonsterDefinitionId", "DropItemGroupId"); + + b.HasIndex("DropItemGroupId"); + + b.ToTable("MonsterDefinitionDropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterSpawnArea", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("GameMapId") + .HasColumnType("uuid"); + + b.Property("MaximumHealthOverride") + .HasColumnType("integer"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Quantity") + .HasColumnType("smallint"); + + b.Property("SpawnTrigger") + .HasColumnType("integer"); + + b.Property("WaveNumber") + .HasColumnType("smallint"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameMapId"); + + b.HasIndex("MonsterDefinitionId"); + + b.ToTable("MonsterSpawnArea", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PlugInConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CustomConfiguration") + .HasColumnType("text"); + + b.Property("CustomPlugInSource") + .HasColumnType("text"); + + b.Property("ExternalAssemblyName") + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("TypeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("PlugInConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BoostId") + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("MagicEffectDefinitionId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BoostId") + .IsUnique(); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("PowerUpDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("MaximumValue") + .HasColumnType("real"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.ToTable("PowerUpDefinitionValue", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("MaximumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MinimumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("QualifiedCharacterId") + .HasColumnType("uuid"); + + b.Property("QuestGiverId") + .HasColumnType("uuid"); + + b.Property("RefuseNumber") + .HasColumnType("smallint"); + + b.Property("Repeatable") + .HasColumnType("boolean"); + + b.Property("RequiredStartMoney") + .HasColumnType("integer"); + + b.Property("RequiresClientAction") + .HasColumnType("boolean"); + + b.Property("StartingNumber") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("MonsterDefinitionId"); + + b.HasIndex("QualifiedCharacterId"); + + b.HasIndex("QuestGiverId"); + + b.ToTable("QuestDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestItemRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("MinimumNumber") + .HasColumnType("integer"); + + b.Property("QuestDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DropItemGroupId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuestDefinitionId"); + + b.ToTable("QuestItemRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MinimumNumber") + .HasColumnType("integer"); + + b.Property("MonsterId") + .HasColumnType("uuid"); + + b.Property("QuestDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MonsterId"); + + b.HasIndex("QuestDefinitionId"); + + b.ToTable("QuestMonsterKillRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirementState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterQuestStateId") + .HasColumnType("uuid"); + + b.Property("KillCount") + .HasColumnType("integer"); + + b.Property("RequirementId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterQuestStateId"); + + b.HasIndex("RequirementId"); + + b.ToTable("QuestMonsterKillRequirementState", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeRewardId") + .HasColumnType("uuid"); + + b.Property("ItemRewardId") + .HasColumnType("uuid"); + + b.Property("QuestDefinitionId") + .HasColumnType("uuid"); + + b.Property("RewardType") + .HasColumnType("integer"); + + b.Property("SkillRewardId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AttributeRewardId"); + + b.HasIndex("ItemRewardId") + .IsUnique(); + + b.HasIndex("QuestDefinitionId"); + + b.HasIndex("SkillRewardId"); + + b.ToTable("QuestReward", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("Rectangle", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MaximumSuccessPercent") + .HasColumnType("smallint"); + + b.Property("Money") + .HasColumnType("integer"); + + b.Property("MoneyPerFinalSuccessPercentage") + .HasColumnType("integer"); + + b.Property("MultipleAllowed") + .HasColumnType("boolean"); + + b.Property("NpcPriceDivisor") + .HasColumnType("integer"); + + b.Property("ResultItemExcellentOptionChance") + .HasColumnType("smallint"); + + b.Property("ResultItemLuckOptionChance") + .HasColumnType("smallint"); + + b.Property("ResultItemMaxExcOptionCount") + .HasColumnType("smallint"); + + b.Property("ResultItemSelect") + .HasColumnType("integer"); + + b.Property("ResultItemSkillChance") + .HasColumnType("smallint"); + + b.Property("SuccessPercent") + .HasColumnType("smallint"); + + b.Property("SuccessPercentageAdditionForAncientItem") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForExcellentItem") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForGuardianItem") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForLuck") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForSocketItem") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("SimpleCraftingSettings", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AreaSkillSettingsId") + .HasColumnType("uuid"); + + b.Property("AttackDamage") + .HasColumnType("integer"); + + b.Property("DamageType") + .HasColumnType("integer"); + + b.Property("ElementalModifierTargetId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("ImplicitTargetRange") + .HasColumnType("smallint"); + + b.Property("MagicEffectDefId") + .HasColumnType("uuid"); + + b.Property("MasterDefinitionId") + .HasColumnType("uuid"); + + b.Property("MovesTarget") + .HasColumnType("boolean"); + + b.Property("MovesToTarget") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("Range") + .HasColumnType("smallint"); + + b.Property("SkillType") + .HasColumnType("integer"); + + b.Property("Target") + .HasColumnType("integer"); + + b.Property("TargetRestriction") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AreaSkillSettingsId") + .IsUnique(); + + b.HasIndex("ElementalModifierTargetId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MagicEffectDefId"); + + b.HasIndex("MasterDefinitionId") + .IsUnique(); + + b.ToTable("Skill", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillCharacterClass", b => + { + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.HasKey("SkillId", "CharacterClassId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("SkillCharacterClass", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MaximumCompletionTime") + .HasColumnType("interval"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SkillComboDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsFinalStep") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("SkillComboDefinitionId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SkillComboDefinitionId"); + + b.HasIndex("SkillId"); + + b.ToTable("SkillComboStep", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("SkillId"); + + b.ToTable("SkillEntry", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CharacterId"); + + b.HasIndex("DefinitionId"); + + b.ToTable("StatAttribute", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttributeDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("BaseValue") + .HasColumnType("real"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("IncreasableByPlayer") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("AttributeId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("StatAttributeDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SystemConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AutoStart") + .HasColumnType("boolean"); + + b.Property("AutoUpdateSchema") + .HasColumnType("boolean"); + + b.Property("IpResolver") + .HasColumnType("integer"); + + b.Property("IpResolverParameter") + .HasColumnType("text"); + + b.Property("ReadConsoleInput") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("SystemConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WarpInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Costs") + .HasColumnType("integer"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("GateId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("LevelRequirement") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("GateId"); + + b.ToTable("WarpInfo", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawVault") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", "VaultId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawVault"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountCharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", "Account") + .WithMany("JoinedUnlockedCharacterClasses") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("CharacterClass"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawCharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId"); + + b.Navigation("RawCharacterClass"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawAttributes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRelationship", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", null) + .WithMany("RawAttributeCombinations") + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawInputAttribute") + .WithMany() + .HasForeignKey("InputAttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawOperandAttribute") + .WithMany() + .HasForeignKey("OperandAttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", null) + .WithMany("RawRelatedValues") + .HasForeignKey("PowerUpDefinitionValueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", null) + .WithMany("RawAttributeRelationships") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawInputAttribute"); + + b.Navigation("RawOperandAttribute"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawMapRequirements") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany("RawRequirements") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", null) + .WithMany("RawConsumeRequirements") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", null) + .WithMany("RawRequirements") + .HasForeignKey("SkillId1") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", "RawGround") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "GroundId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", "RawLeftGoal") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "LeftGoalId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", "RawRightGoal") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "RightGoalId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawGround"); + + b.Navigation("RawLeftGoal"); + + b.Navigation("RawRightGoal"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany("RawCharacters") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawCharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawCurrentMap") + .WithMany() + .HasForeignKey("CurrentMapId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawInventory") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "InventoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawCharacterClass"); + + b.Navigation("RawCurrentMap"); + + b.Navigation("RawInventory"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", "RawComboDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "ComboDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawCharacterClasses") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawHomeMap") + .WithMany() + .HasForeignKey("HomeMapId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawNextGenerationClass") + .WithMany() + .HasForeignKey("NextGenerationClassId"); + + b.Navigation("RawComboDefinition"); + + b.Navigation("RawHomeMap"); + + b.Navigation("RawNextGenerationClass"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "Character") + .WithMany("JoinedDropItemGroups") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("DropItemGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", "RawActiveQuest") + .WithMany() + .HasForeignKey("ActiveQuestId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawQuestStates") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", "RawLastFinishedQuest") + .WithMany() + .HasForeignKey("LastFinishedQuestId"); + + b.Navigation("RawActiveQuest"); + + b.Navigation("RawLastFinishedQuest"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerEndpoint", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerDefinition", null) + .WithMany("RawEndpoints") + .HasForeignKey("ChatServerDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", "RawClient") + .WithMany() + .HasForeignKey("ClientId"); + + b.Navigation("RawClient"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CombinationBonusRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", null) + .WithMany("RawRequirements") + .HasForeignKey("ItemOptionCombinationBonusId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "RawOptionType") + .WithMany() + .HasForeignKey("OptionTypeId"); + + b.Navigation("RawOptionType"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConnectServerDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", "RawClient") + .WithMany() + .HasForeignKey("ClientId"); + + b.Navigation("RawClient"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConstValueAttribute", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany("RawBaseAttributeValues") + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.Navigation("CharacterClass"); + + b.Navigation("RawDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawDropItemGroups") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonster") + .WithMany() + .HasForeignKey("MonsterId"); + + b.Navigation("RawMonster"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroupItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany("JoinedPossibleItems") + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DropItemGroup"); + + b.Navigation("ItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelArea", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", null) + .WithMany("RawDuelAreas") + .HasForeignKey("DuelConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawFirstPlayerGate") + .WithMany() + .HasForeignKey("FirstPlayerGateId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawSecondPlayerGate") + .WithMany() + .HasForeignKey("SecondPlayerGateId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawSpectatorsGate") + .WithMany() + .HasForeignKey("SpectatorsGateId"); + + b.Navigation("RawFirstPlayerGate"); + + b.Navigation("RawSecondPlayerGate"); + + b.Navigation("RawSpectatorsGate"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawExit") + .WithMany() + .HasForeignKey("ExitId"); + + b.Navigation("RawExit"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.EnterGate", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawEnterGates") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawTargetGate") + .WithMany() + .HasForeignKey("TargetGateId"); + + b.Navigation("RawTargetGate"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawMap") + .WithMany("RawExitGates") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawMap"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", "RawDuelConfiguration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "DuelConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawDuelConfiguration"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "RawBattleZone") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "BattleZoneId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMaps") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawSafezoneMap") + .WithMany() + .HasForeignKey("SafezoneMapId"); + + b.Navigation("RawBattleZone"); + + b.Navigation("RawSafezoneMap"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinitionDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "GameMapDefinition") + .WithMany("JoinedDropItemGroups") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DropItemGroup"); + + b.Navigation("GameMapDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfigurationGameMapDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "GameMapDefinition") + .WithMany() + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", "GameServerConfiguration") + .WithMany("JoinedMaps") + .HasForeignKey("GameServerConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameMapDefinition"); + + b.Navigation("GameServerConfiguration"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "RawGameConfiguration") + .WithMany() + .HasForeignKey("GameConfigurationId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", "RawServerConfiguration") + .WithMany() + .HasForeignKey("ServerConfigurationId"); + + b.Navigation("RawGameConfiguration"); + + b.Navigation("RawServerConfiguration"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerEndpoint", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", "RawClient") + .WithMany() + .HasForeignKey("ClientId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", null) + .WithMany("RawEndpoints") + .HasForeignKey("GameServerDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawClient"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", "RawAllianceGuild") + .WithMany() + .HasForeignKey("AllianceGuildId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", "RawHostility") + .WithMany() + .HasForeignKey("HostilityId"); + + b.Navigation("RawAllianceGuild"); + + b.Navigation("RawHostility"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GuildMember", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany("RawMembers") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "Character") + .WithMany() + .HasForeignKey("Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", null) + .WithMany("RawPossibleOptions") + .HasForeignKey("ItemOptionDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "RawOptionType") + .WithMany() + .HasForeignKey("OptionTypeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawPowerUpDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", "PowerUpDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawOptionType"); + + b.Navigation("RawPowerUpDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawItemStorage") + .WithMany("RawItems") + .HasForeignKey("ItemStorageId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawDefinition"); + + b.Navigation("RawItemStorage"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", null) + .WithMany("RawEquippedItems") + .HasForeignKey("AppearanceDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.Navigation("RawDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearanceItemOptionType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", "ItemAppearance") + .WithMany("JoinedVisibleOptions") + .HasForeignKey("ItemAppearanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "ItemOptionType") + .WithMany() + .HasForeignKey("ItemOptionTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemAppearance"); + + b.Navigation("ItemOptionType"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemBasePowerUpDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", "RawBonusPerLevelTable") + .WithMany() + .HasForeignKey("BonusPerLevelTableId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany("RawBasePowerUpAttributes") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawBonusPerLevelTable"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCrafting", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawItemCraftings") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", "RawSimpleCraftingSettings") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCrafting", "SimpleCraftingSettingsId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawSimpleCraftingSettings"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", null) + .WithMany("RawRequiredItems") + .HasForeignKey("SimpleCraftingSettingsId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", "ItemCraftingRequiredItem") + .WithMany("JoinedPossibleItems") + .HasForeignKey("ItemCraftingRequiredItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemCraftingRequiredItem"); + + b.Navigation("ItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemOptionType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", "ItemCraftingRequiredItem") + .WithMany("JoinedRequiredItemOptions") + .HasForeignKey("ItemCraftingRequiredItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "ItemOptionType") + .WithMany() + .HasForeignKey("ItemOptionTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemCraftingRequiredItem"); + + b.Navigation("ItemOptionType"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingResultItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", null) + .WithMany("RawResultItems") + .HasForeignKey("SimpleCraftingSettingsId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "RawConsumeEffect") + .WithMany() + .HasForeignKey("ConsumeEffectId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItems") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSlotType", "RawItemSlot") + .WithMany() + .HasForeignKey("ItemSlotId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.Navigation("RawConsumeEffect"); + + b.Navigation("RawItemSlot"); + + b.Navigation("RawSkill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionCharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany("JoinedQualifiedCharacters") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CharacterClass"); + + b.Navigation("ItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemOptionDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany("JoinedPossibleItemOptions") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", "ItemOptionDefinition") + .WithMany() + .HasForeignKey("ItemOptionDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemDefinition"); + + b.Navigation("ItemOptionDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemSetGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany("JoinedPossibleItemSetGroups") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", "ItemSetGroup") + .WithMany() + .HasForeignKey("ItemSetGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemDefinition"); + + b.Navigation("ItemSetGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany("RawDropItems") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonster") + .WithMany() + .HasForeignKey("MonsterId"); + + b.Navigation("RawMonster"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroupItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", "ItemDropItemGroup") + .WithMany("JoinedPossibleItems") + .HasForeignKey("ItemDropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemDefinition"); + + b.Navigation("ItemDropItemGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemItemOfItemSet", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", "Item") + .WithMany("JoinedItemSetGroups") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOfItemSet", "ItemOfItemSet") + .WithMany() + .HasForeignKey("ItemOfItemSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("ItemOfItemSet"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemLevelBonusTables") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOfItemSet", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", "RawBonusOption") + .WithMany() + .HasForeignKey("BonusOptionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", "RawItemSetGroup") + .WithMany("RawItems") + .HasForeignKey("ItemSetGroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawBonusOption"); + + b.Navigation("RawItemDefinition"); + + b.Navigation("RawItemSetGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOption", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "RawOptionType") + .WithMany() + .HasForeignKey("OptionTypeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawPowerUpDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOption", "PowerUpDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawOptionType"); + + b.Navigation("RawPowerUpDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawBonus") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", "BonusId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemOptionCombinationBonuses") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawBonus"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemOptions") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionLink", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", null) + .WithMany("RawItemOptions") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", "RawItemOption") + .WithMany() + .HasForeignKey("ItemOptionId"); + + b.Navigation("RawItemOption"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionOfLevel", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", null) + .WithMany("RawLevelDependentOptions") + .HasForeignKey("IncreasableItemOptionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawPowerUpDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionOfLevel", "PowerUpDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawPowerUpDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemOptionTypes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemSetGroups") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", "RawOptions") + .WithMany() + .HasForeignKey("OptionsId"); + + b.Navigation("RawOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSlotType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemSlotTypes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.JewelMix", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawJewelMixes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawMixedJewel") + .WithMany() + .HasForeignKey("MixedJewelId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawSingleJewel") + .WithMany() + .HasForeignKey("SingleJewelId"); + + b.Navigation("RawMixedJewel"); + + b.Navigation("RawSingleJewel"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterBody", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterHeader", "RawHeader") + .WithMany() + .HasForeignKey("HeaderId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", "RawSenderAppearance") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterBody", "SenderAppearanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawHeader"); + + b.Navigation("RawSenderAppearance"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterHeader", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "Receiver") + .WithMany("RawLetters") + .HasForeignKey("ReceiverId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Receiver"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LevelBonus", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", null) + .WithMany("RawBonusPerLevel") + .HasForeignKey("ItemLevelBonusTableId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawChance") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "ChanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawChancePvp") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "ChancePvpId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawDuration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "DurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawDurationPvp") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "DurationPvpId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMagicEffects") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawChance"); + + b.Navigation("RawChancePvp"); + + b.Navigation("RawDuration"); + + b.Navigation("RawDurationPvp"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawReplacedSkill") + .WithMany() + .HasForeignKey("ReplacedSkillId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillRoot", "RawRoot") + .WithMany() + .HasForeignKey("RootId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawReplacedSkill"); + + b.Navigation("RawRoot"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinitionSkill", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", "MasterSkillDefinition") + .WithMany("JoinedRequiredMasterSkills") + .HasForeignKey("MasterSkillDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MasterSkillDefinition"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillRoot", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMasterSkillRoots") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", null) + .WithMany("RawChangeEvents") + .HasForeignKey("MiniGameDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterSpawnArea", "RawSpawnArea") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", "SpawnAreaId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawTargetDefinition") + .WithMany() + .HasForeignKey("TargetDefinitionId"); + + b.Navigation("RawSpawnArea"); + + b.Navigation("RawTargetDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawEntrance") + .WithMany() + .HasForeignKey("EntranceId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMiniGameDefinitions") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawTicketItem") + .WithMany() + .HasForeignKey("TicketItemId"); + + b.Navigation("RawEntrance"); + + b.Navigation("RawTicketItem"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameRankingEntry", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "RawCharacter") + .WithMany() + .HasForeignKey("CharacterId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", "RawMiniGame") + .WithMany() + .HasForeignKey("MiniGameId"); + + b.Navigation("RawCharacter"); + + b.Navigation("RawMiniGame"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameReward", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "RawItemReward") + .WithMany() + .HasForeignKey("ItemRewardId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", null) + .WithMany("RawRewards") + .HasForeignKey("MiniGameDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawRequiredKill") + .WithMany() + .HasForeignKey("RequiredKillId"); + + b.Navigation("RawItemReward"); + + b.Navigation("RawRequiredKill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameSpawnWave", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", null) + .WithMany("RawSpawnWaves") + .HasForeignKey("MiniGameDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameTerrainChange", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", null) + .WithMany("RawTerrainChanges") + .HasForeignKey("MiniGameChangeEventId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterAttribute", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttributeDefinition") + .WithMany() + .HasForeignKey("AttributeDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawAttributes") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttributeDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawAttackSkill") + .WithMany() + .HasForeignKey("AttackSkillId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMonsters") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawMerchantStore") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "MerchantStoreId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttackSkill"); + + b.Navigation("RawMerchantStore"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinitionDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "MonsterDefinition") + .WithMany("JoinedDropItemGroups") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DropItemGroup"); + + b.Navigation("MonsterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterSpawnArea", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawGameMap") + .WithMany("RawMonsterSpawns") + .HasForeignKey("GameMapId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonsterDefinition") + .WithMany() + .HasForeignKey("MonsterDefinitionId"); + + b.Navigation("RawGameMap"); + + b.Navigation("RawMonsterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PlugInConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawPlugInConfigurations") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawBoost") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "BoostId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawCharacterPowerUpDefinitions") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", null) + .WithMany("RawPowerUpDefinitions") + .HasForeignKey("MagicEffectDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawBoost"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawQuests") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawQualifiedCharacter") + .WithMany() + .HasForeignKey("QualifiedCharacterId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawQuestGiver") + .WithMany() + .HasForeignKey("QuestGiverId"); + + b.Navigation("RawQualifiedCharacter"); + + b.Navigation("RawQuestGiver"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestItemRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "RawDropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItem") + .WithMany() + .HasForeignKey("ItemId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", null) + .WithMany("RawRequiredItems") + .HasForeignKey("QuestDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawDropItemGroup"); + + b.Navigation("RawItem"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonster") + .WithMany() + .HasForeignKey("MonsterId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", null) + .WithMany("RawRequiredMonsterKills") + .HasForeignKey("QuestDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawMonster"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirementState", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", null) + .WithMany("RawRequirementStates") + .HasForeignKey("CharacterQuestStateId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirement", "RawRequirement") + .WithMany() + .HasForeignKey("RequirementId"); + + b.Navigation("RawRequirement"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestReward", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttributeReward") + .WithMany() + .HasForeignKey("AttributeRewardId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", "RawItemReward") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestReward", "ItemRewardId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", null) + .WithMany("RawRewards") + .HasForeignKey("QuestDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkillReward") + .WithMany() + .HasForeignKey("SkillRewardId"); + + b.Navigation("RawAttributeReward"); + + b.Navigation("RawItemReward"); + + b.Navigation("RawSkillReward"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AreaSkillSettings", "RawAreaSkillSettings") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "AreaSkillSettingsId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawElementalModifierTarget") + .WithMany() + .HasForeignKey("ElementalModifierTargetId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawSkills") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "RawMagicEffectDef") + .WithMany() + .HasForeignKey("MagicEffectDefId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", "RawMasterDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "MasterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAreaSkillSettings"); + + b.Navigation("RawElementalModifierTarget"); + + b.Navigation("RawMagicEffectDef"); + + b.Navigation("RawMasterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillCharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "Skill") + .WithMany("JoinedQualifiedCharacters") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CharacterClass"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboStep", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", null) + .WithMany("RawSteps") + .HasForeignKey("SkillComboDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.Navigation("RawSkill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillEntry", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawLearnedSkills") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.Navigation("RawSkill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttribute", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany("RawAttributes") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawAttributes") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.Navigation("RawDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttributeDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", null) + .WithMany("RawStatAttributes") + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WarpInfo", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawWarpList") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawGate") + .WithMany() + .HasForeignKey("GateId"); + + b.Navigation("RawGate"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", b => + { + b.Navigation("JoinedUnlockedCharacterClasses"); + + b.Navigation("RawAttributes"); + + b.Navigation("RawCharacters"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.Navigation("RawEquippedItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", b => + { + b.Navigation("JoinedDropItemGroups"); + + b.Navigation("RawAttributes"); + + b.Navigation("RawLearnedSkills"); + + b.Navigation("RawLetters"); + + b.Navigation("RawQuestStates"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => + { + b.Navigation("RawAttributeCombinations"); + + b.Navigation("RawBaseAttributeValues"); + + b.Navigation("RawStatAttributes"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", b => + { + b.Navigation("RawRequirementStates"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerDefinition", b => + { + b.Navigation("RawEndpoints"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", b => + { + b.Navigation("JoinedPossibleItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", b => + { + b.Navigation("RawDuelAreas"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", b => + { + b.Navigation("RawAttributes"); + + b.Navigation("RawCharacterClasses"); + + b.Navigation("RawDropItemGroups"); + + b.Navigation("RawItemLevelBonusTables"); + + b.Navigation("RawItemOptionCombinationBonuses"); + + b.Navigation("RawItemOptionTypes"); + + b.Navigation("RawItemOptions"); + + b.Navigation("RawItemSetGroups"); + + b.Navigation("RawItemSlotTypes"); + + b.Navigation("RawItems"); + + b.Navigation("RawJewelMixes"); + + b.Navigation("RawMagicEffects"); + + b.Navigation("RawMaps"); + + b.Navigation("RawMasterSkillRoots"); + + b.Navigation("RawMiniGameDefinitions"); + + b.Navigation("RawMonsters"); + + b.Navigation("RawPlugInConfigurations"); + + b.Navigation("RawSkills"); + + b.Navigation("RawWarpList"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", b => + { + b.Navigation("JoinedDropItemGroups"); + + b.Navigation("RawCharacterPowerUpDefinitions"); + + b.Navigation("RawEnterGates"); + + b.Navigation("RawExitGates"); + + b.Navigation("RawMapRequirements"); + + b.Navigation("RawMonsterSpawns"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", b => + { + b.Navigation("JoinedMaps"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.Navigation("RawEndpoints"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", b => + { + b.Navigation("RawMembers"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", b => + { + b.Navigation("RawLevelDependentOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", b => + { + b.Navigation("JoinedItemSetGroups"); + + b.Navigation("RawItemOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", b => + { + b.Navigation("JoinedVisibleOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", b => + { + b.Navigation("JoinedPossibleItems"); + + b.Navigation("JoinedRequiredItemOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", b => + { + b.Navigation("JoinedPossibleItemOptions"); + + b.Navigation("JoinedPossibleItemSetGroups"); + + b.Navigation("JoinedQualifiedCharacters"); + + b.Navigation("RawBasePowerUpAttributes"); + + b.Navigation("RawDropItems"); + + b.Navigation("RawRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", b => + { + b.Navigation("JoinedPossibleItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", b => + { + b.Navigation("RawBonusPerLevel"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", b => + { + b.Navigation("RawRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", b => + { + b.Navigation("RawPossibleOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", b => + { + b.Navigation("RawItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", b => + { + b.Navigation("RawItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => + { + b.Navigation("RawPowerUpDefinitions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => + { + b.Navigation("JoinedRequiredMasterSkills"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", b => + { + b.Navigation("RawTerrainChanges"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", b => + { + b.Navigation("RawChangeEvents"); + + b.Navigation("RawRewards"); + + b.Navigation("RawSpawnWaves"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", b => + { + b.Navigation("JoinedDropItemGroups"); + + b.Navigation("RawAttributes"); + + b.Navigation("RawItemCraftings"); + + b.Navigation("RawQuests"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", b => + { + b.Navigation("RawRelatedValues"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", b => + { + b.Navigation("RawRequiredItems"); + + b.Navigation("RawRequiredMonsterKills"); + + b.Navigation("RawRewards"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", b => + { + b.Navigation("RawRequiredItems"); + + b.Navigation("RawResultItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", b => + { + b.Navigation("JoinedQualifiedCharacters"); + + b.Navigation("RawAttributeRelationships"); + + b.Navigation("RawConsumeRequirements"); + + b.Navigation("RawRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", b => + { + b.Navigation("RawSteps"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs b/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs new file mode 100644 index 000000000..b429c129a --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs @@ -0,0 +1,186 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + /// + public partial class AddMagicEffectChanceAndOthers : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "MaximumValue", + schema: "config", + table: "PowerUpDefinitionValue", + type: "real", + nullable: true); + + migrationBuilder.AddColumn( + name: "ChanceId", + schema: "config", + table: "MagicEffectDefinition", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "ChancePvpId", + schema: "config", + table: "MagicEffectDefinition", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "DurationDependsOnTargetLevel", + schema: "config", + table: "MagicEffectDefinition", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "DurationPvpId", + schema: "config", + table: "MagicEffectDefinition", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "MonsterTargetLevelDivisor", + schema: "config", + table: "MagicEffectDefinition", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.AddColumn( + name: "PlayerTargetLevelDivisor", + schema: "config", + table: "MagicEffectDefinition", + type: "real", + nullable: false, + defaultValue: 0f); + + migrationBuilder.CreateIndex( + name: "IX_MagicEffectDefinition_ChanceId", + schema: "config", + table: "MagicEffectDefinition", + column: "ChanceId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_MagicEffectDefinition_ChancePvpId", + schema: "config", + table: "MagicEffectDefinition", + column: "ChancePvpId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_MagicEffectDefinition_DurationPvpId", + schema: "config", + table: "MagicEffectDefinition", + column: "DurationPvpId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_MagicEffectDefinition_PowerUpDefinitionValue_ChanceId", + schema: "config", + table: "MagicEffectDefinition", + column: "ChanceId", + principalSchema: "config", + principalTable: "PowerUpDefinitionValue", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MagicEffectDefinition_PowerUpDefinitionValue_ChancePvpId", + schema: "config", + table: "MagicEffectDefinition", + column: "ChancePvpId", + principalSchema: "config", + principalTable: "PowerUpDefinitionValue", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MagicEffectDefinition_PowerUpDefinitionValue_DurationPvpId", + schema: "config", + table: "MagicEffectDefinition", + column: "DurationPvpId", + principalSchema: "config", + principalTable: "PowerUpDefinitionValue", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_MagicEffectDefinition_PowerUpDefinitionValue_ChanceId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropForeignKey( + name: "FK_MagicEffectDefinition_PowerUpDefinitionValue_ChancePvpId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropForeignKey( + name: "FK_MagicEffectDefinition_PowerUpDefinitionValue_DurationPvpId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropIndex( + name: "IX_MagicEffectDefinition_ChanceId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropIndex( + name: "IX_MagicEffectDefinition_ChancePvpId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropIndex( + name: "IX_MagicEffectDefinition_DurationPvpId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropColumn( + name: "MaximumValue", + schema: "config", + table: "PowerUpDefinitionValue"); + + migrationBuilder.DropColumn( + name: "ChanceId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropColumn( + name: "ChancePvpId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropColumn( + name: "DurationDependsOnTargetLevel", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropColumn( + name: "DurationPvpId", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropColumn( + name: "MonsterTargetLevelDivisor", + schema: "config", + table: "MagicEffectDefinition"); + + migrationBuilder.DropColumn( + name: "PlayerTargetLevelDivisor", + schema: "config", + table: "MagicEffectDefinition"); + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs index 902947166..ffcb56578 100644 --- a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs +++ b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs @@ -2214,15 +2214,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uuid"); + b.Property("ChanceId") + .HasColumnType("uuid"); + + b.Property("ChancePvpId") + .HasColumnType("uuid"); + + b.Property("DurationDependsOnTargetLevel") + .HasColumnType("boolean"); + b.Property("DurationId") .HasColumnType("uuid"); + b.Property("DurationPvpId") + .HasColumnType("uuid"); + b.Property("GameConfigurationId") .HasColumnType("uuid"); b.Property("InformObservers") .HasColumnType("boolean"); + b.Property("MonsterTargetLevelDivisor") + .HasColumnType("real"); + b.Property("Name") .IsRequired() .HasColumnType("text"); @@ -2230,6 +2245,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Number") .HasColumnType("smallint"); + b.Property("PlayerTargetLevelDivisor") + .HasColumnType("real"); + b.Property("SendDuration") .HasColumnType("boolean"); @@ -2241,9 +2259,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("ChanceId") + .IsUnique(); + + b.HasIndex("ChancePvpId") + .IsUnique(); + b.HasIndex("DurationId") .IsUnique(); + b.HasIndex("DurationPvpId") + .IsUnique(); + b.HasIndex("GameConfigurationId"); b.ToTable("MagicEffectDefinition", "config"); @@ -2831,6 +2858,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AggregateType") .HasColumnType("integer"); + b.Property("MaximumValue") + .HasColumnType("real"); + b.Property("Value") .HasColumnType("real"); @@ -4356,17 +4386,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawChance") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "ChanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawChancePvp") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "ChancePvpId") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawDuration") .WithOne() .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "DurationId") .OnDelete(DeleteBehavior.Cascade); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawDurationPvp") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "DurationPvpId") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) .WithMany("RawMagicEffects") .HasForeignKey("GameConfigurationId") .OnDelete(DeleteBehavior.Cascade); + b.Navigation("RawChance"); + + b.Navigation("RawChancePvp"); + b.Navigation("RawDuration"); + + b.Navigation("RawDurationPvp"); }); modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => diff --git a/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs b/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs index fb9921ed0..f3ac09a43 100644 --- a/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs +++ b/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs @@ -162,7 +162,10 @@ protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuild modelBuilder.Entity().HasMany(entity => entity.RawMapRequirements).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawCharacterPowerUpDefinitions).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawEndpoints).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(entity => entity.RawChance).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(entity => entity.RawChancePvp).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawDuration).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(entity => entity.RawDurationPvp).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawPowerUpDefinitions).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawSpawnArea).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawTerrainChanges).WithOne().OnDelete(DeleteBehavior.Cascade); diff --git a/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs b/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs index 3ec14e756..4f597d49e 100644 --- a/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs +++ b/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs @@ -37,6 +37,58 @@ internal partial class MagicEffectDefinition : MUnique.OpenMU.DataModel.Configur [NotMapped] public override ICollection PowerUpDefinitions => base.PowerUpDefinitions ??= new CollectionAdapter(this.RawPowerUpDefinitions); + /// + /// Gets or sets the identifier of . + /// + public Guid? ChanceId { get; set; } + + /// + /// Gets the raw object of . + /// + [ForeignKey(nameof(ChanceId))] + public PowerUpDefinitionValue RawChance + { + get => base.Chance as PowerUpDefinitionValue; + set => base.Chance = value; + } + + /// + [NotMapped] + public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue Chance + { + get => base.Chance;set + { + base.Chance = value; + this.ChanceId = this.RawChance?.Id; + } + } + + /// + /// Gets or sets the identifier of . + /// + public Guid? ChancePvpId { get; set; } + + /// + /// Gets the raw object of . + /// + [ForeignKey(nameof(ChancePvpId))] + public PowerUpDefinitionValue RawChancePvp + { + get => base.ChancePvp as PowerUpDefinitionValue; + set => base.ChancePvp = value; + } + + /// + [NotMapped] + public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue ChancePvp + { + get => base.ChancePvp;set + { + base.ChancePvp = value; + this.ChancePvpId = this.RawChancePvp?.Id; + } + } + /// /// Gets or sets the identifier of . /// @@ -63,6 +115,32 @@ public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue Durat } } + /// + /// Gets or sets the identifier of . + /// + public Guid? DurationPvpId { get; set; } + + /// + /// Gets the raw object of . + /// + [ForeignKey(nameof(DurationPvpId))] + public PowerUpDefinitionValue RawDurationPvp + { + get => base.DurationPvp as PowerUpDefinitionValue; + set => base.DurationPvp = value; + } + + /// + [NotMapped] + public override MUnique.OpenMU.DataModel.Attributes.PowerUpDefinitionValue DurationPvp + { + get => base.DurationPvp;set + { + base.DurationPvp = value; + this.DurationPvpId = this.RawDurationPvp?.Id; + } + } + /// public override MUnique.OpenMU.DataModel.Configuration.MagicEffectDefinition Clone(MUnique.OpenMU.DataModel.Configuration.GameConfiguration gameConfiguration) { diff --git a/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs b/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs index 7502f4ac4..6d7863c1b 100644 --- a/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs +++ b/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs @@ -164,12 +164,6 @@ private void ApplyElementalModifier(ElementalType elementalModifier, Skill skill return; } - if ((SkillNumber)skill.Number is SkillNumber.Sleep) - { - skill.MagicEffectDef = this.CreateEffect(ElementalType.Undefined, MagicEffectNumber.Sleep, Stats.IsAsleep, 5); - return; - } - switch (elementalModifier) { case ElementalType.Ice: diff --git a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs index 7600b85ad..14aaf7361 100644 --- a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs @@ -10,7 +10,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Skills; using MUnique.OpenMU.GameLogic.Attributes; /// -/// Initializer for the sleep buff effect. +/// Initializer for the sleep effect. /// public class SleepEffectInitializer : InitializerBase { @@ -29,14 +29,14 @@ public override void Initialize() { var magicEffect = this.Context.CreateNew(); this.GameConfiguration.MagicEffects.Add(magicEffect); - magicEffect.Number = (short)MagicEffectNumber.Berserker; - magicEffect.Name = "Sleep Buff Skill Effect"; + magicEffect.Number = (short)MagicEffectNumber.Sleep; + magicEffect.Name = "Sleep Skill Effect"; magicEffect.InformObservers = true; magicEffect.SendDuration = false; magicEffect.StopByDeath = true; magicEffect.DurationDependsOnTargetLevel = true; - magicEffect.TargetLevelDivisor = 20; - magicEffect.TargetLevelDivisorPvp = 100; + magicEffect.MonsterTargetLevelDivisor = 20; + magicEffect.PlayerTargetLevelDivisor = 100; magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.2f; // 20% @@ -70,6 +70,7 @@ public override void Initialize() magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 5; // 5 Seconds + magicEffect.Duration.MaximumValue = 20; // 20 Seconds var durationPerEnergy = this.Context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -79,6 +80,7 @@ public override void Initialize() magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 4; // 4 Seconds + magicEffect.DurationPvp.MaximumValue = 10; // 10 Seconds var durationPerEnergyPvp = this.Context.CreateNew(); durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -91,5 +93,11 @@ public override void Initialize() durationPerLevelPvp.InputOperator = InputOperator.Multiply; durationPerLevelPvp.InputOperand = 1f / 100f; // 100 levels adds 1s magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + + var isAsleep = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(isAsleep); + isAsleep.TargetAttribute = Stats.IsAsleep.GetPersistent(this.GameConfiguration); + isAsleep.Boost = this.Context.CreateNew(); + isAsleep.Boost.ConstantValue.Value = 1; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index b1429d5d3..1bcb40ee6 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -67,6 +67,7 @@ internal class SkillsInitializer : SkillsInitializerBase { SkillNumber.ExpansionofWizardry, MagicEffectNumber.WizEnhance }, { SkillNumber.Berserker, MagicEffectNumber.Berserker }, { SkillNumber.KillingBlow, MagicEffectNumber.Weakness }, + { SkillNumber.Sleep, MagicEffectNumber.Sleep }, }; private readonly IDictionary _masterSkillRoots; @@ -662,6 +663,7 @@ private void InitializeEffects() new BlessPotionEffectInitializer(this.Context, this.GameConfiguration).Initialize(); new BerserkerEffectInitializer(this.Context, this.GameConfiguration).Initialize(); new WeaknessEffectInitializer(this.Context, this.GameConfiguration).Initialize(); + new SleepEffectInitializer(this.Context, this.GameConfiguration).Initialize(); } private void MapSkillsToEffects() From e32f6af6f1d1376bcacb995d0d75e5421dc02e42 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 23 Dec 2025 15:59:54 +0000 Subject: [PATCH 13/34] Added innovation skill and more --- .../Configuration/MagicEffectDefinition.cs | 9 ++ src/DataModel/Entities/SkillEntry.cs | 6 + src/DataModel/GameConfigurationHelper.cs | 1 + src/GameLogic/AttackableExtensions.cs | 10 +- .../Attributes/AttributeSystemExtensions.cs | 2 +- src/GameLogic/Attributes/Stats.cs | 5 + src/GameLogic/Player.cs | 16 ++- .../Skills/AreaSkillAttackAction.cs | 9 +- .../Skills/ChainLightningSkillPlugIn.cs | 5 + .../Skills/DrainLifeSkillPlugIn.cs | 3 +- .../Skills/SkillCancellationTokenSource.cs | 4 +- .../MagicEffectDefinition.Generated.cs | 21 +++ .../ModelBuilder/SkillExtensions.cs | 1 + ...AddMagicEffectChanceAndOthers.Designer.cs} | 15 ++- ...11145710_AddMagicEffectChanceAndOthers.cs} | 41 +++++- .../EntityDataContextModelSnapshot.cs | 13 ++ .../Model/ExtendedTypeContext.Generated.cs | 1 + .../Model/MagicEffectDefinition.Generated.cs | 9 ++ .../Skills/InnovationEffectInitializer.cs | 124 +++++++++++++++++ .../Skills/MagicEffectNumber.cs | 15 ++- .../Skills/WeaknessEffectInitializer.cs | 8 +- .../WeaknessSummonerEffectInitializer.cs | 126 ++++++++++++++++++ .../VersionSeasonSix/SkillsInitializer.cs | 21 ++- 23 files changed, 446 insertions(+), 19 deletions(-) rename src/Persistence/EntityFramework/Migrations/{20251208181437_AddMagicEffectChanceAndOthers.Designer.cs => 20251211145710_AddMagicEffectChanceAndOthers.Designer.cs} (99%) rename src/Persistence/EntityFramework/Migrations/{20251208181437_AddMagicEffectChanceAndOthers.cs => 20251211145710_AddMagicEffectChanceAndOthers.cs} (80%) create mode 100644 src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs create mode 100644 src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs diff --git a/src/DataModel/Configuration/MagicEffectDefinition.cs b/src/DataModel/Configuration/MagicEffectDefinition.cs index 19dc2157f..2d4f1d0e6 100644 --- a/src/DataModel/Configuration/MagicEffectDefinition.cs +++ b/src/DataModel/Configuration/MagicEffectDefinition.cs @@ -105,6 +105,15 @@ public partial class MagicEffectDefinition [MemberOfAggregate] public virtual ICollection PowerUpDefinitions { get; protected set; } = null!; + /// + /// Gets or sets the power up definitions which are used to create the actual power up element for PvP. + /// + /// + /// Results in the same collection as if not set. + /// + [MemberOfAggregate] + public virtual ICollection PowerUpDefinitionsPvp { get; protected set; } = null!; + /// public override string ToString() { diff --git a/src/DataModel/Entities/SkillEntry.cs b/src/DataModel/Entities/SkillEntry.cs index ebc2bf6d9..d1078ff73 100644 --- a/src/DataModel/Entities/SkillEntry.cs +++ b/src/DataModel/Entities/SkillEntry.cs @@ -49,6 +49,12 @@ public int Level [Transient] public (AttributeDefinition Target, IElement BuffPowerUp)[]? PowerUps { get; set; } + /// + /// Gets or sets the PvP power up element of this skill of this player. It is a "cached" element which will be created on demand and can be applied multiple times. + /// + [Transient] + public (AttributeDefinition Target, IElement BuffPowerUp)[]? PowerUpsPvp { get; set; } + /// /// Gets or sets the duration of the . /// diff --git a/src/DataModel/GameConfigurationHelper.cs b/src/DataModel/GameConfigurationHelper.cs index c6e4f1225..9a5a03a86 100644 --- a/src/DataModel/GameConfigurationHelper.cs +++ b/src/DataModel/GameConfigurationHelper.cs @@ -46,6 +46,7 @@ public static class GameConfigurationHelper .Concat(c.ItemOptions.SelectMany(o => o.PossibleOptions.Select(p => p.PowerUpDefinition).WhereNotNull())) .Concat(c.ItemOptions.SelectMany(o => o.PossibleOptions.SelectMany(p => p.LevelDependentOptions.Select(l => l.PowerUpDefinition).WhereNotNull()))) .Concat(c.MagicEffects.SelectMany(m => m.PowerUpDefinitions)) + .Concat(c.MagicEffects.SelectMany(m => m.PowerUpDefinitionsPvp)) }, { typeof(ItemBasePowerUpDefinition), c => c.Items.SelectMany(i => i.BasePowerUpAttributes) }, { typeof(ItemDropItemGroup), c => c.Items.SelectMany(i => i.DropItems) }, diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 7637d3f2f..9a9e1621d 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -65,6 +65,11 @@ public static async ValueTask CalculateDamageAsync(this IAttacker attac { var defenseAttribute = defender.GetDefenseAttribute(attacker); defense = (int)defender.Attributes[defenseAttribute]; + defense -= (int)(defense * defender.Attributes[Stats.InnovationDefDecrement]); + if (defense < 0) + { + defense = 0; + } } attacker.GetBaseDmg(skill, out int baseMinDamage, out int baseMaxDamage, out DamageType damageType); @@ -317,7 +322,8 @@ public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAt } var duration = ((IElement, float?))(target is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!); - await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, skillEntry.PowerUps!).ConfigureAwait(false); + var powerUps = target is Player ? skillEntry.PowerUpsPvp! : skillEntry.PowerUps!; + await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, powerUps).ConfigureAwait(false); } /// @@ -397,7 +403,7 @@ public static async ValueTask TryApplyElementalEffectsAsync(this IAttackab } /// - /// Applies the elemental effects of a players skill to the target. + /// Applies the elemental effects of a monster's skill to the target. /// /// The target. /// The attacker. diff --git a/src/GameLogic/Attributes/AttributeSystemExtensions.cs b/src/GameLogic/Attributes/AttributeSystemExtensions.cs index 24af9343c..c05202b08 100644 --- a/src/GameLogic/Attributes/AttributeSystemExtensions.cs +++ b/src/GameLogic/Attributes/AttributeSystemExtensions.cs @@ -77,7 +77,7 @@ public static IElement CreateElement(this IAttributeSystem attributeSystem, Powe r.GetOperandElement(attributeSystem), r.InputOperator) { - AggregateType = result.AggregateType, + AggregateType = r.AggregateType, }) .Cast(); diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 482e37c1b..e780e976a 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -920,6 +920,11 @@ public class Stats /// Only applies to physical damage. public static AttributeDefinition WeaknessPhysDmgDecrement { get; } = new(new Guid("37497650-139B-4DA1-9FB6-27AEB8F04CF6"), "Weakness Physical Damage Decrement", "The inflicted physical damage decrement due to the magic effects of weakness or killing blow skills, which is multiplied with the final damage and subtracted from it."); + /// + /// Gets the innovation defense decrement due to Summoner's innovation skill attribute definition. + /// + public static AttributeDefinition InnovationDefDecrement { get; } = new(new Guid("D8B3B1C9-B409-4A07-8F4D-8F315DCB173A"), "Innovation Defense Decrement", "The defense decrement due to the magic effect of innovation skill, which is multiplied with the final defense and subtracted from it."); + /// /// Gets the 'is shield equipped' attribute definition. /// diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 82402b044..b511ceb88 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1461,23 +1461,31 @@ public void CreateMagicEffectPowerUp(SkillEntry skillEntry) throw new InvalidOperationException($"Skill {skill.Name} ({skill.Number}) has no duration in MagicEffectDef."); } - int i = 0; var result = new (AttributeDefinition Target, IElement BuffPowerUp)[skill.MagicEffectDef.PowerUpDefinitions.Count]; + var resultPvp = new (AttributeDefinition Target, IElement BuffPowerUp)[skill.MagicEffectDef.PowerUpDefinitionsPvp.Count]; var durationElement = this.Attributes!.CreateDurationElement(skill.MagicEffectDef.Duration); var durationElementPvp = skill.MagicEffectDef.DurationPvp is { } durationPvp ? this.Attributes!.CreateDurationElement(durationPvp) : durationElement; var chanceElement = skill.MagicEffectDef.Chance is { } chance ? this.Attributes!.CreateChanceElement(chance) : new ConstantElement(1.0f); var chanceElementPvp = skill.MagicEffectDef.ChancePvp is { } chancePvp ? this.Attributes!.CreateChanceElement(chancePvp) : chanceElement; - AddSkillPowersToResult(skill); + AddSkillPowersToResult(skill, skill.MagicEffectDef.PowerUpDefinitions, ref result); + AddSkillPowersToResult(skill, skill.MagicEffectDef.PowerUpDefinitionsPvp, ref resultPvp); skillEntry.PowerUpDuration = (durationElement, skill.MagicEffectDef.Duration.MaximumValue); skillEntry.PowerUpDurationPvp = (durationElementPvp, skill.MagicEffectDef.DurationPvp?.MaximumValue); skillEntry.PowerUpChance = chanceElement; skillEntry.PowerUpChancePvp = chanceElementPvp; skillEntry.PowerUps = result; + skillEntry.PowerUpsPvp = resultPvp.Count() > 0 ? resultPvp : result; - void AddSkillPowersToResult(Skill skill) + void AddSkillPowersToResult(Skill skill, ICollection powerUps, ref (AttributeDefinition Target, IElement BuffPowerUp)[] result) { + if (powerUps.Count() == 0) + { + return; + } + + int i = 0; var durationExtended = false; - foreach (var powerUpDef in skill.MagicEffectDef!.PowerUpDefinitions) + foreach (var powerUpDef in powerUps) { IElement powerUp; if (skillEntry.Level > 0) diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index fce50f35c..047892c28 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -46,7 +46,7 @@ public async ValueTask AttackAsync(Player player, ushort extraTargetId, ushort s return; } - if (skill.SkillType is SkillType.AreaSkillAutomaticHits or SkillType.AreaSkillExplicitTarget + if (skill.SkillType is SkillType.AreaSkillAutomaticHits or SkillType.AreaSkillExplicitTarget or SkillType.Buff || (skill.SkillType is SkillType.AreaSkillExplicitHits && hitImplicitlyForExplicitSkill)) { // todo: delayed automatic hits, like evil spirit, flame, triple shot... when hitImplicitlyForExplicitSkill = true. @@ -248,6 +248,13 @@ private static IEnumerable GetTargetsInRange(Player player, Point t private async ValueTask ApplySkillAsync(Player player, SkillEntry skillEntry, IAttackable target, Point targetAreaCenter, bool isCombo) { skillEntry.ThrowNotInitializedProperty(skillEntry.Skill is null, nameof(skillEntry.Skill)); + var skill = skillEntry.Skill; + + if (skillEntry.Skill.SkillType == SkillType.Buff) + { + await target.ApplyMagicEffectAsync(player, skillEntry).ConfigureAwait(false); + return; + } var hitInfo = await target.AttackByAsync(player, skillEntry, isCombo).ConfigureAwait(false); await target.TryApplyElementalEffectsAsync(player, skillEntry).ConfigureAwait(false); diff --git a/src/GameLogic/PlayerActions/Skills/ChainLightningSkillPlugIn.cs b/src/GameLogic/PlayerActions/Skills/ChainLightningSkillPlugIn.cs index 5f9891965..68cf24200 100644 --- a/src/GameLogic/PlayerActions/Skills/ChainLightningSkillPlugIn.cs +++ b/src/GameLogic/PlayerActions/Skills/ChainLightningSkillPlugIn.cs @@ -28,6 +28,11 @@ public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackab { bool FilterTarget(IAttackable attackable) { + if (!attackable.IsAlive) + { + return false; + } + if (attackable is Monster { SummonedBy: null } or Destructible) { return true; diff --git a/src/GameLogic/PlayerActions/Skills/DrainLifeSkillPlugIn.cs b/src/GameLogic/PlayerActions/Skills/DrainLifeSkillPlugIn.cs index a3c823f08..a8feddda6 100644 --- a/src/GameLogic/PlayerActions/Skills/DrainLifeSkillPlugIn.cs +++ b/src/GameLogic/PlayerActions/Skills/DrainLifeSkillPlugIn.cs @@ -7,14 +7,13 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; using System.Runtime.InteropServices; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.PlugIns; -using MUnique.OpenMU.GameLogic.Views.Character; using MUnique.OpenMU.Pathfinding; using MUnique.OpenMU.PlugIns; /// /// Handles the drain life skill of the summoner class. Additionally to the attacked target, it regains life for damage dealt. /// -[PlugIn(nameof(ChainLightningSkillPlugIn), "Handles the drain life skill of the summoner class. Additionally to the attacked target, it regains life for damage dealt.")] +[PlugIn(nameof(DrainLifeSkillPlugIn), "Handles the drain life skill of the summoner class. Additionally to the attacked target, it regains life for damage dealt.")] [Guid("9A5A5671-3A8C-4C01-984F-1A8F8E0E7BDA")] public class DrainLifeSkillPlugIn : IAreaSkillPlugIn { diff --git a/src/GameLogic/PlayerActions/Skills/SkillCancellationTokenSource.cs b/src/GameLogic/PlayerActions/Skills/SkillCancellationTokenSource.cs index 12dcafc8b..25b873b40 100644 --- a/src/GameLogic/PlayerActions/Skills/SkillCancellationTokenSource.cs +++ b/src/GameLogic/PlayerActions/Skills/SkillCancellationTokenSource.cs @@ -1,4 +1,4 @@ -// +// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -9,7 +9,7 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; /// /// A which allows to specify an explicit target when cancelling the nova skill. /// -/// +/// public class SkillCancellationTokenSource : CancellationTokenSource { /// diff --git a/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs b/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs index 130f8ef0d..f91b5a046 100644 --- a/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs +++ b/src/Persistence/BasicModel/MagicEffectDefinition.Generated.cs @@ -46,6 +46,27 @@ protected set } } + /// + /// Gets the raw collection of . + /// + [System.Text.Json.Serialization.JsonPropertyName("powerUpDefinitionsPvp")] + public ICollection RawPowerUpDefinitionsPvp { get; } = new List(); + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override ICollection PowerUpDefinitionsPvp + { + get => base.PowerUpDefinitionsPvp ??= new CollectionAdapter(this.RawPowerUpDefinitionsPvp); + protected set + { + this.PowerUpDefinitionsPvp.Clear(); + foreach (var item in value) + { + this.PowerUpDefinitionsPvp.Add(item); + } + } + } + /// /// Gets the raw object of . /// diff --git a/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs b/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs index 4aad25b2a..85ac56266 100644 --- a/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs +++ b/src/Persistence/EntityFramework/Extensions/ModelBuilder/SkillExtensions.cs @@ -19,6 +19,7 @@ internal static class SkillExtensions public static void Apply(this EntityTypeBuilder builder) { builder.Ignore(s => s.PowerUps); + builder.Ignore(s => s.PowerUpsPvp); builder.Ignore(s => s.PowerUpDuration); builder.Ignore(s => s.PowerUpDurationPvp); builder.Ignore(s => s.PowerUpChance); diff --git a/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs b/src/Persistence/EntityFramework/Migrations/20251211145710_AddMagicEffectChanceAndOthers.Designer.cs similarity index 99% rename from src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs rename to src/Persistence/EntityFramework/Migrations/20251211145710_AddMagicEffectChanceAndOthers.Designer.cs index 9469fcea7..72a886110 100644 --- a/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.Designer.cs +++ b/src/Persistence/EntityFramework/Migrations/20251211145710_AddMagicEffectChanceAndOthers.Designer.cs @@ -12,7 +12,7 @@ namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations { [DbContext(typeof(EntityDataContext))] - [Migration("20251208181437_AddMagicEffectChanceAndOthers")] + [Migration("20251211145710_AddMagicEffectChanceAndOthers")] partial class AddMagicEffectChanceAndOthers { /// @@ -2835,6 +2835,9 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("MagicEffectDefinitionId") .HasColumnType("uuid"); + b.Property("MagicEffectDefinitionId1") + .HasColumnType("uuid"); + b.Property("TargetAttributeId") .HasColumnType("uuid"); @@ -2847,6 +2850,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("MagicEffectDefinitionId"); + b.HasIndex("MagicEffectDefinitionId1"); + b.HasIndex("TargetAttributeId"); b.ToTable("PowerUpDefinition", "config"); @@ -4658,6 +4663,12 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasForeignKey("MagicEffectDefinitionId") .OnDelete(DeleteBehavior.Cascade); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", null) + .WithMany("RawPowerUpDefinitionsPvp") + .HasForeignKey("MagicEffectDefinitionId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_PowerUpDefinition_MagicEffectDefinition_MagicEffectDefinit~1"); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") .WithMany() .HasForeignKey("TargetAttributeId"); @@ -5089,6 +5100,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => { b.Navigation("RawPowerUpDefinitions"); + + b.Navigation("RawPowerUpDefinitionsPvp"); }); modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => diff --git a/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs b/src/Persistence/EntityFramework/Migrations/20251211145710_AddMagicEffectChanceAndOthers.cs similarity index 80% rename from src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs rename to src/Persistence/EntityFramework/Migrations/20251211145710_AddMagicEffectChanceAndOthers.cs index b429c129a..f5cd08e42 100644 --- a/src/Persistence/EntityFramework/Migrations/20251208181437_AddMagicEffectChanceAndOthers.cs +++ b/src/Persistence/EntityFramework/Migrations/20251211145710_AddMagicEffectChanceAndOthers.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using System; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -17,6 +18,13 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "real", nullable: true); + migrationBuilder.AddColumn( + name: "MagicEffectDefinitionId1", + schema: "config", + table: "PowerUpDefinition", + type: "uuid", + nullable: true); + migrationBuilder.AddColumn( name: "ChanceId", schema: "config", @@ -62,6 +70,12 @@ protected override void Up(MigrationBuilder migrationBuilder) nullable: false, defaultValue: 0f); + migrationBuilder.CreateIndex( + name: "IX_PowerUpDefinition_MagicEffectDefinitionId1", + schema: "config", + table: "PowerUpDefinition", + column: "MagicEffectDefinitionId1"); + migrationBuilder.CreateIndex( name: "IX_MagicEffectDefinition_ChanceId", schema: "config", @@ -112,6 +126,16 @@ protected override void Up(MigrationBuilder migrationBuilder) principalTable: "PowerUpDefinitionValue", principalColumn: "Id", onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_PowerUpDefinition_MagicEffectDefinition_MagicEffectDefinit~1", + schema: "config", + table: "PowerUpDefinition", + column: "MagicEffectDefinitionId1", + principalSchema: "config", + principalTable: "MagicEffectDefinition", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); } /// @@ -132,6 +156,16 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "config", table: "MagicEffectDefinition"); + migrationBuilder.DropForeignKey( + name: "FK_PowerUpDefinition_MagicEffectDefinition_MagicEffectDefinit~1", + schema: "config", + table: "PowerUpDefinition"); + + migrationBuilder.DropIndex( + name: "IX_PowerUpDefinition_MagicEffectDefinitionId1", + schema: "config", + table: "PowerUpDefinition"); + migrationBuilder.DropIndex( name: "IX_MagicEffectDefinition_ChanceId", schema: "config", @@ -152,6 +186,11 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "config", table: "PowerUpDefinitionValue"); + migrationBuilder.DropColumn( + name: "MagicEffectDefinitionId1", + schema: "config", + table: "PowerUpDefinition"); + migrationBuilder.DropColumn( name: "ChanceId", schema: "config", diff --git a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs index ffcb56578..b04972c73 100644 --- a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs +++ b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs @@ -2832,6 +2832,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("MagicEffectDefinitionId") .HasColumnType("uuid"); + b.Property("MagicEffectDefinitionId1") + .HasColumnType("uuid"); + b.Property("TargetAttributeId") .HasColumnType("uuid"); @@ -2844,6 +2847,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("MagicEffectDefinitionId"); + b.HasIndex("MagicEffectDefinitionId1"); + b.HasIndex("TargetAttributeId"); b.ToTable("PowerUpDefinition", "config"); @@ -4655,6 +4660,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("MagicEffectDefinitionId") .OnDelete(DeleteBehavior.Cascade); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", null) + .WithMany("RawPowerUpDefinitionsPvp") + .HasForeignKey("MagicEffectDefinitionId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_PowerUpDefinition_MagicEffectDefinition_MagicEffectDefinit~1"); + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") .WithMany() .HasForeignKey("TargetAttributeId"); @@ -5086,6 +5097,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => { b.Navigation("RawPowerUpDefinitions"); + + b.Navigation("RawPowerUpDefinitionsPvp"); }); modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => diff --git a/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs b/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs index f3ac09a43..88fc1437c 100644 --- a/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs +++ b/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs @@ -167,6 +167,7 @@ protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuild modelBuilder.Entity().HasOne(entity => entity.RawDuration).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawDurationPvp).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawPowerUpDefinitions).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasMany(entity => entity.RawPowerUpDefinitionsPvp).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawSpawnArea).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawTerrainChanges).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawRewards).WithOne().OnDelete(DeleteBehavior.Cascade); diff --git a/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs b/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs index 4f597d49e..fa20be3df 100644 --- a/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs +++ b/src/Persistence/EntityFramework/Model/MagicEffectDefinition.Generated.cs @@ -37,6 +37,15 @@ internal partial class MagicEffectDefinition : MUnique.OpenMU.DataModel.Configur [NotMapped] public override ICollection PowerUpDefinitions => base.PowerUpDefinitions ??= new CollectionAdapter(this.RawPowerUpDefinitions); + /// + /// Gets the raw collection of . + /// + public ICollection RawPowerUpDefinitionsPvp { get; } = new EntityFramework.List(); + + /// + [NotMapped] + public override ICollection PowerUpDefinitionsPvp => base.PowerUpDefinitionsPvp ??= new CollectionAdapter(this.RawPowerUpDefinitionsPvp); + /// /// Gets or sets the identifier of . /// diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs new file mode 100644 index 000000000..45dc025ff --- /dev/null +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -0,0 +1,124 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Skills; + +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel.Attributes; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Attributes; + +/// +/// Initializer for the innovation effect. +/// +public class InnovationEffectInitializer : InitializerBase +{ + /// + /// Initializes a new instance of the class. + /// + /// The context. + /// The game configuration. + public InnovationEffectInitializer(IContext context, GameConfiguration gameConfiguration) + : base(context, gameConfiguration) + { + } + + /// + public override void Initialize() + { + var magicEffect = this.Context.CreateNew(); + this.GameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Innovation; + magicEffect.Name = "Innovation Effect (Summoner)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.DurationDependsOnTargetLevel = true; + magicEffect.MonsterTargetLevelDivisor = 20; + magicEffect.PlayerTargetLevelDivisor = 150; + + // Chance to apply the effect + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.32f; // 32% + + var chancePerEnergy = this.Context.CreateNew(); + chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + chancePerEnergy.InputOperator = InputOperator.Multiply; + chancePerEnergy.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerEnergy); + + var chancePerBookRise = this.Context.CreateNew(); + chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(this.GameConfiguration); + chancePerBookRise.InputOperator = InputOperator.Multiply; + chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + + magicEffect.ChancePvp = this.Context.CreateNew(); + magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17% + + var chancePerEnergyPvp = this.Context.CreateNew(); + chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + chancePerEnergyPvp.InputOperator = InputOperator.Multiply; + chancePerEnergyPvp.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp); + + var chancePerBookRisePvp = this.Context.CreateNew(); + chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(this.GameConfiguration); + chancePerBookRisePvp.InputOperator = InputOperator.Multiply; + chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + + // Duration of the effect + magicEffect.Duration = this.Context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds + magicEffect.Duration.MaximumValue = 100; // 100 Seconds + + var durationPerEnergy = this.Context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + magicEffect.DurationPvp = this.Context.CreateNew(); + magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds + magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + + var durationPerEnergyPvp = this.Context.CreateNew(); + durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergyPvp.InputOperator = InputOperator.Multiply; + durationPerEnergyPvp.InputOperand = 1f / 300f; // 300 energy adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp); + + var durationPerLevelPvp = this.Context.CreateNew(); + durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + durationPerLevelPvp.InputOperator = InputOperator.Multiply; + durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + + // Power-down: target's defense decreases X% (applies last) + var decDefPowerUpDefinition = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); + decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); + decDefPowerUpDefinition.Boost = this.Context.CreateNew(); + decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease + + var decDefPvmPerEnergy = this.Context.CreateNew(); + decDefPvmPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + decDefPvmPerEnergy.InputOperator = InputOperator.Multiply; + decDefPvmPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01 + decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPvmPerEnergy); + + var decDefPowerUpDefinitionPvp = this.Context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); + decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); + decDefPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); + decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease + + var decDefPvmPerEnergyPvp = this.Context.CreateNew(); + decDefPvmPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + decDefPvmPerEnergyPvp.InputOperator = InputOperator.Multiply; + decDefPvmPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 0.01 + decDefPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDefPvmPerEnergyPvp); + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/MagicEffectNumber.cs b/src/Persistence/Initialization/Skills/MagicEffectNumber.cs index d5c0dca2e..79c686888 100644 --- a/src/Persistence/Initialization/Skills/MagicEffectNumber.cs +++ b/src/Persistence/Initialization/Skills/MagicEffectNumber.cs @@ -12,6 +12,14 @@ internal enum MagicEffectNumber : short { #region Artificial effects which don't end up as an actual magic effect, but regenerate something + /// + /// The Weakness (Summoner) skill effect number. + /// + /// + /// Internal. Proxy of . + /// + WeaknessSummoner = -4, + /// /// The shield recover skill effect number. /// @@ -236,10 +244,15 @@ internal enum MagicEffectNumber : short Blind = 0x49, /// - /// The weakness effect, which decreases the attacker's physical damage. + /// The weakness effect, which decreases the target's physical damage. /// Weakness = 0x4C, + /// + /// The innovation effect, which decreases the target's defense. + /// + Innovation = 0x4D, + /// /// The cherry blossom wine effect (+ 700 Mana). /// diff --git a/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs index da97fc783..4c96db2eb 100644 --- a/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs @@ -10,7 +10,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Skills; using MUnique.OpenMU.GameLogic.Attributes; /// -/// Initializer for the weakness effect, which can result from Killing Blow (RF) or Weakness (Sum) skills. +/// Initializer for the weakness effect which results from Killing Blow (RF) skill. /// public class WeaknessEffectInitializer : InitializerBase { @@ -30,13 +30,17 @@ public override void Initialize() var magicEffect = this.Context.CreateNew(); this.GameConfiguration.MagicEffects.Add(magicEffect); magicEffect.Number = (short)MagicEffectNumber.Weakness; - magicEffect.Name = "Weakness Effect"; + magicEffect.Name = "Weakness Effect (Killing Blow)"; magicEffect.InformObservers = true; magicEffect.SendDuration = false; magicEffect.StopByDeath = true; magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 10; // 10 seconds + // Chance to apply the effect + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% + // Target's physical damage decreases by 5% var decDmgPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition); diff --git a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs new file mode 100644 index 000000000..739af5302 --- /dev/null +++ b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs @@ -0,0 +1,126 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Skills; + +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel.Attributes; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Attributes; + +/// +/// Initializer for the weakness effect which results from Weakness (Summoner) skill. +/// +public class WeaknessSummonerEffectInitializer : InitializerBase +{ + /// + /// Initializes a new instance of the class. + /// + /// The context. + /// The game configuration. + public WeaknessSummonerEffectInitializer(IContext context, GameConfiguration gameConfiguration) + : base(context, gameConfiguration) + { + } + + /// + public override void Initialize() + { + var magicEffect = this.Context.CreateNew(); + this.GameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.WeaknessSummoner; + magicEffect.Name = "Weakness Effect (Summoner)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.DurationDependsOnTargetLevel = true; + magicEffect.MonsterTargetLevelDivisor = 20; + magicEffect.PlayerTargetLevelDivisor = 150; + + // Chance to apply the effect + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.32f; // 32% + + var chancePerEnergy = this.Context.CreateNew(); + chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + chancePerEnergy.InputOperator = InputOperator.Multiply; + chancePerEnergy.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerEnergy); + + var chancePerBookRise = this.Context.CreateNew(); + chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(this.GameConfiguration); + chancePerBookRise.InputOperator = InputOperator.Multiply; + chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + + magicEffect.ChancePvp = this.Context.CreateNew(); + magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17% + + var chancePerEnergyPvp = this.Context.CreateNew(); + chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + chancePerEnergyPvp.InputOperator = InputOperator.Multiply; + chancePerEnergyPvp.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp); + + var chancePerBookRisePvp = this.Context.CreateNew(); + chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(this.GameConfiguration); + chancePerBookRisePvp.InputOperator = InputOperator.Multiply; + chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + + // Duration of the effect + magicEffect.Duration = this.Context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds + magicEffect.Duration.MaximumValue = 100; // 100 Seconds + + var durationPerEnergy = this.Context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + magicEffect.DurationPvp = this.Context.CreateNew(); + magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds + magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + + var durationPerEnergyPvp = this.Context.CreateNew(); + durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergyPvp.InputOperator = InputOperator.Multiply; + durationPerEnergyPvp.InputOperand = 1f / 300f; // 300 energy adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp); + + var durationPerLevelPvp = this.Context.CreateNew(); + durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + durationPerLevelPvp.InputOperator = InputOperator.Multiply; + durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + + // Power-down: target's physical damage decreases X% + var decDmgPowerUpDefinition = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition); + decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); + decDmgPowerUpDefinition.Boost = this.Context.CreateNew(); + decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.04f; // 4% decrease + decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // based on 4k total energy cap -- to-do: check zTeam + + var decDmgPerEnergy = this.Context.CreateNew(); + decDmgPerEnergy.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + decDmgPerEnergy.InputOperator = InputOperator.Multiply; + decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 0.01 + decDmgPowerUpDefinition.Boost.RelatedValues.Add(decDmgPerEnergy); + + var decDmgPowerUpDefinitionPvp = this.Context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(decDmgPowerUpDefinitionPvp); + decDmgPowerUpDefinitionPvp.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); + decDmgPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); + decDmgPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.03f; // 3% decrease + decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 1f; // -- to-do: check zTeam + + var decDmgPerEnergyPvp = this.Context.CreateNew(); + decDmgPerEnergyPvp.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + decDmgPerEnergyPvp.InputOperator = InputOperator.Multiply; + decDmgPerEnergyPvp.InputOperand = 1f / 9300f; // 93 energy further decreases 0.01 + decDmgPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDmgPerEnergyPvp); + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 1bcb40ee6..38a504e91 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -68,6 +68,8 @@ internal class SkillsInitializer : SkillsInitializerBase { SkillNumber.Berserker, MagicEffectNumber.Berserker }, { SkillNumber.KillingBlow, MagicEffectNumber.Weakness }, { SkillNumber.Sleep, MagicEffectNumber.Sleep }, + { SkillNumber.Weakness, MagicEffectNumber.WeaknessSummoner }, + { SkillNumber.Innovation, MagicEffectNumber.Innovation }, }; private readonly IDictionary _masterSkillRoots; @@ -192,8 +194,10 @@ public override void Initialize() this.CreateSkill(SkillNumber.DamageReflection, "Damage Reflection", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 10, manaConsumption: 40, energyRequirement: 375); this.CreateSkill(SkillNumber.Berserker, "Berserker", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 50, manaConsumption: 100, energyRequirement: 620, skillType: SkillType.Buff, targetRestriction: SkillTargetRestriction.Self); this.CreateSkill(SkillNumber.Sleep, "Sleep", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 3, manaConsumption: 20, energyRequirement: 180, skillType: SkillType.Buff); - this.CreateSkill(SkillNumber.Weakness, "Weakness", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 50, energyRequirement: 663); - this.CreateSkill(SkillNumber.Innovation, "Innovation", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 70, energyRequirement: 912); + this.CreateSkill(SkillNumber.Weakness, "Weakness", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 50, energyRequirement: 663, skillType: SkillType.Buff); + this.AddAreaSkillSettings(SkillNumber.Weakness, false, 0, 0, 0, maximumHitsPerAttack: 5, useTargetAreaFilter: true, targetAreaDiameter: 10); + this.CreateSkill(SkillNumber.Innovation, "Innovation", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 70, energyRequirement: 912, skillType: SkillType.Buff); + this.AddAreaSkillSettings(SkillNumber.Innovation, false, 0, 0, 0, maximumHitsPerAttack: 5, useTargetAreaFilter: true, targetAreaDiameter: 10); this.CreateSkill(SkillNumber.Explosion223, "Explosion", CharacterClasses.AllSummoners, DamageType.Curse, 40, 6, 5, 90, energyRequirement: 100, elementalModifier: ElementalType.Fire); // Book of Samut's skill this.CreateSkill(SkillNumber.Requiem, "Requiem", CharacterClasses.AllSummoners, DamageType.Curse, 65, 6, 10, 110, energyRequirement: 99, elementalModifier: ElementalType.Wind); // Book of Neil's skill this.CreateSkill(SkillNumber.Pollution, "Pollution", CharacterClasses.AllSummoners, DamageType.Curse, 80, 6, 15, 120, energyRequirement: 115, elementalModifier: ElementalType.Lightning); // Book of Lagle's skill @@ -664,6 +668,8 @@ private void InitializeEffects() new BerserkerEffectInitializer(this.Context, this.GameConfiguration).Initialize(); new WeaknessEffectInitializer(this.Context, this.GameConfiguration).Initialize(); new SleepEffectInitializer(this.Context, this.GameConfiguration).Initialize(); + new WeaknessSummonerEffectInitializer(this.Context, this.GameConfiguration).Initialize(); + new InnovationEffectInitializer(this.Context, this.GameConfiguration).Initialize(); } private void MapSkillsToEffects() @@ -673,6 +679,17 @@ private void MapSkillsToEffects() var skill = this.GameConfiguration.Skills.First(s => s.Number == (short)effectOfSkill.Key); var effect = this.GameConfiguration.MagicEffects.First(e => e.Number == (short)effectOfSkill.Value); skill.MagicEffectDef = effect; + + // After the mapping, we override the internal effect number to the client's + switch (effect.Number) + { + case (short)MagicEffectNumber.WeaknessSummoner: + effect.Number = (short)MagicEffectNumber.Weakness; + break; + default: + // no change needed + break; + } } } From 959f5a9e27d67199400f9f2eb8736ef4315510df Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 23 Dec 2025 16:16:49 +0000 Subject: [PATCH 14/34] Removed update plugin --- .../Attributes/AttributeSystemExtensions.cs | 2 +- .../Updates/FixSleepSkillUpdate.cs | 88 ------------------- .../Initialization/Updates/UpdateVersion.cs | 5 -- 3 files changed, 1 insertion(+), 94 deletions(-) delete mode 100644 src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs diff --git a/src/GameLogic/Attributes/AttributeSystemExtensions.cs b/src/GameLogic/Attributes/AttributeSystemExtensions.cs index c05202b08..24af9343c 100644 --- a/src/GameLogic/Attributes/AttributeSystemExtensions.cs +++ b/src/GameLogic/Attributes/AttributeSystemExtensions.cs @@ -77,7 +77,7 @@ public static IElement CreateElement(this IAttributeSystem attributeSystem, Powe r.GetOperandElement(attributeSystem), r.InputOperator) { - AggregateType = r.AggregateType, + AggregateType = result.AggregateType, }) .Cast(); diff --git a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs b/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs deleted file mode 100644 index eb70bcba4..000000000 --- a/src/Persistence/Initialization/Updates/FixSleepSkillUpdate.cs +++ /dev/null @@ -1,88 +0,0 @@ -// -// 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.DataModel.Attributes; -using MUnique.OpenMU.DataModel.Configuration; -using MUnique.OpenMU.GameLogic.Attributes; -using MUnique.OpenMU.Persistence.Initialization.Skills; -using MUnique.OpenMU.PlugIns; - -/// -/// This adds the items required to enter the kalima map. -/// -[PlugIn(PlugInName, PlugInDescription)] -[Guid("A8827A3C-7F52-47CF-9EA5-562A9C06B986")] -public class FixSleepSkillUpdate : UpdatePlugInBase -{ - /// - /// The plug in name. - /// - internal const string PlugInName = "Fix Sleep Skill"; - - /// - /// The plug in description. - /// - internal const string PlugInDescription = "Updates the Sleep skill definition to correct it and implement the new stat."; - - /// - public override UpdateVersion Version => UpdateVersion.FixSleepSkillSeason6; - - /// - public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; - - /// - public override string Name => PlugInName; - - /// - public override string Description => PlugInDescription; - - /// - public override bool IsMandatory => false; - - /// - public override DateTime CreatedAt => new(2024, 09, 01, 18, 0, 0, DateTimeKind.Utc); - - /// - protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) - { - var sleepAttribute = gameConfiguration.Attributes.FirstOrDefault(a => a.Id == Stats.IsAsleep.Id); - - if (sleepAttribute == null) - { - var attr = context.CreateNew(); - attr.Id = Stats.IsAsleep.Id; - attr.Designation = Stats.IsAsleep.Designation; - attr.Description = Stats.IsAsleep.Description; - gameConfiguration.Attributes.Add(attr); - } - - var magicEffectSleep = gameConfiguration.MagicEffects.FirstOrDefault(e => e.Number == (short)MagicEffectNumber.Sleep); - - if (magicEffectSleep == null) - { - magicEffectSleep = context.CreateNew(); - gameConfiguration.MagicEffects.Add(magicEffectSleep); - magicEffectSleep.Name = "Sleep"; - magicEffectSleep.InformObservers = true; - magicEffectSleep.Number = (short)MagicEffectNumber.Sleep; - magicEffectSleep.StopByDeath = true; - magicEffectSleep.SubType = 255; - magicEffectSleep.Duration = context.CreateNew(); - magicEffectSleep.Duration.ConstantValue.Value = 5; - var powerUp = context.CreateNew(); - magicEffectSleep.PowerUpDefinitions.Add(powerUp); - var boost = context.CreateNew(); - powerUp.Boost = boost; - boost.ConstantValue.Value = 1; - powerUp.TargetAttribute = Stats.IsAsleep.GetPersistent(gameConfiguration); - } - - var sleepSkill = gameConfiguration.Skills.First(x => x.Number == (short)SkillNumber.Sleep); - sleepSkill.SkillType = SkillType.Buff; - sleepSkill.MagicEffectDef = magicEffectSleep; - } -} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 9411f7e75..35a6e9b1b 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -334,9 +334,4 @@ public enum UpdateVersion /// The version of the . /// FixSkillMultipliers = 65, - - /// - /// The version of the . - /// - FixSleepSkillSeason6 = 66, } \ No newline at end of file From 47221631b57fd99e72627f138087680fd568b893 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 23 Dec 2025 16:28:37 +0000 Subject: [PATCH 15/34] Improved MagicEffectNumber comment descriptions --- src/Persistence/Initialization/Skills/MagicEffectNumber.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Persistence/Initialization/Skills/MagicEffectNumber.cs b/src/Persistence/Initialization/Skills/MagicEffectNumber.cs index 79c686888..28275b5b5 100644 --- a/src/Persistence/Initialization/Skills/MagicEffectNumber.cs +++ b/src/Persistence/Initialization/Skills/MagicEffectNumber.cs @@ -244,12 +244,12 @@ internal enum MagicEffectNumber : short Blind = 0x49, /// - /// The weakness effect, which decreases the target's physical damage. + /// The weakness effect, which decreases the physical damage. /// Weakness = 0x4C, /// - /// The innovation effect, which decreases the target's defense. + /// The innovation effect, which decreases the defense. /// Innovation = 0x4D, From 51f812e6797264b120a96126f3ba58fa2aeedb65 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 23 Dec 2025 16:52:53 +0000 Subject: [PATCH 16/34] Added bugfixes in AreaSkillAttackAction --- .../PlayerActions/Skills/AreaSkillAttackAction.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 047892c28..a66e14c25 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -129,13 +129,18 @@ private async ValueTask PerformAutomaticHitsAsync(Player player, ushort extraTar for (int attackRound = 0; attackRound < areaSkillSettings.MaximumNumberOfHitsPerTarget; attackRound++) { - if (attackCount > maxAttacks) + if (attackCount >= maxAttacks) { break; } foreach (var target in targets) { + if (attackCount >= maxAttacks) + { + break; + } + if (target.Id == extraTargetId) { extraTarget = target; @@ -201,7 +206,7 @@ private static IEnumerable GetTargets(Player player, Point targetAr if (skill.SkillType == SkillType.AreaSkillExplicitTarget) { if (extraTarget?.CheckSkillTargetRestrictions(player, skill) is true - && player.IsInRange(extraTarget.Position, skill.Range + 2) + && player.IsInRange(extraTarget.Position, skill.Range) && !extraTarget.IsAtSafezone()) { yield return extraTarget; From 1ca7e50ba149204ea4d232fe37d27a7a0f60e8b4 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 24 Dec 2025 11:34:10 +0000 Subject: [PATCH 17/34] Some fixes --- src/GameLogic/AttackableExtensions.cs | 8 +++++--- src/GameLogic/Attributes/Stats.cs | 2 +- .../Skills/InnovationEffectInitializer.cs | 20 +++++++++---------- .../WeaknessSummonerEffectInitializer.cs | 4 ++-- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 9a9e1621d..b70c7656f 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -791,9 +791,11 @@ private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IA if (magicEffectDefinition.DurationDependsOnTargetLevel) { - finalDuration -= target is Player - ? target.Attributes[Stats.Level] / magicEffectDefinition.PlayerTargetLevelDivisor - : target.Attributes[Stats.Level] / magicEffectDefinition.MonsterTargetLevelDivisor; + var divisor = target is Player ? magicEffectDefinition.PlayerTargetLevelDivisor : magicEffectDefinition.MonsterTargetLevelDivisor; + if (divisor != 0) + { + finalDuration -= target.Attributes[Stats.Level] / divisor; + } } finalDuration = Math.Min(finalDuration, duration.MaxDur ?? float.MaxValue); diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index e780e976a..079f50329 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -638,7 +638,7 @@ public class Stats /// /// Gets the explosion skill MST bonus damage, which rises with fire tome strengthener and is added late stage. /// - public static AttributeDefinition ExplosionBonusDmg { get; } = new(new Guid("543E01C2-5C61-4473-ACF9-8A63A987A230"), "Explosion Bonus Damage (MST)", "The explosion skill (book of samut) bonus damage, which rises with fire stome strengthener and is added at a late stage."); + public static AttributeDefinition ExplosionBonusDmg { get; } = new(new Guid("543E01C2-5C61-4473-ACF9-8A63A987A230"), "Explosion Bonus Damage (MST)", "The explosion skill (book of samut) bonus damage, which rises with fire tome strengthener and is added at a late stage."); /// /// Gets the requiem skill MST bonus damage, which rises with wind tome strengthener and is added late stage. diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs index 45dc025ff..fed9de368 100644 --- a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -103,11 +103,11 @@ public override void Initialize() decDefPowerUpDefinition.Boost = this.Context.CreateNew(); decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease - var decDefPvmPerEnergy = this.Context.CreateNew(); - decDefPvmPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); - decDefPvmPerEnergy.InputOperator = InputOperator.Multiply; - decDefPvmPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01 - decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPvmPerEnergy); + var decDefPerEnergy = this.Context.CreateNew(); + decDefPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + decDefPerEnergy.InputOperator = InputOperator.Multiply; + decDefPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01 + decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPerEnergy); var decDefPowerUpDefinitionPvp = this.Context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); @@ -115,10 +115,10 @@ public override void Initialize() decDefPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease - var decDefPvmPerEnergyPvp = this.Context.CreateNew(); - decDefPvmPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); - decDefPvmPerEnergyPvp.InputOperator = InputOperator.Multiply; - decDefPvmPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 0.01 - decDefPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDefPvmPerEnergyPvp); + var decDefPerEnergyPvp = this.Context.CreateNew(); + decDefPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + decDefPerEnergyPvp.InputOperator = InputOperator.Multiply; + decDefPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 0.01 + decDefPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDefPerEnergyPvp); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs index 739af5302..69a1fb827 100644 --- a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs @@ -105,7 +105,7 @@ public override void Initialize() decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // based on 4k total energy cap -- to-do: check zTeam var decDmgPerEnergy = this.Context.CreateNew(); - decDmgPerEnergy.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + decDmgPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); decDmgPerEnergy.InputOperator = InputOperator.Multiply; decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 0.01 decDmgPowerUpDefinition.Boost.RelatedValues.Add(decDmgPerEnergy); @@ -118,7 +118,7 @@ public override void Initialize() decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 1f; // -- to-do: check zTeam var decDmgPerEnergyPvp = this.Context.CreateNew(); - decDmgPerEnergyPvp.InputAttribute = Stats.Level.GetPersistent(this.GameConfiguration); + decDmgPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); decDmgPerEnergyPvp.InputOperator = InputOperator.Multiply; decDmgPerEnergyPvp.InputOperand = 1f / 9300f; // 93 energy further decreases 0.01 decDmgPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDmgPerEnergyPvp); From 11535bf5cbaf8a4926f46dbe3872401f4195fb42 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 24 Dec 2025 13:33:57 +0000 Subject: [PATCH 18/34] Added Reflection MagicEffect --- .../Skills/ReflectionEffectInitializer.cs | 63 +++++++++++++++++++ .../VersionSeasonSix/SkillsInitializer.cs | 2 + 2 files changed, 65 insertions(+) create mode 100644 src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs diff --git a/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs b/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs new file mode 100644 index 000000000..a76bf4eeb --- /dev/null +++ b/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs @@ -0,0 +1,63 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Skills; + +using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel.Attributes; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Attributes; + +/// +/// Initializer for the reflection effect which results from Damage Reflection (Summoner) skill. +/// +public class ReflectionEffectInitializer : InitializerBase +{ + /// + /// Initializes a new instance of the class. + /// + /// The context. + /// The game configuration. + public ReflectionEffectInitializer(IContext context, GameConfiguration gameConfiguration) + : base(context, gameConfiguration) + { + } + + /// + public override void Initialize() + { + var magicEffect = this.Context.CreateNew(); + this.GameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Reflection; + magicEffect.Name = "Reflection Effect"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + + // Duration of the effect + magicEffect.Duration = this.Context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 30; // 30 Seconds + magicEffect.Duration.MaximumValue = 180; // 180 Seconds + + var durationPerEnergy = this.Context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 24; // 24 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + // Target's damage reflection increases X% + var incReflectPowerUpDefinition = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(incReflectPowerUpDefinition); + incReflectPowerUpDefinition.TargetAttribute = Stats.DamageReflection.GetPersistent(this.GameConfiguration); + incReflectPowerUpDefinition.Boost = this.Context.CreateNew(); + incReflectPowerUpDefinition.Boost.ConstantValue.Value = 0.3f; // 30% increase + incReflectPowerUpDefinition.Boost.MaximumValue = 0.6f; + + var incReflectPerEnergy = this.Context.CreateNew(); + incReflectPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); + incReflectPerEnergy.InputOperator = InputOperator.Multiply; + incReflectPerEnergy.InputOperand = 1f / 4200f; // 42 energy further increases 0.01 + incReflectPowerUpDefinition.Boost.RelatedValues.Add(incReflectPerEnergy); + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 38a504e91..1edac9b96 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -70,6 +70,7 @@ internal class SkillsInitializer : SkillsInitializerBase { SkillNumber.Sleep, MagicEffectNumber.Sleep }, { SkillNumber.Weakness, MagicEffectNumber.WeaknessSummoner }, { SkillNumber.Innovation, MagicEffectNumber.Innovation }, + { SkillNumber.DamageReflection, MagicEffectNumber.Reflection }, }; private readonly IDictionary _masterSkillRoots; @@ -670,6 +671,7 @@ private void InitializeEffects() new SleepEffectInitializer(this.Context, this.GameConfiguration).Initialize(); new WeaknessSummonerEffectInitializer(this.Context, this.GameConfiguration).Initialize(); new InnovationEffectInitializer(this.Context, this.GameConfiguration).Initialize(); + new ReflectionEffectInitializer(this.Context, this.GameConfiguration).Initialize(); } private void MapSkillsToEffects() From 9a32658c7b7a2efb8895c100c21ff9865d9d96d2 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 26 Dec 2025 17:05:34 +0000 Subject: [PATCH 19/34] Fixed reflection exc and wing options --- src/AttributeSystem/ComposableAttribute.cs | 11 ++++++++++- .../Attributes/AttributeSystemExtensions.cs | 2 +- src/GameLogic/Attributes/Stats.cs | 5 +++++ src/GameLogic/Player.cs | 12 +++++++++++- .../Initialization/Items/ExcellentOptions.cs | 2 +- .../Skills/ReflectionEffectInitializer.cs | 8 ++++---- .../Initialization/VersionSeasonSix/Items/Wings.cs | 2 +- .../VersionSeasonSix/SkillsInitializer.cs | 2 +- 8 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/AttributeSystem/ComposableAttribute.cs b/src/AttributeSystem/ComposableAttribute.cs index 405020505..90183b6fa 100644 --- a/src/AttributeSystem/ComposableAttribute.cs +++ b/src/AttributeSystem/ComposableAttribute.cs @@ -11,6 +11,8 @@ public class ComposableAttribute : BaseAttribute, IComposableAttribute { private readonly IList _elementList; + private float? _maximumValue; + private float? _cachedValue; /// @@ -18,10 +20,12 @@ public class ComposableAttribute : BaseAttribute, IComposableAttribute /// /// The definition. /// Type of the aggregate. - public ComposableAttribute(AttributeDefinition definition, AggregateType aggregateType = AggregateType.AddRaw) + /// The inner maximum value. + public ComposableAttribute(AttributeDefinition definition, AggregateType aggregateType = AggregateType.AddRaw, float? maximumValue = null) : base(definition, aggregateType) { this._elementList = new List(); + this._maximumValue = maximumValue; } /// @@ -78,6 +82,11 @@ private float GetAndCacheValue() } var newValue = (rawValues * multiValues) + finalValues; + if (this._maximumValue.HasValue) + { + newValue = Math.Min(this._maximumValue.Value, newValue); + } + if (this.Definition.MaximumValue.HasValue) { newValue = Math.Min(this.Definition.MaximumValue.Value, newValue); diff --git a/src/GameLogic/Attributes/AttributeSystemExtensions.cs b/src/GameLogic/Attributes/AttributeSystemExtensions.cs index 24af9343c..b91d655bf 100644 --- a/src/GameLogic/Attributes/AttributeSystemExtensions.cs +++ b/src/GameLogic/Attributes/AttributeSystemExtensions.cs @@ -86,7 +86,7 @@ public static IElement CreateElement(this IAttributeSystem attributeSystem, Powe elements = elements.Concat(value.ConstantValue.GetAsEnumerable()); } - var composableResult = new ComposableAttribute(targetDefinition, result.AggregateType); + var composableResult = new ComposableAttribute(targetDefinition, result.AggregateType, value.MaximumValue); elements.ForEach(element => composableResult.AddElement(element)); return composableResult; diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 079f50329..0d2e0bded 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1236,6 +1236,11 @@ public class Stats /// public static AttributeDefinition FullyRecoverHealthAfterHitChance { get; } = new(new Guid("3CA72C07-9C2C-4FC5-8BCB-9BD737F83664"), "Chance to fully recover health when getting hit", "3rd Wing Option"); + /// + /// Gets the fully reflect damage after hit chance definition. + /// + public static AttributeDefinition FullyReflectDamageAfterHitChance { get; } = new(new Guid("1F7C1E04-4FBD-4FCB-A6C2-EB51A91D8C3E"), "Chance to fully reflect damage when getting hit", "3rd Wing Option"); + /// /// Gets the health loss after hit definition. /// diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 20e194082..c93ce899d 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -2051,7 +2051,17 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski return; } - var reflectPercentage = this.Attributes[Stats.DamageReflection]; + var reflectPercentage = 0f; + var fullReflectPercentage = this.Attributes[Stats.FullyReflectDamageAfterHitChance]; + if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) + { + reflectPercentage = 1.0f; + } + else + { + reflectPercentage = this.Attributes[Stats.DamageReflection]; + } + if (reflectPercentage > 0 && attacker is IAttackable attackableAttacker) { var reflectedDamage = (int)((hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage); diff --git a/src/Persistence/Initialization/Items/ExcellentOptions.cs b/src/Persistence/Initialization/Items/ExcellentOptions.cs index bc05e0bdc..63b5cdba3 100644 --- a/src/Persistence/Initialization/Items/ExcellentOptions.cs +++ b/src/Persistence/Initialization/Items/ExcellentOptions.cs @@ -123,7 +123,7 @@ private void CreateDefenseOptions() definition.PossibleOptions.Add(this.CreateExcellentOption(1, Stats.MoneyAmountRate, 1.4f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense)); definition.PossibleOptions.Add(this.CreateExcellentOption(2, Stats.DefenseRatePvm, 1.1f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense)); - definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.DamageReflection, 0.04f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentDefense)); + definition.PossibleOptions.Add(this.CreateExcellentOption(3, Stats.DamageReflection, 0.05f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentDefense)); definition.PossibleOptions.Add(this.CreateExcellentOption(4, Stats.ArmorDamageDecrease, 0.04f, AggregateType.AddRaw, ItemOptionDefinitionNumbers.ExcellentDefense)); definition.PossibleOptions.Add(this.CreateExcellentOption(5, Stats.MaximumMana, 1.04f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense)); definition.PossibleOptions.Add(this.CreateExcellentOption(6, Stats.MaximumHealth, 1.04f, AggregateType.Multiplicate, ItemOptionDefinitionNumbers.ExcellentDefense)); diff --git a/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs b/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs index a76bf4eeb..d91603cc1 100644 --- a/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs @@ -10,7 +10,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Skills; using MUnique.OpenMU.GameLogic.Attributes; /// -/// Initializer for the reflection effect which results from Damage Reflection (Summoner) skill. +/// Initializer for the reflection buff effect. /// public class ReflectionEffectInitializer : InitializerBase { @@ -35,10 +35,10 @@ public override void Initialize() magicEffect.SendDuration = false; magicEffect.StopByDeath = true; - // Duration of the effect + // Duration = 30 + (Energy / 24) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 30; // 30 Seconds - magicEffect.Duration.MaximumValue = 180; // 180 Seconds + magicEffect.Duration.MaximumValue = 180; var durationPerEnergy = this.Context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -46,7 +46,7 @@ public override void Initialize() durationPerEnergy.InputOperand = 1f / 24; // 24 energy adds 1s magicEffect.Duration.RelatedValues.Add(durationPerEnergy); - // Target's damage reflection increases X% + // Reflection % = 30 + (Energy / 42) var incReflectPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(incReflectPowerUpDefinition); incReflectPowerUpDefinition.TargetAttribute = Stats.DamageReflection.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs index 8f4e31980..bde53646b 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/Wings.cs @@ -277,7 +277,7 @@ private ItemOptionDefinition CreateThirdClassWingOptions() definition.MaximumOptionsPerItem = 1; definition.PossibleOptions.Add(this.CreateWingOption(1, Stats.DefenseIgnoreChance, 0.05f, AggregateType.AddRaw)); // Ignore opponent's defensive power by 5% - definition.PossibleOptions.Add(this.CreateWingOption(2, Stats.DamageReflection, 0.05f, AggregateType.AddRaw)); // Ignore opponent's defensive power by 5% + definition.PossibleOptions.Add(this.CreateWingOption(2, Stats.FullyReflectDamageAfterHitChance, 0.05f, AggregateType.AddRaw)); // Fully reflect the damage when hit by 5% definition.PossibleOptions.Add(this.CreateWingOption(3, Stats.FullyRecoverHealthAfterHitChance, 0.05f, AggregateType.AddRaw)); // Fully restore health when hit by 5% definition.PossibleOptions.Add(this.CreateWingOption(4, Stats.FullyRecoverManaAfterHitChance, 0.05f, AggregateType.AddRaw)); // Fully recover mana when hit by 5% diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 1edac9b96..c7d4f5390 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -192,7 +192,7 @@ public override void Initialize() this.CreateSkill(SkillNumber.ShieldBurn, "Shield-Burn", CharacterClasses.All, distance: 3, manaConsumption: 30, elementalModifier: ElementalType.Ice, cooldownMinutes: 5); this.CreateSkill(SkillNumber.DrainLife, "Drain Life", CharacterClasses.AllSummoners, DamageType.Wizardry, 35, 6, manaConsumption: 50, energyRequirement: 150, skillType: SkillType.AreaSkillExplicitTarget); this.CreateSkill(SkillNumber.ChainLightning, "Chain Lightning", CharacterClasses.AllSummoners, DamageType.Wizardry, 70, 6, manaConsumption: 85, energyRequirement: 245, skillType: SkillType.AreaSkillExplicitTarget, skillTarget: SkillTarget.Explicit); - this.CreateSkill(SkillNumber.DamageReflection, "Damage Reflection", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 10, manaConsumption: 40, energyRequirement: 375); + this.CreateSkill(SkillNumber.DamageReflection, "Damage Reflection", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 10, manaConsumption: 40, energyRequirement: 375, skillType: SkillType.Buff); this.CreateSkill(SkillNumber.Berserker, "Berserker", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 50, manaConsumption: 100, energyRequirement: 620, skillType: SkillType.Buff, targetRestriction: SkillTargetRestriction.Self); this.CreateSkill(SkillNumber.Sleep, "Sleep", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 3, manaConsumption: 20, energyRequirement: 180, skillType: SkillType.Buff); this.CreateSkill(SkillNumber.Weakness, "Weakness", CharacterClasses.AllSummoners, distance: 6, abilityConsumption: 15, manaConsumption: 50, energyRequirement: 663, skillType: SkillType.Buff); From 7585b63c245e2dc24b486371f28ba2b098d8ea0d Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 26 Dec 2025 19:25:16 +0000 Subject: [PATCH 20/34] Added update plugin --- src/GameLogic/AttackableExtensions.cs | 3 +- src/GameLogic/NPC/AttackableNpcBase.cs | 3 +- src/GameLogic/Player.cs | 9 +- .../Skills/AreaSkillAttackAction.cs | 1 - .../Skills/InnovationEffectInitializer.cs | 11 +- .../Skills/SleepEffectInitializer.cs | 6 +- .../WeaknessSummonerEffectInitializer.cs | 9 +- .../Updates/AddSummonerBuffSkillsPlugIn.cs | 482 ++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 5 + 9 files changed, 512 insertions(+), 17 deletions(-) create mode 100644 src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index b70c7656f..ff3ef0747 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -8,7 +8,6 @@ namespace MUnique.OpenMU.GameLogic; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.DataModel.Configuration.Items; -using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.NPC; using MUnique.OpenMU.GameLogic.Pet; @@ -459,7 +458,7 @@ public static void ApplyAmmunitionConsumption(this IAttacker attacker, HitInfo h { if (!hitInfo.Attributes.HasFlag(DamageAttributes.Reflected) && attacker.Attributes[Stats.AmmunitionConsumptionRate] > 0.0) { - // Every hit needs ammo. Failed hits don't need ammo. + // Every hit needs ammo, missed or not if (attacker.Attributes[Stats.AmmunitionAmount] < attacker.Attributes[Stats.AmmunitionConsumptionRate]) { return; diff --git a/src/GameLogic/NPC/AttackableNpcBase.cs b/src/GameLogic/NPC/AttackableNpcBase.cs index e16c72b05..01de16858 100644 --- a/src/GameLogic/NPC/AttackableNpcBase.cs +++ b/src/GameLogic/NPC/AttackableNpcBase.cs @@ -111,10 +111,11 @@ public int Health } var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); + attacker.ApplyAmmunitionConsumption(hitInfo); await this.HitAsync(hitInfo, attacker, skill?.Skill).ConfigureAwait(false); + if (hitInfo.HealthDamage > 0) { - attacker.ApplyAmmunitionConsumption(hitInfo); if (attacker is Player player) { await player.AfterHitTargetAsync().ConfigureAwait(false); diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index c93ce899d..bbdc80a60 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -627,6 +627,7 @@ public bool IsAnySelfDefenseActive() } var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); + attacker.ApplyAmmunitionConsumption(hitInfo); if (hitInfo is { HealthDamage: 0, ShieldDamage: 0 }) { @@ -639,8 +640,6 @@ public bool IsAnySelfDefenseActive() return hitInfo; } - attacker.ApplyAmmunitionConsumption(hitInfo); - if (Rand.NextRandomBool(this.Attributes[Stats.FullyRecoverHealthAfterHitChance])) { this.Attributes[Stats.CurrentHealth] = this.Attributes[Stats.MaximumHealth]; @@ -1467,8 +1466,8 @@ public void CreateMagicEffectPowerUp(SkillEntry skillEntry) var durationElementPvp = skill.MagicEffectDef.DurationPvp is { } durationPvp ? this.Attributes!.CreateDurationElement(durationPvp) : durationElement; var chanceElement = skill.MagicEffectDef.Chance is { } chance ? this.Attributes!.CreateChanceElement(chance) : new ConstantElement(1.0f); var chanceElementPvp = skill.MagicEffectDef.ChancePvp is { } chancePvp ? this.Attributes!.CreateChanceElement(chancePvp) : chanceElement; - AddSkillPowersToResult(skill, skill.MagicEffectDef.PowerUpDefinitions, ref result); - AddSkillPowersToResult(skill, skill.MagicEffectDef.PowerUpDefinitionsPvp, ref resultPvp); + AddSkillPowersToResult(skill.MagicEffectDef.PowerUpDefinitions, ref result); + AddSkillPowersToResult(skill.MagicEffectDef.PowerUpDefinitionsPvp, ref resultPvp); skillEntry.PowerUpDuration = (durationElement, skill.MagicEffectDef.Duration.MaximumValue); skillEntry.PowerUpDurationPvp = (durationElementPvp, skill.MagicEffectDef.DurationPvp?.MaximumValue); skillEntry.PowerUpChance = chanceElement; @@ -1476,7 +1475,7 @@ public void CreateMagicEffectPowerUp(SkillEntry skillEntry) skillEntry.PowerUps = result; skillEntry.PowerUpsPvp = resultPvp.Count() > 0 ? resultPvp : result; - void AddSkillPowersToResult(Skill skill, ICollection powerUps, ref (AttributeDefinition Target, IElement BuffPowerUp)[] result) + void AddSkillPowersToResult(ICollection powerUps, ref (AttributeDefinition Target, IElement BuffPowerUp)[] result) { if (powerUps.Count() == 0) { diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index a66e14c25..4966fcad3 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -253,7 +253,6 @@ private static IEnumerable GetTargetsInRange(Player player, Point t private async ValueTask ApplySkillAsync(Player player, SkillEntry skillEntry, IAttackable target, Point targetAreaCenter, bool isCombo) { skillEntry.ThrowNotInitializedProperty(skillEntry.Skill is null, nameof(skillEntry.Skill)); - var skill = skillEntry.Skill; if (skillEntry.Skill.SkillType == SkillType.Buff) { diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs index fed9de368..58d89b610 100644 --- a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -30,7 +30,7 @@ public override void Initialize() var magicEffect = this.Context.CreateNew(); this.GameConfiguration.MagicEffects.Add(magicEffect); magicEffect.Number = (short)MagicEffectNumber.Innovation; - magicEffect.Name = "Innovation Effect (Summoner)"; + magicEffect.Name = "Innovation Effect"; magicEffect.InformObservers = true; magicEffect.SendDuration = false; magicEffect.StopByDeath = true; @@ -38,7 +38,7 @@ public override void Initialize() magicEffect.MonsterTargetLevelDivisor = 20; magicEffect.PlayerTargetLevelDivisor = 150; - // Chance to apply the effect + // Chance % = 32 + (Energy / 50) + (Book Rise / 6) magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.32f; // 32% @@ -54,6 +54,7 @@ public override void Initialize() chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + // Chance PvP % = 17 + (Energy / 50) + (Book Rise / 6) magicEffect.ChancePvp = this.Context.CreateNew(); magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17% @@ -69,7 +70,7 @@ public override void Initialize() chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); - // Duration of the effect + // Duration = 4 + (Energy / 100) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds magicEffect.Duration.MaximumValue = 100; // 100 Seconds @@ -80,6 +81,7 @@ public override void Initialize() durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + // Duration PvP = 5 + (Energy / 300) + ((Level - Target's Level) / 150) magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds @@ -96,7 +98,7 @@ public override void Initialize() durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); - // Power-down: target's defense decreases X% (applies last) + // Defense decrease % (applies last) = 20 + (Energy / 90) var decDefPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); @@ -109,6 +111,7 @@ public override void Initialize() decDefPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01 decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPerEnergy); + // Defense decrease PvP % (applies last) = 12 + (Energy / 110) var decDefPowerUpDefinitionPvp = this.Context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs index 14aaf7361..cc6c0ca85 100644 --- a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs @@ -30,7 +30,7 @@ public override void Initialize() var magicEffect = this.Context.CreateNew(); this.GameConfiguration.MagicEffects.Add(magicEffect); magicEffect.Number = (short)MagicEffectNumber.Sleep; - magicEffect.Name = "Sleep Skill Effect"; + magicEffect.Name = "Sleep Effect"; magicEffect.InformObservers = true; magicEffect.SendDuration = false; magicEffect.StopByDeath = true; @@ -38,6 +38,7 @@ public override void Initialize() magicEffect.MonsterTargetLevelDivisor = 20; magicEffect.PlayerTargetLevelDivisor = 100; + // Chance % = 20 + (Energy / 30) + (Book Rise / 6) magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.2f; // 20% @@ -53,6 +54,7 @@ public override void Initialize() chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + // Chance PvP % = 15 + (Energy / 37) + (Book Rise / 6) magicEffect.ChancePvp = this.Context.CreateNew(); magicEffect.ChancePvp.ConstantValue.Value = 0.15f; // 15% @@ -68,6 +70,7 @@ public override void Initialize() chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + // Duration = 5 + (Energy / 100) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 5; // 5 Seconds magicEffect.Duration.MaximumValue = 20; // 20 Seconds @@ -78,6 +81,7 @@ public override void Initialize() durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + // Duration = 4 + (Energy / 250) + ((Level - Target's Level) / 100) magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 4; // 4 Seconds magicEffect.DurationPvp.MaximumValue = 10; // 10 Seconds diff --git a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs index 69a1fb827..899563326 100644 --- a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs @@ -38,7 +38,7 @@ public override void Initialize() magicEffect.MonsterTargetLevelDivisor = 20; magicEffect.PlayerTargetLevelDivisor = 150; - // Chance to apply the effect + // Chance % = 32 + (Energy / 50) + (Book Rise / 6) magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.32f; // 32% @@ -54,6 +54,7 @@ public override void Initialize() chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + // Chance % = 17 + (Energy / 50) + (Book Rise / 6) magicEffect.ChancePvp = this.Context.CreateNew(); magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17% @@ -69,7 +70,7 @@ public override void Initialize() chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); - // Duration of the effect + // Duration = 4 + (Energy / 100) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds magicEffect.Duration.MaximumValue = 100; // 100 Seconds @@ -80,6 +81,7 @@ public override void Initialize() durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + // Duration = 5 + (Energy / 300) + ((Level - Target's Level) / 150) magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds @@ -96,7 +98,7 @@ public override void Initialize() durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); - // Power-down: target's physical damage decreases X% + // Phys damage decrease % = 4 + (Energy / 58) var decDmgPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition); decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); @@ -110,6 +112,7 @@ public override void Initialize() decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 0.01 decDmgPowerUpDefinition.Boost.RelatedValues.Add(decDmgPerEnergy); + // Phys damage decrease PvP % = 3 + (Energy / 93) var decDmgPowerUpDefinitionPvp = this.Context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDmgPowerUpDefinitionPvp); decDmgPowerUpDefinitionPvp.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs new file mode 100644 index 000000000..f1198d7d9 --- /dev/null +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -0,0 +1,482 @@ +// +// 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 adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("B1E2D6C3-1F4A-4D7C-8C2E-3F6D9A7B8E2F")] +public class AddSummonerBuffSkillsPlugIn : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Add Summoner Buff Skills"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills."; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.AddSummonerBuffSkills; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2025, 12, 29, 16, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + // Add new attributes + var innovationDefDecrement = context.CreateNew(Stats.InnovationDefDecrement.Id, Stats.InnovationDefDecrement.Designation, Stats.InnovationDefDecrement.Description); + gameConfiguration.Attributes.Add(innovationDefDecrement); + var isAsleep = context.CreateNew(Stats.IsAsleep.Id, Stats.IsAsleep.Designation, Stats.IsAsleep.Description); + gameConfiguration.Attributes.Add(isAsleep); + var fullyReflectDamageAfterHitChance = context.CreateNew(Stats.FullyReflectDamageAfterHitChance.Id, Stats.FullyReflectDamageAfterHitChance.Designation, Stats.FullyReflectDamageAfterHitChance.Description); + gameConfiguration.Attributes.Add(fullyReflectDamageAfterHitChance); + + // Fix reflect excellent defensive option + var excDefenseOptionsId = new Guid("00000083-0012-0000-0000-000000000000"); + if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == excDefenseOptionsId) is { } excDefenseOptions) + { + if (excDefenseOptions.PossibleOptions.FirstOrDefault(p => p.PowerUpDefinition?.TargetAttribute == Stats.DamageReflection) is { } dmgReflection) + { + dmgReflection.PowerUpDefinition!.Boost!.ConstantValue.Value = 0.05f; + } + } + + // Update Weakness magic effect + if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.Weakness) is { } weaknessEffect) + { + weaknessEffect.Chance = context.CreateNew(); + weaknessEffect.Chance.ConstantValue.Value = 0.1f; // 10% + } + + var innovationEffect = this.CreateInnovationMagicEffect(context, gameConfiguration); + var reflectionEffect = this.CreateReflectionMagicEffect(context, gameConfiguration); + var sleepEffect = this.CreateSleepMagicEffect(context, gameConfiguration); + var weaknessSummonerEffect = this.CreateWeaknessSummonerMagicEffect(context, gameConfiguration); + + // Update 3rd wing reflect option + var thirWingOptionDefId = new Guid("00000083-0067-0000-0000-000000000000"); + if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == thirWingOptionDefId) is { } thirWingOptionDef + && thirWingOptionDef.PossibleOptions.FirstOrDefault(po => po.PowerUpDefinition?.TargetAttribute == Stats.DamageReflection) is { } reflectOpt + && reflectOpt.PowerUpDefinition is not null) + { + reflectOpt.PowerUpDefinition.TargetAttribute = fullyReflectDamageAfterHitChance; + } + + // Update existing skills + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Innovation) is { } innovation) + { + innovation.MagicEffectDef = innovationEffect; + innovation.SkillType = SkillType.Buff; + innovation.AreaSkillSettings = + this.AddAreaSkillSettings(context, false, 0, 0, 0, maximumHitsPerAttack: 5, useTargetAreaFilter: true, targetAreaDiameter: 10); + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DamageReflection) is { } damageReflection) + { + damageReflection.MagicEffectDef = reflectionEffect; + damageReflection.SkillType = SkillType.Buff; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Sleep) is { } sleep) + { + sleep.MagicEffectDef = sleepEffect; + sleep.SkillType = SkillType.Buff; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.Weakness) is { } weakness) + { + weakness.MagicEffectDef = weaknessSummonerEffect; + weakness.SkillType = SkillType.Buff; + weakness.AreaSkillSettings = + this.AddAreaSkillSettings(context, false, 0, 0, 0, maximumHitsPerAttack: 5, useTargetAreaFilter: true, targetAreaDiameter: 10); + } + } + + private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, GameConfiguration gameConfiguration) + { + var magicEffect = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Innovation; + magicEffect.Name = "Innovation Effect"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.DurationDependsOnTargetLevel = true; + magicEffect.MonsterTargetLevelDivisor = 20; + magicEffect.PlayerTargetLevelDivisor = 150; + + // Chance % = 32 + (Energy / 50) + (Book Rise / 6) + magicEffect.Chance = context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.32f; // 32% + + var chancePerEnergy = context.CreateNew(); + chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + chancePerEnergy.InputOperator = InputOperator.Multiply; + chancePerEnergy.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerEnergy); + + var chancePerBookRise = context.CreateNew(); + chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration); + chancePerBookRise.InputOperator = InputOperator.Multiply; + chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + + // Chance PvP % = 17 + (Energy / 50) + (Book Rise / 6) + magicEffect.ChancePvp = context.CreateNew(); + magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17% + + var chancePerEnergyPvp = context.CreateNew(); + chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + chancePerEnergyPvp.InputOperator = InputOperator.Multiply; + chancePerEnergyPvp.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp); + + var chancePerBookRisePvp = context.CreateNew(); + chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration); + chancePerBookRisePvp.InputOperator = InputOperator.Multiply; + chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + + // Duration = 4 + (Energy / 100) + magicEffect.Duration = context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds + magicEffect.Duration.MaximumValue = 100; // 100 Seconds + + var durationPerEnergy = context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + magicEffect.DurationPvp = context.CreateNew(); + magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds + magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + + // Duration PvP = 5 + (Energy / 300) + ((Level - Target's Level) / 150) + var durationPerEnergyPvp = context.CreateNew(); + durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergyPvp.InputOperator = InputOperator.Multiply; + durationPerEnergyPvp.InputOperand = 1f / 300f; // 300 energy adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp); + + var durationPerLevelPvp = context.CreateNew(); + durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(gameConfiguration); + durationPerLevelPvp.InputOperator = InputOperator.Multiply; + durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + + // Defense decrease % (applies last) = 20 + (Energy / 90) + var decDefPowerUpDefinition = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); + decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); + decDefPowerUpDefinition.Boost = context.CreateNew(); + decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease + + var decDefPerEnergy = context.CreateNew(); + decDefPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + decDefPerEnergy.InputOperator = InputOperator.Multiply; + decDefPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01 + decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPerEnergy); + + // Defense decrease PvP % (applies last) = 12 + (Energy / 110) + var decDefPowerUpDefinitionPvp = context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); + decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); + decDefPowerUpDefinitionPvp.Boost = context.CreateNew(); + decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease + + var decDefPerEnergyPvp = context.CreateNew(); + decDefPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + decDefPerEnergyPvp.InputOperator = InputOperator.Multiply; + decDefPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 0.01 + decDefPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDefPerEnergyPvp); + + return magicEffect; + } + + private MagicEffectDefinition CreateReflectionMagicEffect(IContext context, GameConfiguration gameConfiguration) + { + var magicEffect = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Reflection; + magicEffect.Name = "Reflection Effect"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + + // Duration = 30 + (Energy / 24) + magicEffect.Duration = context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 30; // 30 Seconds + magicEffect.Duration.MaximumValue = 180; + + var durationPerEnergy = context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 24; // 24 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + // Reflection % = 30 + (Energy / 42) + var incReflectPowerUpDefinition = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(incReflectPowerUpDefinition); + incReflectPowerUpDefinition.TargetAttribute = Stats.DamageReflection.GetPersistent(gameConfiguration); + incReflectPowerUpDefinition.Boost = context.CreateNew(); + incReflectPowerUpDefinition.Boost.ConstantValue.Value = 0.3f; // 30% increase + incReflectPowerUpDefinition.Boost.MaximumValue = 0.6f; + + var incReflectPerEnergy = context.CreateNew(); + incReflectPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + incReflectPerEnergy.InputOperator = InputOperator.Multiply; + incReflectPerEnergy.InputOperand = 1f / 4200f; // 42 energy further increases 0.01 + incReflectPowerUpDefinition.Boost.RelatedValues.Add(incReflectPerEnergy); + + return magicEffect; + } + + private MagicEffectDefinition CreateSleepMagicEffect(IContext context, GameConfiguration gameConfiguration) + { + var magicEffect = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Sleep; + magicEffect.Name = "Sleep Effect"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.DurationDependsOnTargetLevel = true; + magicEffect.MonsterTargetLevelDivisor = 20; + magicEffect.PlayerTargetLevelDivisor = 100; + + // Chance % = 20 + (Energy / 30) + (Book Rise / 6) + magicEffect.Chance = context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.2f; // 20% + + var chancePerEnergy = context.CreateNew(); + chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + chancePerEnergy.InputOperator = InputOperator.Multiply; + chancePerEnergy.InputOperand = 1f / 3000f; // 30 energy adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerEnergy); + + var chancePerBookRise = context.CreateNew(); + chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration); + chancePerBookRise.InputOperator = InputOperator.Multiply; + chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + + // Chance PvP % = 15 + (Energy / 37) + (Book Rise / 6) + magicEffect.ChancePvp = context.CreateNew(); + magicEffect.ChancePvp.ConstantValue.Value = 0.15f; // 15% + + var chancePerEnergyPvp = context.CreateNew(); + chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + chancePerEnergyPvp.InputOperator = InputOperator.Multiply; + chancePerEnergyPvp.InputOperand = 1f / 3700f; // 37 energy adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp); + + var chancePerBookRisePvp = context.CreateNew(); + chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration); + chancePerBookRisePvp.InputOperator = InputOperator.Multiply; + chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + + // Duration = 5 + (Energy / 100) + magicEffect.Duration = context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 5; // 5 Seconds + magicEffect.Duration.MaximumValue = 20; // 20 Seconds + + var durationPerEnergy = context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + // Duration = 4 + (Energy / 250) + ((Level - Target's Level) / 100) + magicEffect.DurationPvp = context.CreateNew(); + magicEffect.DurationPvp.ConstantValue.Value = 4; // 4 Seconds + magicEffect.DurationPvp.MaximumValue = 10; // 10 Seconds + + var durationPerEnergyPvp = context.CreateNew(); + durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergyPvp.InputOperator = InputOperator.Multiply; + durationPerEnergyPvp.InputOperand = 1f / 250f; // 250 energy adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp); + + var durationPerLevelPvp = context.CreateNew(); + durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(gameConfiguration); + durationPerLevelPvp.InputOperator = InputOperator.Multiply; + durationPerLevelPvp.InputOperand = 1f / 100f; // 100 levels adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + + var isAsleep = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(isAsleep); + isAsleep.TargetAttribute = Stats.IsAsleep.GetPersistent(gameConfiguration); + isAsleep.Boost = context.CreateNew(); + isAsleep.Boost.ConstantValue.Value = 1; + + return magicEffect; + } + + private MagicEffectDefinition CreateWeaknessSummonerMagicEffect(IContext context, GameConfiguration gameConfiguration) + { + var magicEffect = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.Weakness; // We will map skill to effect by hand in this update, so we use this number instead of WeaknessSummoner + magicEffect.Name = "Weakness Effect (Summoner)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.DurationDependsOnTargetLevel = true; + magicEffect.MonsterTargetLevelDivisor = 20; + magicEffect.PlayerTargetLevelDivisor = 150; + + // Chance % = 32 + (Energy / 50) + (Book Rise / 6) + magicEffect.Chance = context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.32f; // 32% + + var chancePerEnergy = context.CreateNew(); + chancePerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + chancePerEnergy.InputOperator = InputOperator.Multiply; + chancePerEnergy.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerEnergy); + + var chancePerBookRise = context.CreateNew(); + chancePerBookRise.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration); + chancePerBookRise.InputOperator = InputOperator.Multiply; + chancePerBookRise.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.Chance.RelatedValues.Add(chancePerBookRise); + + // Chance % = 17 + (Energy / 50) + (Book Rise / 6) + magicEffect.ChancePvp = context.CreateNew(); + magicEffect.ChancePvp.ConstantValue.Value = 0.17f; // 17% + + var chancePerEnergyPvp = context.CreateNew(); + chancePerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + chancePerEnergyPvp.InputOperator = InputOperator.Multiply; + chancePerEnergyPvp.InputOperand = 1f / 5000f; // 50 energy adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerEnergyPvp); + + var chancePerBookRisePvp = context.CreateNew(); + chancePerBookRisePvp.InputAttribute = Stats.BookRise.GetPersistent(gameConfiguration); + chancePerBookRisePvp.InputOperator = InputOperator.Multiply; + chancePerBookRisePvp.InputOperand = 1f / 600f; // 6 book rise adds 1% chance + magicEffect.ChancePvp.RelatedValues.Add(chancePerBookRisePvp); + + // Duration = 4 + (Energy / 100) + magicEffect.Duration = context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds + magicEffect.Duration.MaximumValue = 100; // 100 Seconds + + var durationPerEnergy = context.CreateNew(); + durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergy.InputOperator = InputOperator.Multiply; + durationPerEnergy.InputOperand = 1f / 100f; // 100 energy adds 1s + magicEffect.Duration.RelatedValues.Add(durationPerEnergy); + + // Duration = 5 + (Energy / 300) + ((Level - Target's Level) / 150) + magicEffect.DurationPvp = context.CreateNew(); + magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds + magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + + var durationPerEnergyPvp = context.CreateNew(); + durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + durationPerEnergyPvp.InputOperator = InputOperator.Multiply; + durationPerEnergyPvp.InputOperand = 1f / 300f; // 300 energy adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerEnergyPvp); + + var durationPerLevelPvp = context.CreateNew(); + durationPerLevelPvp.InputAttribute = Stats.Level.GetPersistent(gameConfiguration); + durationPerLevelPvp.InputOperator = InputOperator.Multiply; + durationPerLevelPvp.InputOperand = 1f / 150f; // 150 levels adds 1s + magicEffect.DurationPvp.RelatedValues.Add(durationPerLevelPvp); + + // Phys damage decrease % = 4 + (Energy / 58) + var decDmgPowerUpDefinition = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition); + decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration); + decDmgPowerUpDefinition.Boost = context.CreateNew(); + decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.04f; // 4% decrease + decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // based on 4k total energy cap -- to-do: check zTeam + + var decDmgPerEnergy = context.CreateNew(); + decDmgPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + decDmgPerEnergy.InputOperator = InputOperator.Multiply; + decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 0.01 + decDmgPowerUpDefinition.Boost.RelatedValues.Add(decDmgPerEnergy); + + // Phys damage decrease PvP % = 3 + (Energy / 93) + var decDmgPowerUpDefinitionPvp = context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(decDmgPowerUpDefinitionPvp); + decDmgPowerUpDefinitionPvp.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration); + decDmgPowerUpDefinitionPvp.Boost = context.CreateNew(); + decDmgPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.03f; // 3% decrease + decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 1f; // -- to-do: check zTeam + + var decDmgPerEnergyPvp = context.CreateNew(); + decDmgPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); + decDmgPerEnergyPvp.InputOperator = InputOperator.Multiply; + decDmgPerEnergyPvp.InputOperand = 1f / 9300f; // 93 energy further decreases 0.01 + decDmgPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDmgPerEnergyPvp); + + return magicEffect; + } + + private AreaSkillSettings AddAreaSkillSettings( + IContext context, + bool useFrustumFilter, + float frustumStartWidth, + float frustumEndWidth, + float frustumDistance, + bool useDeferredHits = false, + TimeSpan delayPerOneDistance = default, + TimeSpan delayBetweenHits = default, + int minimumHitsPerTarget = 1, + int maximumHitsPerTarget = 1, + int maximumHitsPerAttack = default, + float hitChancePerDistanceMultiplier = 1.0f, + bool useTargetAreaFilter = false, + float targetAreaDiameter = default) + { + var areaSkillSettings = context.CreateNew(); + + 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.MaximumNumberOfHitsPerAttack = maximumHitsPerAttack; + areaSkillSettings.HitChancePerDistanceMultiplier = hitChancePerDistanceMultiplier; + + return areaSkillSettings; + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 35a6e9b1b..02468a338 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -334,4 +334,9 @@ public enum UpdateVersion /// The version of the . /// FixSkillMultipliers = 65, + + /// + /// The version of the . + /// + AddSummonerBuffSkills = 66, } \ No newline at end of file From 90aaf2641b89ad5c489a3c12252adddb633d4b45 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 29 Dec 2025 11:04:38 +0000 Subject: [PATCH 21/34] Added max reflect damage for mobs --- src/GameLogic/Player.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index bbdc80a60..907ca547b 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -2063,7 +2063,9 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski if (reflectPercentage > 0 && attacker is IAttackable attackableAttacker) { - var reflectedDamage = (int)((hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage); + var reflectedDamage = attackableAttacker is Player + ? (hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage + : attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg]; if (reflectedDamage <= 0) { return; From d25bd4298ff5e440a8dd6c2f949edd9ccb64494a Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 29 Dec 2025 11:20:15 +0000 Subject: [PATCH 22/34] Added condition for ammo consumption --- src/GameLogic/NPC/AttackableNpcBase.cs | 7 ++++++- src/GameLogic/Player.cs | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/GameLogic/NPC/AttackableNpcBase.cs b/src/GameLogic/NPC/AttackableNpcBase.cs index 01de16858..2e609c168 100644 --- a/src/GameLogic/NPC/AttackableNpcBase.cs +++ b/src/GameLogic/NPC/AttackableNpcBase.cs @@ -111,7 +111,12 @@ public int Health } var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); - attacker.ApplyAmmunitionConsumption(hitInfo); + + if (skill?.Skill is not { } attackSkill || attackSkill.DamageType != DamageType.Fenrir) + { + attacker.ApplyAmmunitionConsumption(hitInfo); + } + await this.HitAsync(hitInfo, attacker, skill?.Skill).ConfigureAwait(false); if (hitInfo.HealthDamage > 0) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 907ca547b..f55281110 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -627,7 +627,11 @@ public bool IsAnySelfDefenseActive() } var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); - attacker.ApplyAmmunitionConsumption(hitInfo); + + if (skill?.Skill is not { } attackSkill || attackSkill.DamageType != DamageType.Fenrir) + { + attacker.ApplyAmmunitionConsumption(hitInfo); + } if (hitInfo is { HealthDamage: 0, ShieldDamage: 0 }) { From 0d18f3330461ecd0a73cd661dcfa89fa4256780d Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 29 Dec 2025 11:43:13 +0000 Subject: [PATCH 23/34] Added removal of IsAsleep for players when attacked --- src/GameLogic/NPC/AttackableNpcBase.cs | 10 +++++----- src/GameLogic/Player.cs | 5 +++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/GameLogic/NPC/AttackableNpcBase.cs b/src/GameLogic/NPC/AttackableNpcBase.cs index 2e609c168..722d7a372 100644 --- a/src/GameLogic/NPC/AttackableNpcBase.cs +++ b/src/GameLogic/NPC/AttackableNpcBase.cs @@ -105,11 +105,6 @@ public int Health return null; } - if (this.Attributes[Stats.IsAsleep] > 0) - { - await this.MagicEffectList.ClearAllEffectsProducingSpecificStatAsync(Stats.IsAsleep).ConfigureAwait(false); - } - var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); if (skill?.Skill is not { } attackSkill || attackSkill.DamageType != DamageType.Fenrir) @@ -121,6 +116,11 @@ public int Health if (hitInfo.HealthDamage > 0) { + if (this.Attributes[Stats.IsAsleep] > 0) + { + await this.MagicEffectList.ClearAllEffectsProducingSpecificStatAsync(Stats.IsAsleep).ConfigureAwait(false); + } + if (attacker is Player player) { await player.AfterHitTargetAsync().ConfigureAwait(false); diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index f55281110..47a9465bf 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -644,6 +644,11 @@ public bool IsAnySelfDefenseActive() return hitInfo; } + if (this.Attributes[Stats.IsAsleep] > 0) + { + await this.MagicEffectList.ClearAllEffectsProducingSpecificStatAsync(Stats.IsAsleep).ConfigureAwait(false); + } + if (Rand.NextRandomBool(this.Attributes[Stats.FullyRecoverHealthAfterHitChance])) { this.Attributes[Stats.CurrentHealth] = this.Attributes[Stats.MaximumHealth]; From f60c8c11b0f391ad9a1e6fa619c1923baad9564e Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 29 Dec 2025 15:47:41 +0000 Subject: [PATCH 24/34] Added/changed skill max values --- .../Skills/InnovationEffectInitializer.cs | 6 ++++-- .../Skills/WeaknessSummonerEffectInitializer.cs | 8 ++++---- .../Updates/AddSummonerBuffSkillsPlugIn.cs | 14 ++++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs index 58d89b610..5b2222029 100644 --- a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -73,7 +73,7 @@ public override void Initialize() // Duration = 4 + (Energy / 100) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds - magicEffect.Duration.MaximumValue = 100; // 100 Seconds + magicEffect.Duration.MaximumValue = 44; // 44 Seconds (based on 4k total energy cap) var durationPerEnergy = this.Context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -84,7 +84,7 @@ public override void Initialize() // Duration PvP = 5 + (Energy / 300) + ((Level - Target's Level) / 150) magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds - magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + magicEffect.DurationPvp.MaximumValue = 18; // 18 Seconds (based on 4k total energy cap) var durationPerEnergyPvp = this.Context.CreateNew(); durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -104,6 +104,7 @@ public override void Initialize() decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); decDefPowerUpDefinition.Boost = this.Context.CreateNew(); decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease + decDefPowerUpDefinition.Boost.MaximumValue = 0.64f; // 64% decrease (based on 4k total energy cap) var decDefPerEnergy = this.Context.CreateNew(); decDefPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -117,6 +118,7 @@ public override void Initialize() decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); decDefPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease + decDefPowerUpDefinitionPvp.Boost.MaximumValue = 0.48f; // 48% decrease (based on 4k total energy cap) var decDefPerEnergyPvp = this.Context.CreateNew(); decDefPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs index 899563326..7ece6cf13 100644 --- a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs @@ -73,7 +73,7 @@ public override void Initialize() // Duration = 4 + (Energy / 100) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds - magicEffect.Duration.MaximumValue = 100; // 100 Seconds + magicEffect.Duration.MaximumValue = 44; // 44 Seconds (based on 4k total energy cap) var durationPerEnergy = this.Context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -84,7 +84,7 @@ public override void Initialize() // Duration = 5 + (Energy / 300) + ((Level - Target's Level) / 150) magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds - magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + magicEffect.DurationPvp.MaximumValue = 18; // 18 Seconds (based on 4k total energy cap) var durationPerEnergyPvp = this.Context.CreateNew(); durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -104,7 +104,7 @@ public override void Initialize() decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); decDmgPowerUpDefinition.Boost = this.Context.CreateNew(); decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.04f; // 4% decrease - decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // based on 4k total energy cap -- to-do: check zTeam + decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // 73% decrease (based on 4k total energy cap) var decDmgPerEnergy = this.Context.CreateNew(); decDmgPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -118,7 +118,7 @@ public override void Initialize() decDmgPowerUpDefinitionPvp.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); decDmgPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); decDmgPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.03f; // 3% decrease - decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 1f; // -- to-do: check zTeam + decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 0.46f; // 46% decrease (based on 4k total energy cap) var decDmgPerEnergyPvp = this.Context.CreateNew(); decDmgPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs index f1198d7d9..6f401729d 100644 --- a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -167,7 +167,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game // Duration = 4 + (Energy / 100) magicEffect.Duration = context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds - magicEffect.Duration.MaximumValue = 100; // 100 Seconds + magicEffect.Duration.MaximumValue = 44; // 44 Seconds (based on 4k total energy cap) var durationPerEnergy = context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); @@ -177,7 +177,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game magicEffect.DurationPvp = context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds - magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + magicEffect.DurationPvp.MaximumValue = 18; // 18 Seconds (based on 4k total energy cap) // Duration PvP = 5 + (Energy / 300) + ((Level - Target's Level) / 150) var durationPerEnergyPvp = context.CreateNew(); @@ -198,6 +198,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); decDefPowerUpDefinition.Boost = context.CreateNew(); decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease + decDefPowerUpDefinition.Boost.MaximumValue = 0.64f; // 64% decrease (based on 4k total energy cap) var decDefPerEnergy = context.CreateNew(); decDefPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); @@ -211,6 +212,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); decDefPowerUpDefinitionPvp.Boost = context.CreateNew(); decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease + decDefPowerUpDefinitionPvp.Boost.MaximumValue = 0.48f; // 48% decrease (based on 4k total energy cap) var decDefPerEnergyPvp = context.CreateNew(); decDefPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); @@ -389,7 +391,7 @@ private MagicEffectDefinition CreateWeaknessSummonerMagicEffect(IContext context // Duration = 4 + (Energy / 100) magicEffect.Duration = context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 4; // 4 Seconds - magicEffect.Duration.MaximumValue = 100; // 100 Seconds + magicEffect.Duration.MaximumValue = 44; // 44 Seconds (based on 4k total energy cap) var durationPerEnergy = context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); @@ -400,7 +402,7 @@ private MagicEffectDefinition CreateWeaknessSummonerMagicEffect(IContext context // Duration = 5 + (Energy / 300) + ((Level - Target's Level) / 150) magicEffect.DurationPvp = context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 5; // 5 Seconds - magicEffect.DurationPvp.MaximumValue = 20; // 20 Seconds + magicEffect.DurationPvp.MaximumValue = 18; // 18 Seconds (based on 4k total energy cap) var durationPerEnergyPvp = context.CreateNew(); durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); @@ -420,7 +422,7 @@ private MagicEffectDefinition CreateWeaknessSummonerMagicEffect(IContext context decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration); decDmgPowerUpDefinition.Boost = context.CreateNew(); decDmgPowerUpDefinition.Boost.ConstantValue.Value = 0.04f; // 4% decrease - decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // based on 4k total energy cap -- to-do: check zTeam + decDmgPowerUpDefinition.Boost.MaximumValue = 0.73f; // 73% decrease (based on 4k total energy cap) var decDmgPerEnergy = context.CreateNew(); decDmgPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); @@ -434,7 +436,7 @@ private MagicEffectDefinition CreateWeaknessSummonerMagicEffect(IContext context decDmgPowerUpDefinitionPvp.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration); decDmgPowerUpDefinitionPvp.Boost = context.CreateNew(); decDmgPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.03f; // 3% decrease - decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 1f; // -- to-do: check zTeam + decDmgPowerUpDefinitionPvp.Boost.MaximumValue = 0.46f; // 46% decrease (based on 4k total energy cap) var decDmgPerEnergyPvp = context.CreateNew(); decDmgPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(gameConfiguration); From 15ef02a6a29685664631d1d0f74979939255788d Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 10:41:27 +0000 Subject: [PATCH 25/34] Change comment --- .../Initialization/Skills/WeaknessEffectInitializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs index 4c96db2eb..e9680766b 100644 --- a/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/WeaknessEffectInitializer.cs @@ -41,7 +41,7 @@ public override void Initialize() magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% - // Target's physical damage decreases by 5% + // Physical damage decreases by 5% var decDmgPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDmgPowerUpDefinition); decDmgPowerUpDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(this.GameConfiguration); From 7b0610a9ef7ff46b5bb5e19c52c9b676f0c07034 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 16:09:24 +0000 Subject: [PATCH 26/34] Some fixes --- src/DataModel/Entities/SkillEntry.cs | 4 ++-- src/GameLogic/AttackableExtensions.cs | 9 ++++----- src/GameLogic/Player.cs | 10 +++++----- .../Updates/AddSummonerBuffSkillsPlugIn.cs | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/DataModel/Entities/SkillEntry.cs b/src/DataModel/Entities/SkillEntry.cs index d1078ff73..87e280262 100644 --- a/src/DataModel/Entities/SkillEntry.cs +++ b/src/DataModel/Entities/SkillEntry.cs @@ -62,7 +62,7 @@ public int Level /// It is an IElement, because the duration can be dependent from the player attributes. /// [Transient] - public (IElement PowerUpDuration, float? MaximumDuration)? PowerUpDuration { get; set; } + public IElement? PowerUpDuration { get; set; } /// /// Gets or sets the duration of the for PvP. @@ -71,7 +71,7 @@ public int Level /// It is an IElement, because the duration can be dependent from the player attributes. /// [Transient] - public (IElement PowerUpDuration, float? MaximumDuration)? PowerUpDurationPvp { get; set; } + public IElement? PowerUpDurationPvp { get; set; } /// /// Gets or sets the chance of applying the . diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index ff3ef0747..d86ae0dca 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -320,7 +320,7 @@ public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAt return; } - var duration = ((IElement, float?))(target is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!); + var duration = target is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!; var powerUps = target is Player ? skillEntry.PowerUpsPvp! : skillEntry.PowerUps!; await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, powerUps).ConfigureAwait(false); } @@ -436,7 +436,7 @@ public static async ValueTask TryApplyElementalEffectsAsync(this IAttackab && targetAttribute is not null) { // power-up is the wrong term here... it's more like a power-down ;-) - await target.ApplyMagicEffectAsync(attacker, effectDefinition, (duration, null), (targetAttribute, powerUp)).ConfigureAwait(false); + await target.ApplyMagicEffectAsync(attacker, effectDefinition, duration, (targetAttribute, powerUp)).ConfigureAwait(false); applied = true; } @@ -784,9 +784,9 @@ private static void GetBaseDmg(this IAttacker attacker, SkillEntry? skill, out i /// The magic effect definition. /// The duration. /// The power ups of the effect. - private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, (IElement DurElement, float? MaxDur) duration, params (AttributeDefinition Target, IElement Boost)[] powerUps) + private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, IElement duration, params (AttributeDefinition Target, IElement Boost)[] powerUps) { - float finalDuration = duration.DurElement.Value; + float finalDuration = duration.Value; if (magicEffectDefinition.DurationDependsOnTargetLevel) { @@ -797,7 +797,6 @@ private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IA } } - finalDuration = Math.Min(finalDuration, duration.MaxDur ?? float.MaxValue); TimeSpan durationSpan = TimeSpan.FromSeconds(finalDuration); if (durationSpan < TimeSpan.FromSeconds(1)) { diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 47a9465bf..1adb02576 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1477,8 +1477,8 @@ public void CreateMagicEffectPowerUp(SkillEntry skillEntry) var chanceElementPvp = skill.MagicEffectDef.ChancePvp is { } chancePvp ? this.Attributes!.CreateChanceElement(chancePvp) : chanceElement; AddSkillPowersToResult(skill.MagicEffectDef.PowerUpDefinitions, ref result); AddSkillPowersToResult(skill.MagicEffectDef.PowerUpDefinitionsPvp, ref resultPvp); - skillEntry.PowerUpDuration = (durationElement, skill.MagicEffectDef.Duration.MaximumValue); - skillEntry.PowerUpDurationPvp = (durationElementPvp, skill.MagicEffectDef.DurationPvp?.MaximumValue); + skillEntry.PowerUpDuration = durationElement; + skillEntry.PowerUpDurationPvp = durationElementPvp; skillEntry.PowerUpChance = chanceElement; skillEntry.PowerUpChancePvp = chanceElementPvp; skillEntry.PowerUps = result; @@ -2072,9 +2072,9 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski if (reflectPercentage > 0 && attacker is IAttackable attackableAttacker) { - var reflectedDamage = attackableAttacker is Player - ? (hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage - : attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg]; + var reflectedDamage = attackableAttacker is not Player && reflectPercentage == 1.0f + ? attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg] + : (hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage; if (reflectedDamage <= 0) { return; diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs index 6f401729d..52127d55d 100644 --- a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -58,7 +58,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio var fullyReflectDamageAfterHitChance = context.CreateNew(Stats.FullyReflectDamageAfterHitChance.Id, Stats.FullyReflectDamageAfterHitChance.Designation, Stats.FullyReflectDamageAfterHitChance.Description); gameConfiguration.Attributes.Add(fullyReflectDamageAfterHitChance); - // Fix reflect excellent defensive option + // Fix reflect excellent option var excDefenseOptionsId = new Guid("00000083-0012-0000-0000-000000000000"); if (gameConfiguration.ItemOptions.FirstOrDefault(io => io.GetId() == excDefenseOptionsId) is { } excDefenseOptions) { From 2f24715a08ec59bba80b2378513c00bf8c085026 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 16:53:24 +0000 Subject: [PATCH 27/34] Fixed damage reflection and added raven dmg reflect to owner --- src/GameLogic/Player.cs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 1adb02576..551e6de98 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -2059,22 +2059,29 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski return; } - var reflectPercentage = 0f; - var fullReflectPercentage = this.Attributes[Stats.FullyReflectDamageAfterHitChance]; - if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) + if (attacker is IAttackable or AttackerSurrogate) { - reflectPercentage = 1.0f; - } - else - { - reflectPercentage = this.Attributes[Stats.DamageReflection]; + var attackableAttacker = (attacker as AttackerSurrogate)?.Owner ?? (IAttackable)attacker; + + var reflectPercentage = this.Attributes[Stats.DamageReflection]; + if (reflectPercentage > 0) + { + var reflectedDamage = (hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage; + ReflectDamage(reflectedDamage, attackableAttacker); + } + + var fullReflectPercentage = this.Attributes[Stats.FullyReflectDamageAfterHitChance]; + if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) + { + var reflectedDamage = attackableAttacker is Player + ? (hitInfo.HealthDamage + hitInfo.ShieldDamage) + : attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg]; + ReflectDamage(reflectedDamage, attackableAttacker); + } } - if (reflectPercentage > 0 && attacker is IAttackable attackableAttacker) + void ReflectDamage(float reflectedDamage, IAttackable attackable) { - var reflectedDamage = attackableAttacker is not Player && reflectPercentage == 1.0f - ? attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg] - : (hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage; if (reflectedDamage <= 0) { return; @@ -2083,9 +2090,9 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski _ = Task.Run(async () => { await Task.Delay(500).ConfigureAwait(false); - if (attackableAttacker.IsAlive) + if (attackable.IsAlive) { - await attackableAttacker.ReflectDamageAsync(this, (uint)reflectedDamage).ConfigureAwait(false); + await attackable.ReflectDamageAsync(this, (uint)reflectedDamage).ConfigureAwait(false); } }); } From 97814a1e405982ee06833ff20760106965fafc7b Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 17:03:40 +0000 Subject: [PATCH 28/34] Excluded raven from full reflect --- src/GameLogic/Player.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 551e6de98..da945148a 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -2070,14 +2070,18 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski ReflectDamage(reflectedDamage, attackableAttacker); } - var fullReflectPercentage = this.Attributes[Stats.FullyReflectDamageAfterHitChance]; - if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) + if (attacker is not AttackerSurrogate) { - var reflectedDamage = attackableAttacker is Player - ? (hitInfo.HealthDamage + hitInfo.ShieldDamage) - : attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg]; - ReflectDamage(reflectedDamage, attackableAttacker); + var fullReflectPercentage = this.Attributes[Stats.FullyReflectDamageAfterHitChance]; + if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) + { + var reflectedDamage = attackableAttacker is Player + ? (hitInfo.HealthDamage + hitInfo.ShieldDamage) + : attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg]; + ReflectDamage(reflectedDamage, attackableAttacker); + } } + } void ReflectDamage(float reflectedDamage, IAttackable attackable) From 1d5cb0dd09303cca4a30d006d7864074bc4683c2 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 17:18:57 +0000 Subject: [PATCH 29/34] Added IsAlive condition upfront check to apply magic effects --- src/GameLogic/AttackableExtensions.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index d86ae0dca..efdfda0d7 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -369,6 +369,11 @@ public static async ValueTask ApplyRegenerationAsync(this IAttackable target, Pl /// The success of the appliance. public static async ValueTask TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, SkillEntry skillEntry) { + if (!target.IsAlive) + { + return false; + } + skillEntry.ThrowNotInitializedProperty(skillEntry.Skill is null, nameof(skillEntry.Skill)); var modifier = skillEntry.Skill.ElementalModifierTarget; if (modifier is null) @@ -415,6 +420,11 @@ public static async ValueTask TryApplyElementalEffectsAsync(this IAttackab /// public static async ValueTask TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, Skill skill, IElement? powerUp, IElement? duration, AttributeDefinition? targetAttribute) { + if (!target.IsAlive) + { + return false; + } + var modifier = skill.ElementalModifierTarget; if (modifier is null) { @@ -433,7 +443,8 @@ public static async ValueTask TryApplyElementalEffectsAsync(this IAttackab && !target.MagicEffectList.ActiveEffects.ContainsKey(effectDefinition.Number) && powerUp is not null && duration is not null - && targetAttribute is not null) + && targetAttribute is not null + && target.IsAlive) { // power-up is the wrong term here... it's more like a power-down ;-) await target.ApplyMagicEffectAsync(attacker, effectDefinition, duration, (targetAttribute, powerUp)).ConfigureAwait(false); From 7ce25e5829240e319a7352c1a51c004dd7bcbfc9 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 17:25:28 +0000 Subject: [PATCH 30/34] reverted leftover --- src/GameLogic/AttackableExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index efdfda0d7..63684c730 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -443,8 +443,7 @@ public static async ValueTask TryApplyElementalEffectsAsync(this IAttackab && !target.MagicEffectList.ActiveEffects.ContainsKey(effectDefinition.Number) && powerUp is not null && duration is not null - && targetAttribute is not null - && target.IsAlive) + && targetAttribute is not null) { // power-up is the wrong term here... it's more like a power-down ;-) await target.ApplyMagicEffectAsync(attacker, effectDefinition, duration, (targetAttribute, powerUp)).ConfigureAwait(false); From 6e40bbfce7a2d92f25899cd9af4eaefd5893ed19 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 30 Dec 2025 17:58:48 +0000 Subject: [PATCH 31/34] Reverted int cast to avoid reflected damage lower than 1 --- src/GameLogic/Player.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index da945148a..21b00a66f 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -2067,7 +2067,7 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski if (reflectPercentage > 0) { var reflectedDamage = (hitInfo.HealthDamage + hitInfo.ShieldDamage) * reflectPercentage; - ReflectDamage(reflectedDamage, attackableAttacker); + ReflectDamage((int)reflectedDamage, attackableAttacker); } if (attacker is not AttackerSurrogate) @@ -2076,15 +2076,14 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) { var reflectedDamage = attackableAttacker is Player - ? (hitInfo.HealthDamage + hitInfo.ShieldDamage) + ? hitInfo.HealthDamage + hitInfo.ShieldDamage : attackableAttacker.Attributes[Stats.MaximumPhysBaseDmg]; - ReflectDamage(reflectedDamage, attackableAttacker); + ReflectDamage((int)reflectedDamage, attackableAttacker); } } - } - void ReflectDamage(float reflectedDamage, IAttackable attackable) + void ReflectDamage(int reflectedDamage, IAttackable attackable) { if (reflectedDamage <= 0) { From c8f130d2931f32288dff3a79c5602e7d94fae7f9 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 31 Dec 2025 14:49:36 +0000 Subject: [PATCH 32/34] Adjusted comments --- .../Initialization/Skills/InnovationEffectInitializer.cs | 4 ++-- .../Initialization/Skills/ReflectionEffectInitializer.cs | 4 ++-- .../Initialization/Skills/SleepEffectInitializer.cs | 4 ++-- .../Skills/WeaknessSummonerEffectInitializer.cs | 4 ++-- .../Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs index 5b2222029..254a2ec3c 100644 --- a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -109,7 +109,7 @@ public override void Initialize() var decDefPerEnergy = this.Context.CreateNew(); decDefPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); decDefPerEnergy.InputOperator = InputOperator.Multiply; - decDefPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 0.01 + decDefPerEnergy.InputOperand = 1f / 9000f; // 90 energy further decreases 1% decDefPowerUpDefinition.Boost.RelatedValues.Add(decDefPerEnergy); // Defense decrease PvP % (applies last) = 12 + (Energy / 110) @@ -123,7 +123,7 @@ public override void Initialize() var decDefPerEnergyPvp = this.Context.CreateNew(); decDefPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); decDefPerEnergyPvp.InputOperator = InputOperator.Multiply; - decDefPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 0.01 + decDefPerEnergyPvp.InputOperand = 1f / 11000f; // 110 energy further decreases 1% decDefPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDefPerEnergyPvp); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs b/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs index d91603cc1..0cd51aa8d 100644 --- a/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/ReflectionEffectInitializer.cs @@ -52,12 +52,12 @@ public override void Initialize() incReflectPowerUpDefinition.TargetAttribute = Stats.DamageReflection.GetPersistent(this.GameConfiguration); incReflectPowerUpDefinition.Boost = this.Context.CreateNew(); incReflectPowerUpDefinition.Boost.ConstantValue.Value = 0.3f; // 30% increase - incReflectPowerUpDefinition.Boost.MaximumValue = 0.6f; + incReflectPowerUpDefinition.Boost.MaximumValue = 0.6f; // 60% increase var incReflectPerEnergy = this.Context.CreateNew(); incReflectPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); incReflectPerEnergy.InputOperator = InputOperator.Multiply; - incReflectPerEnergy.InputOperand = 1f / 4200f; // 42 energy further increases 0.01 + incReflectPerEnergy.InputOperand = 1f / 4200f; // 42 energy further increases 1% incReflectPowerUpDefinition.Boost.RelatedValues.Add(incReflectPerEnergy); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs index cc6c0ca85..c75397351 100644 --- a/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/SleepEffectInitializer.cs @@ -73,7 +73,7 @@ public override void Initialize() // Duration = 5 + (Energy / 100) magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 5; // 5 Seconds - magicEffect.Duration.MaximumValue = 20; // 20 Seconds + magicEffect.Duration.MaximumValue = 20; var durationPerEnergy = this.Context.CreateNew(); durationPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); @@ -84,7 +84,7 @@ public override void Initialize() // Duration = 4 + (Energy / 250) + ((Level - Target's Level) / 100) magicEffect.DurationPvp = this.Context.CreateNew(); magicEffect.DurationPvp.ConstantValue.Value = 4; // 4 Seconds - magicEffect.DurationPvp.MaximumValue = 10; // 10 Seconds + magicEffect.DurationPvp.MaximumValue = 10; var durationPerEnergyPvp = this.Context.CreateNew(); durationPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs index 7ece6cf13..1b5e51091 100644 --- a/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/WeaknessSummonerEffectInitializer.cs @@ -109,7 +109,7 @@ public override void Initialize() var decDmgPerEnergy = this.Context.CreateNew(); decDmgPerEnergy.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); decDmgPerEnergy.InputOperator = InputOperator.Multiply; - decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 0.01 + decDmgPerEnergy.InputOperand = 1f / 5800f; // 58 energy further decreases 1% decDmgPowerUpDefinition.Boost.RelatedValues.Add(decDmgPerEnergy); // Phys damage decrease PvP % = 3 + (Energy / 93) @@ -123,7 +123,7 @@ public override void Initialize() var decDmgPerEnergyPvp = this.Context.CreateNew(); decDmgPerEnergyPvp.InputAttribute = Stats.TotalEnergy.GetPersistent(this.GameConfiguration); decDmgPerEnergyPvp.InputOperator = InputOperator.Multiply; - decDmgPerEnergyPvp.InputOperand = 1f / 9300f; // 93 energy further decreases 0.01 + decDmgPerEnergyPvp.InputOperand = 1f / 9300f; // 93 energy further decreases 1% decDmgPowerUpDefinitionPvp.Boost.RelatedValues.Add(decDmgPerEnergyPvp); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs index 52127d55d..c3b8da1dd 100644 --- a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -13,7 +13,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using MUnique.OpenMU.PlugIns; /// -/// This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills. +/// This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills. It also fixes the 3rd wing full reflect option. /// [PlugIn(PlugInName, PlugInDescription)] [Guid("B1E2D6C3-1F4A-4D7C-8C2E-3F6D9A7B8E2F")] @@ -27,7 +27,7 @@ public class AddSummonerBuffSkillsPlugIn : UpdatePlugInBase /// /// The plug in description. /// - internal const string PlugInDescription = "This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills."; + internal const string PlugInDescription = "This update adds the Sleep, Innovation, Damage Reflection and Weakness Summoner buff skills. It also fixes the 3rd wing full reflect option."; /// public override string Name => PlugInName; From 41e1a4f62d77d7fa64adc0a5943e57823f17733c Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 31 Dec 2025 15:25:26 +0000 Subject: [PATCH 33/34] Added comment --- src/GameLogic/Player.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 21b00a66f..2ce747927 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -2072,6 +2072,7 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski if (attacker is not AttackerSurrogate) { + // Raven does not cause full reflect. var fullReflectPercentage = this.Attributes[Stats.FullyReflectDamageAfterHitChance]; if (fullReflectPercentage > 0 && Rand.NextRandomBool(fullReflectPercentage)) { From d8cb30789c37bbcc30ec2bdbfa7b629db67c21b2 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 1 Jan 2026 16:01:26 +0000 Subject: [PATCH 34/34] Reverted extra range for AreaSkillExplicitTarget by request --- src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 4966fcad3..caf326a3d 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -206,7 +206,7 @@ private static IEnumerable GetTargets(Player player, Point targetAr if (skill.SkillType == SkillType.AreaSkillExplicitTarget) { if (extraTarget?.CheckSkillTargetRestrictions(player, skill) is true - && player.IsInRange(extraTarget.Position, skill.Range) + && player.IsInRange(extraTarget.Position, skill.Range + 2) && !extraTarget.IsAtSafezone()) { yield return extraTarget;