From 50e525db99a53fd6653b105affdab6ef0aa7e6f0 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 23 Dec 2025 16:38:28 +0000 Subject: [PATCH 01/13] Added logic for rage fighter skills: killing blow, beast uppercut, chain drive, dragon roar, phoenix shot --- src/DataModel/Configuration/Skill.cs | 5 + src/GameLogic/DamageAttributes.cs | 10 + src/GameLogic/IAttackable.cs | 7 +- src/GameLogic/NPC/AttackableNpcBase.cs | 23 +- src/GameLogic/NPC/SoccerBall.cs | 2 +- src/GameLogic/Player.cs | 17 +- .../Skills/AreaSkillAttackAction.cs | 8 +- .../Skills/DragonRoarSkillPlugIn.cs | 93 + .../Skills/PhoenixShotSkillPlugIn.cs | 22 + .../Skills/TargetedSkillDefaultPlugin.cs | 7 +- .../RageSkillAttackHandlerPlugIn.cs | 6 +- .../RemoteView/World/ShowHitExtendedPlugIn.cs | 4 +- .../RemoteView/World/ShowHitPlugIn.cs | 4 +- .../ServerToClient/ConnectionExtensions.cs | 12 +- .../ServerToClient/ServerToClientPackets.cs | 44 +- .../ServerToClient/ServerToClientPackets.xml | 32 +- .../ServerToClientPacketsRef.cs | 36 + ..._AddSkillNumberOfHitsPerAttack.Designer.cs | 5179 +++++++++++++++++ ...217154940_AddSkillNumberOfHitsPerAttack.cs | 31 + .../EntityDataContextModelSnapshot.cs | 3 + .../Skills/DecreaseBlockEffectInitializer.cs | 61 + ...ReductionBeastUppercutEffectInitializer.cs | 58 + .../Skills/MagicEffectNumber.cs | 13 + .../Skills/SkillsInitializerBase.cs | 14 +- .../VersionSeasonSix/SkillsInitializer.cs | 17 +- 25 files changed, 5675 insertions(+), 33 deletions(-) create mode 100644 src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs create mode 100644 src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs create mode 100644 src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.Designer.cs create mode 100644 src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.cs create mode 100644 src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs create mode 100644 src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs diff --git a/src/DataModel/Configuration/Skill.cs b/src/DataModel/Configuration/Skill.cs index dee56455e..fb826a748 100644 --- a/src/DataModel/Configuration/Skill.cs +++ b/src/DataModel/Configuration/Skill.cs @@ -308,6 +308,11 @@ public partial class Skill [MemberOfAggregate] public virtual AreaSkillSettings? AreaSkillSettings { get; set; } + /// + /// Gets or sets the number of hits per attack. + /// + public short NumberOfHitsPerAttack { get; set; } + /// public override string ToString() { diff --git a/src/GameLogic/DamageAttributes.cs b/src/GameLogic/DamageAttributes.cs index f4b8b3ac7..9b2f1e7e9 100644 --- a/src/GameLogic/DamageAttributes.cs +++ b/src/GameLogic/DamageAttributes.cs @@ -54,4 +54,14 @@ public enum DamageAttributes /// The damage includes the combo bonus. /// Combo = 128, + + /// + /// The damage is a non-final hit of a quick sequence from a rage fighter skill. + /// + RageFighterStreakHit = 256, + + /// + /// The damage is the final hit of a quick sequence from a rage fighter skill. + /// + RageFighterStreakFinalHit = 512, } \ No newline at end of file diff --git a/src/GameLogic/IAttackable.cs b/src/GameLogic/IAttackable.cs index 0196b9044..0d4a781f7 100644 --- a/src/GameLogic/IAttackable.cs +++ b/src/GameLogic/IAttackable.cs @@ -55,8 +55,13 @@ public interface IAttackable : IIdentifiable, ILocateable /// The skill. /// If set to true, the attacker did a combination of skills. /// The damage factor. + /// + /// Not null when it's a rage fighter multiple hit skill: + /// true, if it's the final hit; + /// false, for other hits. + /// /// Returns information about the damage inflicted. - ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0); + ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0, bool? isFinalStreakHit = null); /// /// Reflects the damage which was done previously with or even to the . diff --git a/src/GameLogic/NPC/AttackableNpcBase.cs b/src/GameLogic/NPC/AttackableNpcBase.cs index e16c72b05..7fd706a45 100644 --- a/src/GameLogic/NPC/AttackableNpcBase.cs +++ b/src/GameLogic/NPC/AttackableNpcBase.cs @@ -64,7 +64,7 @@ protected AttackableNpcBase(MonsterSpawnArea spawnInfo, MonsterDefinition stats, /// /// true if teleporting; otherwise, false. /// - /// Teleporting for monsters oor npcs is not implemented yet. + /// Teleporting for monsters or npcs is not implemented yet. public bool IsTeleporting => false; /// @@ -98,7 +98,7 @@ public int Health || (this.SpawnArea.SpawnTrigger == SpawnTrigger.AutomaticDuringWave && (this._eventStateProvider?.IsSpawnWaveActive(this.SpawnArea.WaveNumber) ?? false)); /// - public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0) + public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0, bool? isStreakFinalHit = null) { if (this.Definition.ObjectKind == NpcObjectKind.Guard) { @@ -111,7 +111,7 @@ public int Health } var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false); - await this.HitAsync(hitInfo, attacker, skill?.Skill).ConfigureAwait(false); + await this.HitAsync(hitInfo, attacker, skill?.Skill, isStreakFinalHit).ConfigureAwait(false); if (hitInfo.HealthDamage > 0) { attacker.ApplyAmmunitionConsumption(hitInfo); @@ -175,7 +175,12 @@ protected virtual void OnRemoveFromMap() /// The hit information. /// The attacker. /// The skill. - protected async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? skill) + /// + /// Not null when it's a rage fighter multiple hit skill: + /// true, if it's the final hit; + /// false, for other hits. + /// + protected async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? skill, bool? isFinalStreakHit = null) { if (!this.IsAlive) { @@ -187,6 +192,16 @@ protected async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? s var player = this.GetHitNotificationTarget(attacker); if (player is not null) { + if (isFinalStreakHit.HasValue) + { + hitInfo.Attributes |= DamageAttributes.RageFighterStreakHit; + + if (isFinalStreakHit.Value || killed) + { + hitInfo.Attributes |= DamageAttributes.RageFighterStreakFinalHit; + } + } + await player.InvokeViewPlugInAsync(p => p.ShowHitAsync(this, hitInfo)).ConfigureAwait(false); player.GameContext.PlugInManager.GetPlugInPoint()?.AttackableGotHit(this, attacker, hitInfo); } diff --git a/src/GameLogic/NPC/SoccerBall.cs b/src/GameLogic/NPC/SoccerBall.cs index 800b87354..c207b912b 100644 --- a/src/GameLogic/NPC/SoccerBall.cs +++ b/src/GameLogic/NPC/SoccerBall.cs @@ -55,7 +55,7 @@ public SoccerBall(MonsterSpawnArea spawnInfo, MonsterDefinition stats, GameMap m public DeathInformation? LastDeath => null; /// - public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0) + public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0, bool? isFinalStreakHit = null) { var direction = attacker.GetDirectionTo(this); await this.MoveToDirectionAsync(direction, skill is { }).ConfigureAwait(false); diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index b511ceb88..f16682146 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -613,7 +613,7 @@ public bool IsAnySelfDefenseActive() } /// - public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0) + public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0, bool? isFinalStreakHit = null) { if (this.Attributes is null) { @@ -652,7 +652,7 @@ public bool IsAnySelfDefenseActive() this.Attributes[Stats.CurrentMana] = (manaFullyRecovered ? this.Attributes[Stats.MaximumMana] : this.Attributes[Stats.CurrentMana]) - hitInfo.ManaToll; } - await this.HitAsync(hitInfo, attacker, skill?.Skill).ConfigureAwait(false); + await this.HitAsync(hitInfo, attacker, skill?.Skill, isFinalStreakHit).ConfigureAwait(false); await this.DecreaseItemDurabilityAfterHitAsync(hitInfo).ConfigureAwait(false); if (attacker as IPlayerSurrogate is { } playerSurrogate) @@ -1942,7 +1942,7 @@ private async ValueTask GetSpawnGateOfCurrentMapAsync() ?? throw new InvalidOperationException($"Game map {spawnTargetMapDefinition} has no spawn gate."); } - private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? skill) + private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? skill, bool? isFinalStreakHit = null) { this.Summon?.Item2.RegisterHit(attacker); var healthDamage = hitInfo.HealthDamage; @@ -1958,6 +1958,17 @@ private async ValueTask HitAsync(HitInfo hitInfo, IAttacker attacker, Skill? ski } this.Attributes[Stats.CurrentHealth] -= healthDamage; + + if (isFinalStreakHit.HasValue) + { + hitInfo.Attributes |= DamageAttributes.RageFighterStreakHit; + + if (isFinalStreakHit.Value || this.Attributes[Stats.CurrentHealth] < 1) + { + hitInfo.Attributes |= DamageAttributes.RageFighterStreakFinalHit; + } + } + await this.InvokeViewPlugInAsync(p => p.ShowHitAsync(this, hitInfo)).ConfigureAwait(false); if (attacker is IWorldObserver observer) { diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 047892c28..c02978be0 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -256,8 +256,14 @@ private async ValueTask ApplySkillAsync(Player player, SkillEntry skillEntry, IA return; } - var hitInfo = await target.AttackByAsync(player, skillEntry, isCombo).ConfigureAwait(false); + var hitInfo = await target.AttackByAsync(player, skillEntry, isCombo, 1, skill.NumberOfHitsPerAttack > 1 ? false : null).ConfigureAwait(false); await target.TryApplyElementalEffectsAsync(player, skillEntry).ConfigureAwait(false); + + for (int hit = 2; hit <= skill.NumberOfHitsPerAttack; hit++) + { + await target.AttackByAsync(player, skillEntry, isCombo, 1, hit == skill.NumberOfHitsPerAttack).ConfigureAwait(false); + } + var baseSkill = skillEntry.GetBaseSkill(); if (player.GameContext.PlugInManager.GetStrategy(baseSkill.Number) is { } strategy) diff --git a/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs b/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs new file mode 100644 index 000000000..bc5617c7d --- /dev/null +++ b/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs @@ -0,0 +1,93 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.GuildWar; +using MUnique.OpenMU.GameLogic.NPC; +using MUnique.OpenMU.GameLogic.PlugIns; +using MUnique.OpenMU.Pathfinding; +using MUnique.OpenMU.PlugIns; + +/// +/// Handles the dragon roar skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets. +/// +[PlugIn(nameof(DragonRoarSkillPlugIn), "Handles the dragon roar skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets.")] +[Guid("a797a6ad-ac92-4731-a0fb-d46d4c1dd0df")] +public class DragonRoarSkillPlugIn : IAreaSkillPlugIn +{ + /// + public virtual short Key => 264; + + /// + /// Gets the range around the target, in which additional targets are searched. + /// + protected virtual short Range => 3; + + /// + public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter, HitInfo? hitInfo) + { + bool FilterTarget(IAttackable attackable) + { + if (attackable == target) + { + return false; + } + + if (attackable is Monster { SummonedBy: null } or Destructible) + { + return true; + } + + if (attackable is Monster { SummonedBy: not null } summoned) + { + return FilterTarget(summoned.SummonedBy); + } + + if (attackable is Player { DuelRoom.State: DuelState.DuelStarted } targetPlayer + && attacker is Player { DuelRoom.State: DuelState.DuelStarted } duelPlayer + && targetPlayer.DuelRoom == duelPlayer.DuelRoom + && targetPlayer.DuelRoom.IsDuelist(targetPlayer) && targetPlayer.DuelRoom.IsDuelist(duelPlayer)) + { + return true; + } + + if (attackable is Player { GuildWarContext.State: GuildWarState.Started } guildWarTarget + && attacker is Player { GuildWarContext.State: GuildWarState.Started } guildWarAttacker + && guildWarTarget.GuildWarContext == guildWarAttacker.GuildWarContext) + { + return true; + } + + return false; + } + + var extraTargets = target.CurrentMap?.GetAttackablesInRange(target.Position, this.Range).Where(FilterTarget).Take(7); + + if (extraTargets is null) + { + return; + } + + int i = 1; + var skill = skillEntry.Skill!; + foreach (var extraTarget in extraTargets) + { + if (i <= 3 || Rand.NextRandomBool()) + { + // first three 100% chance, others 50% chance + await extraTarget.AttackByAsync(attacker, skillEntry, false, 1, false).ConfigureAwait(false); + await extraTarget.TryApplyElementalEffectsAsync(attacker, skillEntry).ConfigureAwait(false); + + for (int hit = 2; hit <= skill.NumberOfHitsPerAttack; hit++) + { + await extraTarget.AttackByAsync(attacker, skillEntry, false, 1, hit == skill.NumberOfHitsPerAttack).ConfigureAwait(false); + } + } + + i++; + } + } +} \ No newline at end of file diff --git a/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs b/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs new file mode 100644 index 000000000..4e84a90a8 --- /dev/null +++ b/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs @@ -0,0 +1,22 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.PlugIns; + +/// +/// Handles the phoenix shot weapon skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets. +/// +[PlugIn(nameof(PhoenixShotSkillPlugIn), "Handles the phoenix shot weapon skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets.")] +[Guid("2c78e3db-ddc7-4bc6-8539-707f81638abf")] +public class PhoenixShotSkillPlugIn : DragonRoarSkillPlugIn +{ + /// + public override short Key => 270; + + /// + protected override short Range => 2; +} \ No newline at end of file diff --git a/src/GameLogic/PlayerActions/Skills/TargetedSkillDefaultPlugin.cs b/src/GameLogic/PlayerActions/Skills/TargetedSkillDefaultPlugin.cs index e4dcddb32..8b02af741 100644 --- a/src/GameLogic/PlayerActions/Skills/TargetedSkillDefaultPlugin.cs +++ b/src/GameLogic/PlayerActions/Skills/TargetedSkillDefaultPlugin.cs @@ -262,9 +262,14 @@ private async ValueTask ApplySkillAsync(Player player, IAttackable targete if (!target.IsAtSafezone() && !player.IsAtSafezone() && target != player) { - await target.AttackByAsync(player, skillEntry, isCombo).ConfigureAwait(false); + await target.AttackByAsync(player, skillEntry, isCombo, 1, skill.NumberOfHitsPerAttack > 1 ? false : null).ConfigureAwait(false); player.LastAttackedTarget.SetTarget(target); success = await target.TryApplyElementalEffectsAsync(player, skillEntry).ConfigureAwait(false) || success; + + for (int hit = 2; hit <= skill.NumberOfHitsPerAttack; hit++) + { + await target.AttackByAsync(player, skillEntry, isCombo, 1, hit == skill.NumberOfHitsPerAttack).ConfigureAwait(false); + } } } else if (skill.MagicEffectDef != null) diff --git a/src/GameServer/MessageHandler/RageSkillAttackHandlerPlugIn.cs b/src/GameServer/MessageHandler/RageSkillAttackHandlerPlugIn.cs index 14c95db54..17a5518d4 100644 --- a/src/GameServer/MessageHandler/RageSkillAttackHandlerPlugIn.cs +++ b/src/GameServer/MessageHandler/RageSkillAttackHandlerPlugIn.cs @@ -13,9 +13,9 @@ namespace MUnique.OpenMU.GameServer.MessageHandler; using MUnique.OpenMU.PlugIns; /// -/// Handler for rage fighter area skill attack packets (beast uppercut, chain drive, dark side). +/// Handler for rage fighter area skill attack packets (beast uppercut, chain drive, dragon slasher, dark side). /// -[PlugIn(nameof(RageSkillAttackHandlerPlugIn), "Handler for rage fighter area skill attack packets (dark side).")] +[PlugIn(nameof(RageSkillAttackHandlerPlugIn), "Handler for rage fighter area skill attack packets (beast uppercut, chain drive, dragon slasher, dark side).")] [Guid("D09ED5FB-DB6F-4707-A740-144CF2BA5D92")] [MinimumClient(6, 0, ClientLanguage.Invariant)] internal class RageSkillAttackHandlerPlugIn : IPacketHandlerPlugIn @@ -35,7 +35,7 @@ public async ValueTask HandlePacketAsync(Player player, Memory packet) var target = player.GetObject(targetId) as IAttackable; if (player.SkillList is null || player.SkillList.GetSkill(message.SkillId) is not { } skill - || (target is null || !target.IsInRange(player.Position, skill.Skill!.Range))) + || target is null || !target.IsInRange(player.Position, skill.Skill!.Range)) { return; } diff --git a/src/GameServer/RemoteView/World/ShowHitExtendedPlugIn.cs b/src/GameServer/RemoteView/World/ShowHitExtendedPlugIn.cs index 7b1b1a9d2..590a57cca 100644 --- a/src/GameServer/RemoteView/World/ShowHitExtendedPlugIn.cs +++ b/src/GameServer/RemoteView/World/ShowHitExtendedPlugIn.cs @@ -52,6 +52,8 @@ public async ValueTask ShowHitAsync(IAttackable target, HitInfo hitInfo) var targetId = target.GetId(this._player); await connection.SendObjectHitExtendedAsync( this.GetDamageKind(hitInfo.Attributes), + hitInfo.Attributes.HasFlag(DamageAttributes.RageFighterStreakHit), + hitInfo.Attributes.HasFlag(DamageAttributes.RageFighterStreakFinalHit), hitInfo.Attributes.HasFlag(DamageAttributes.Double), hitInfo.Attributes.HasFlag(DamageAttributes.Triple), targetId, @@ -98,7 +100,7 @@ private DamageKind GetDamageKind(DamageAttributes attributes) if (attributes.HasFlag(DamageAttributes.Reflected)) { - return DamageKind.ReflectedDarkPink; + return DamageKind.ReflectedLightPink; } if (attributes.HasFlag(DamageAttributes.Poison)) diff --git a/src/GameServer/RemoteView/World/ShowHitPlugIn.cs b/src/GameServer/RemoteView/World/ShowHitPlugIn.cs index 4aaf59b39..3fdb34605 100644 --- a/src/GameServer/RemoteView/World/ShowHitPlugIn.cs +++ b/src/GameServer/RemoteView/World/ShowHitPlugIn.cs @@ -62,6 +62,8 @@ await connection.SendObjectHitAsync( targetId, healthDamage, this.GetDamageKind(hitInfo.Attributes), + hitInfo.Attributes.HasFlag(DamageAttributes.RageFighterStreakHit), + hitInfo.Attributes.HasFlag(DamageAttributes.RageFighterStreakFinalHit), hitInfo.Attributes.HasFlag(DamageAttributes.Double), hitInfo.Attributes.HasFlag(DamageAttributes.Triple), shieldDamage).ConfigureAwait(false); @@ -118,7 +120,7 @@ private DamageKind GetDamageKind(DamageAttributes attributes) if (attributes.HasFlag(DamageAttributes.Reflected)) { - return DamageKind.ReflectedDarkPink; + return DamageKind.ReflectedLightPink; } if (attributes.HasFlag(DamageAttributes.Poison)) diff --git a/src/Network/Packets/ServerToClient/ConnectionExtensions.cs b/src/Network/Packets/ServerToClient/ConnectionExtensions.cs index 9f3e850b9..fefddfa0c 100644 --- a/src/Network/Packets/ServerToClient/ConnectionExtensions.cs +++ b/src/Network/Packets/ServerToClient/ConnectionExtensions.cs @@ -1049,6 +1049,8 @@ int WritePacket() /// The object id. /// The health damage. /// The kind. + /// The is rage fighter streak hit. + /// The is rage fighter streak final hit. /// The is double damage. /// The is triple damage. /// The shield damage. @@ -1056,7 +1058,7 @@ int WritePacket() /// Is sent by the server when: An object got hit in two cases: 1. When the own player is hit; 2. When the own player attacked some other object which got hit. /// Causes reaction on client side: The damage is shown at the object which received the hit. /// - public static async ValueTask SendObjectHitAsync(this IConnection? connection, byte @headerCode, ushort @objectId, ushort @healthDamage, DamageKind @kind, bool @isDoubleDamage, bool @isTripleDamage, ushort @shieldDamage) + public static async ValueTask SendObjectHitAsync(this IConnection? connection, byte @headerCode, ushort @objectId, ushort @healthDamage, DamageKind @kind, bool @isRageFighterStreakHit, bool @isRageFighterStreakFinalHit, bool @isDoubleDamage, bool @isTripleDamage, ushort @shieldDamage) { if (connection is null) { @@ -1071,6 +1073,8 @@ int WritePacket() packet.ObjectId = @objectId; packet.HealthDamage = @healthDamage; packet.Kind = @kind; + packet.IsRageFighterStreakHit = @isRageFighterStreakHit; + packet.IsRageFighterStreakFinalHit = @isRageFighterStreakFinalHit; packet.IsDoubleDamage = @isDoubleDamage; packet.IsTripleDamage = @isTripleDamage; packet.ShieldDamage = @shieldDamage; @@ -1086,6 +1090,8 @@ int WritePacket() /// /// The connection. /// The kind. + /// The is rage fighter streak hit. + /// The is rage fighter streak final hit. /// The is double damage. /// The is triple damage. /// The object id. @@ -1097,7 +1103,7 @@ int WritePacket() /// Is sent by the server when: An object got hit in two cases: 1. When the own player is hit; 2. When the own player attacked some other object which got hit. /// Causes reaction on client side: The damage is shown at the object which received the hit. /// - public static async ValueTask SendObjectHitExtendedAsync(this IConnection? connection, DamageKind @kind, bool @isDoubleDamage, bool @isTripleDamage, ushort @objectId, byte @healthStatus, byte @shieldStatus, uint @healthDamage, uint @shieldDamage) + public static async ValueTask SendObjectHitExtendedAsync(this IConnection? connection, DamageKind @kind, bool @isRageFighterStreakHit, bool @isRageFighterStreakFinalHit, bool @isDoubleDamage, bool @isTripleDamage, ushort @objectId, byte @healthStatus, byte @shieldStatus, uint @healthDamage, uint @shieldDamage) { if (connection is null) { @@ -1109,6 +1115,8 @@ int WritePacket() var length = ObjectHitExtendedRef.Length; var packet = new ObjectHitExtendedRef(connection.Output.GetSpan(length)[..length]); packet.Kind = @kind; + packet.IsRageFighterStreakHit = @isRageFighterStreakHit; + packet.IsRageFighterStreakFinalHit = @isRageFighterStreakFinalHit; packet.IsDoubleDamage = @isDoubleDamage; packet.IsTripleDamage = @isTripleDamage; packet.ObjectId = @objectId; diff --git a/src/Network/Packets/ServerToClient/ServerToClientPackets.cs b/src/Network/Packets/ServerToClient/ServerToClientPackets.cs index 6c5980ab5..d36adccad 100644 --- a/src/Network/Packets/ServerToClient/ServerToClientPackets.cs +++ b/src/Network/Packets/ServerToClient/ServerToClientPackets.cs @@ -6464,6 +6464,24 @@ public DamageKind Kind set => this._data.Span[7..].SetByteValue((byte)value, 4, 0); } + /// + /// Gets or sets the is rage fighter streak hit. + /// + public bool IsRageFighterStreakHit + { + get => this._data.Span[7..].GetBoolean(4); + set => this._data.Span[7..].SetBoolean(value, 4); + } + + /// + /// Gets or sets the is rage fighter streak final hit. + /// + public bool IsRageFighterStreakFinalHit + { + get => this._data.Span[7..].GetBoolean(5); + set => this._data.Span[7..].SetBoolean(value, 5); + } + /// /// Gets or sets the is double damage. /// @@ -6570,6 +6588,24 @@ public DamageKind Kind set => this._data.Span[3..].SetByteValue((byte)value, 4, 0); } + /// + /// Gets or sets the is rage fighter streak hit. + /// + public bool IsRageFighterStreakHit + { + get => this._data.Span[3..].GetBoolean(4); + set => this._data.Span[3..].SetBoolean(value, 4); + } + + /// + /// Gets or sets the is rage fighter streak final hit. + /// + public bool IsRageFighterStreakFinalHit + { + get => this._data.Span[3..].GetBoolean(5); + set => this._data.Span[3..].SetBoolean(value, 5); + } + /// /// Gets or sets the is double damage. /// @@ -30545,9 +30581,9 @@ public enum DamageKind CriticalBlue = 3, /// - /// Light pink color. + /// Light pink color, usually used by reflected damage. /// - LightPink = 4, + ReflectedLightPink = 4, /// /// Dark green color, usually used by poison damage. @@ -30555,9 +30591,9 @@ public enum DamageKind PoisonDarkGreen = 5, /// - /// Dark pink color, usually used by reflected damage. + /// Dark pink color. /// - ReflectedDarkPink = 6, + DarkPink = 6, /// /// White color. diff --git a/src/Network/Packets/ServerToClient/ServerToClientPackets.xml b/src/Network/Packets/ServerToClient/ServerToClientPackets.xml index d270b0d74..65e8368e7 100644 --- a/src/Network/Packets/ServerToClient/ServerToClientPackets.xml +++ b/src/Network/Packets/ServerToClient/ServerToClientPackets.xml @@ -2368,6 +2368,18 @@ Kind 4 + + 7 + 4 + Boolean + IsRageFighterStreakHit + + + 7 + 5 + Boolean + IsRageFighterStreakFinalHit + 7 6 @@ -2404,6 +2416,18 @@ Kind 4 + + 3 + 4 + Boolean + IsRageFighterStreakHit + + + 3 + 5 + Boolean + IsRageFighterStreakFinalHit + 3 6 @@ -11270,8 +11294,8 @@ 3 - LightPink - Light pink color. + ReflectedLightPink + Light pink color, usually used by reflected damage. 4 @@ -11280,8 +11304,8 @@ 5 - ReflectedDarkPink - Dark pink color, usually used by reflected damage. + DarkPink + Dark pink color. 6 diff --git a/src/Network/Packets/ServerToClient/ServerToClientPacketsRef.cs b/src/Network/Packets/ServerToClient/ServerToClientPacketsRef.cs index 08387116d..f723a70bf 100644 --- a/src/Network/Packets/ServerToClient/ServerToClientPacketsRef.cs +++ b/src/Network/Packets/ServerToClient/ServerToClientPacketsRef.cs @@ -6315,6 +6315,24 @@ public DamageKind Kind set => this._data[7..].SetByteValue((byte)value, 4, 0); } + /// + /// Gets or sets the is rage fighter streak hit. + /// + public bool IsRageFighterStreakHit + { + get => this._data[7..].GetBoolean(4); + set => this._data[7..].SetBoolean(value, 4); + } + + /// + /// Gets or sets the is rage fighter streak final hit. + /// + public bool IsRageFighterStreakFinalHit + { + get => this._data[7..].GetBoolean(5); + set => this._data[7..].SetBoolean(value, 5); + } + /// /// Gets or sets the is double damage. /// @@ -6421,6 +6439,24 @@ public DamageKind Kind set => this._data[3..].SetByteValue((byte)value, 4, 0); } + /// + /// Gets or sets the is rage fighter streak hit. + /// + public bool IsRageFighterStreakHit + { + get => this._data[3..].GetBoolean(4); + set => this._data[3..].SetBoolean(value, 4); + } + + /// + /// Gets or sets the is rage fighter streak final hit. + /// + public bool IsRageFighterStreakFinalHit + { + get => this._data[3..].GetBoolean(5); + set => this._data[3..].SetBoolean(value, 5); + } + /// /// Gets or sets the is double damage. /// diff --git a/src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.Designer.cs b/src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.Designer.cs new file mode 100644 index 000000000..98ea46d3c --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.Designer.cs @@ -0,0 +1,5179 @@ +// +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("20251217154940_AddSkillNumberOfHitsPerAttack")] + partial class AddSkillNumberOfHitsPerAttack + { + /// + 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("MagicEffectDefinitionId1") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BoostId") + .IsUnique(); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId1"); + + 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("NumberOfHitsPerAttack") + .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.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"); + + 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"); + + b.Navigation("RawPowerUpDefinitionsPvp"); + }); + + 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/20251217154940_AddSkillNumberOfHitsPerAttack.cs b/src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.cs new file mode 100644 index 000000000..75778b5d8 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20251217154940_AddSkillNumberOfHitsPerAttack.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + /// + public partial class AddSkillNumberOfHitsPerAttack : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "NumberOfHitsPerAttack", + schema: "config", + table: "Skill", + type: "smallint", + nullable: false, + defaultValue: (short)0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "NumberOfHitsPerAttack", + schema: "config", + table: "Skill"); + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs index b04972c73..9176f8f9c 100644 --- a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs +++ b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs @@ -3171,6 +3171,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Number") .HasColumnType("smallint"); + b.Property("NumberOfHitsPerAttack") + .HasColumnType("smallint"); + b.Property("Range") .HasColumnType("smallint"); diff --git a/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs b/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs new file mode 100644 index 000000000..2982edeb7 --- /dev/null +++ b/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs @@ -0,0 +1,61 @@ +// +// 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 decrease block effect which results from Phoenix Shot (Rage Fighter) skill. +/// +public class DecreaseBlockEffectInitializer : InitializerBase +{ + /// + /// Initializes a new instance of the class. + /// + /// The context. + /// The game configuration. + public DecreaseBlockEffectInitializer(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.DecreaseBlock; + magicEffect.Name = "Decrease Block Effect (Phoenix Shot)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + + // Chance to apply the effect + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% + + // Duration of the effect + magicEffect.Duration = this.Context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 10; // 10 Seconds + + // Power-down: target's defense rate decreases X% + var decDefRatePowerUpDefinition = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(decDefRatePowerUpDefinition); + decDefRatePowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(this.GameConfiguration); + decDefRatePowerUpDefinition.Boost = this.Context.CreateNew(); + decDefRatePowerUpDefinition.Boost.ConstantValue.Value = 0.5f; // 50% decrease + decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + var decDefRatePowerUpDefinitionPvp = this.Context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(decDefRatePowerUpDefinitionPvp); + decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvp.GetPersistent(this.GameConfiguration); + decDefRatePowerUpDefinitionPvp.Boost = this.Context.CreateNew(); + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.8f; // 20% decrease + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs new file mode 100644 index 000000000..05b82855a --- /dev/null +++ b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs @@ -0,0 +1,58 @@ +// +// 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 defense reduction effect which results from Beast Uppercut (Rage Fighter) skill. +/// +public class DefenseReductionBeastUppercutEffectInitializer : InitializerBase +{ + /// + /// Initializes a new instance of the class. + /// + /// The context. + /// The game configuration. + public DefenseReductionBeastUppercutEffectInitializer(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.DefenseReductionBeastUppercut; + magicEffect.Name = "Defense Reduction Effect (Beast Uppercut)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = true; + magicEffect.StopByDeath = true; + magicEffect.Duration = this.Context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = (float)TimeSpan.FromSeconds(10).TotalSeconds; + + // Chance to apply the effect + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% + + var reducePvmDefenseEffect = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(reducePvmDefenseEffect); + reducePvmDefenseEffect.TargetAttribute = Stats.DefensePvm.GetPersistent(this.GameConfiguration); + reducePvmDefenseEffect.Boost = this.Context.CreateNew(); + reducePvmDefenseEffect.Boost.ConstantValue.Value = 0.9f; + reducePvmDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + var reducePvpDefenseEffect = this.Context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(reducePvpDefenseEffect); + reducePvpDefenseEffect.TargetAttribute = Stats.DefensePvp.GetPersistent(this.GameConfiguration); + reducePvpDefenseEffect.Boost = this.Context.CreateNew(); + reducePvpDefenseEffect.Boost.ConstantValue.Value = 0.9f; + reducePvpDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/MagicEffectNumber.cs b/src/Persistence/Initialization/Skills/MagicEffectNumber.cs index 28275b5b5..75eaed314 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 Beast Uppercut skill effect number. + /// + /// + /// Internal. Proxy of . + /// + DefenseReductionBeastUppercut = -5, + /// /// The Weakness (Summoner) skill effect number. /// @@ -293,6 +301,11 @@ internal enum MagicEffectNumber : short /// IncreaseBlock = 131, + /// + /// The decrease block effect of the rage fighter. + /// + DecreaseBlock = 132, + /// /// The wiz enhance strengthener effect. /// diff --git a/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs b/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs index 6d7863c1b..45463581e 100644 --- a/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs +++ b/src/Persistence/Initialization/Skills/SkillsInitializerBase.cs @@ -47,6 +47,7 @@ protected SkillsInitializerBase(IContext context, GameConfiguration gameConfigur /// If set to true, the skill moves the player to the target. /// If set to true, it moves target randomly. /// The cooldown minutes. + /// The number of hits per attack. protected void CreateSkill( SkillNumber number, string name, @@ -66,7 +67,8 @@ protected void CreateSkill( SkillTargetRestriction targetRestriction = SkillTargetRestriction.Undefined, bool movesToTarget = false, bool movesTarget = false, - int cooldownMinutes = 0) + int cooldownMinutes = 0, + byte hitsPerAttack = 1) { var skill = this.Context.CreateNew(); this.GameConfiguration.Skills.Add(skill); @@ -75,6 +77,7 @@ protected void CreateSkill( skill.MovesToTarget = movesToTarget; skill.MovesTarget = movesTarget; skill.AttackDamage = damage; + skill.NumberOfHitsPerAttack = hitsPerAttack; this.CreateSkillRequirementIfNeeded(skill, Stats.Level, levelRequirement); this.CreateSkillRequirementIfNeeded(skill, Stats.TotalLeadership, leadershipRequirement); @@ -157,7 +160,7 @@ protected void AddAreaSkillSettings( private void ApplyElementalModifier(ElementalType elementalModifier, Skill skill) { - if ((SkillNumber)skill.Number is SkillNumber.IceArrow or SkillNumber.IceArrowStrengthener) + if ((SkillNumber)skill.Number is SkillNumber.IceArrow) { skill.ElementalModifierTarget = Stats.IceResistance.GetPersistent(this.GameConfiguration); skill.MagicEffectDef = this.CreateEffect(ElementalType.Ice, MagicEffectNumber.Freeze, Stats.IsFrozen, 5); @@ -169,6 +172,13 @@ private void ApplyElementalModifier(ElementalType elementalModifier, Skill skill case ElementalType.Ice: skill.ElementalModifierTarget = Stats.IceResistance.GetPersistent(this.GameConfiguration); skill.MagicEffectDef = this.CreateEffect(ElementalType.Ice, MagicEffectNumber.Iced, Stats.IsIced, 10); + + if ((SkillNumber)skill.Number is SkillNumber.ChainDrive) + { + skill.MagicEffectDef.Chance = this.Context.CreateNew(); + skill.MagicEffectDef.Chance.ConstantValue.Value = 0.4f; + } + break; case ElementalType.Poison: skill.ElementalModifierTarget = Stats.PoisonResistance.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 38a504e91..1a7c3454a 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -70,6 +70,8 @@ internal class SkillsInitializer : SkillsInitializerBase { SkillNumber.Sleep, MagicEffectNumber.Sleep }, { SkillNumber.Weakness, MagicEffectNumber.WeaknessSummoner }, { SkillNumber.Innovation, MagicEffectNumber.Innovation }, + { SkillNumber.BeastUppercut, MagicEffectNumber.DefenseReductionBeastUppercut }, + { SkillNumber.PhoenixShot, MagicEffectNumber.DecreaseBlock }, }; private readonly IDictionary _masterSkillRoots; @@ -213,17 +215,17 @@ public override void Initialize() this.CreateSkill(SkillNumber.ChaoticDiseier, "Chaotic Diseier", CharacterClasses.AllLords, DamageType.Physical, 190, 6, 15, 50, 100, 16, skillType: SkillType.AreaSkillAutomaticHits); this.AddAreaSkillSettings(SkillNumber.ChaoticDiseier, true, 1.5f, 1.5f, 6f); this.CreateSkill(SkillNumber.DoppelgangerSelfExplosion, "Doppelganger Self Explosion", CharacterClasses.AllMGs, DamageType.Wizardry, 140, 3, 25, 20, 100, elementalModifier: ElementalType.Fire); - this.CreateSkill(SkillNumber.KillingBlow, "Killing Blow", CharacterClasses.AllFighters, DamageType.Physical, distance: 2, manaConsumption: 9, elementalModifier: ElementalType.Earth); - this.CreateSkill(SkillNumber.BeastUppercut, "Beast Uppercut", CharacterClasses.AllFighters, DamageType.Physical, distance: 2, manaConsumption: 9, elementalModifier: ElementalType.Fire); - this.CreateSkill(SkillNumber.ChainDrive, "Chain Drive", CharacterClasses.AllFighters, DamageType.Physical, distance: 4, abilityConsumption: 20, manaConsumption: 15, levelRequirement: 150, elementalModifier: ElementalType.Ice); + this.CreateSkill(SkillNumber.KillingBlow, "Killing Blow", CharacterClasses.AllFighters, DamageType.Physical, distance: 2, manaConsumption: 9, elementalModifier: ElementalType.Earth, hitsPerAttack: 4); // 1 packet => 1*4 hits + this.CreateSkill(SkillNumber.BeastUppercut, "Beast Uppercut", CharacterClasses.AllFighters, DamageType.Physical, distance: 2, manaConsumption: 9, elementalModifier: ElementalType.Fire, hitsPerAttack: 2); // 2 packets => 2*2 hits + this.CreateSkill(SkillNumber.ChainDrive, "Chain Drive", CharacterClasses.AllFighters, DamageType.Physical, distance: 4, abilityConsumption: 20, manaConsumption: 15, levelRequirement: 150, elementalModifier: ElementalType.Ice, hitsPerAttack: 4); // 2 packets => 2*4 hits this.CreateSkill(SkillNumber.DarkSide, "Dark Side", CharacterClasses.AllFighters, DamageType.Physical, distance: 4, manaConsumption: 70, levelRequirement: 180, elementalModifier: ElementalType.Wind); - this.CreateSkill(SkillNumber.DragonRoar, "Dragon Roar", CharacterClasses.AllFighters, DamageType.Physical, distance: 3, abilityConsumption: 30, manaConsumption: 50, levelRequirement: 150, elementalModifier: ElementalType.Earth, skillType: SkillType.AreaSkillAutomaticHits); + this.CreateSkill(SkillNumber.DragonRoar, "Dragon Roar", CharacterClasses.AllFighters, DamageType.Physical, distance: 3, abilityConsumption: 30, manaConsumption: 50, levelRequirement: 150, elementalModifier: ElementalType.Earth, skillType: SkillType.AreaSkillExplicitTarget, hitsPerAttack: 4); // 1 packet => 1*4 hits this.CreateSkill(SkillNumber.DragonSlasher, "Dragon Slasher", CharacterClasses.AllFighters, DamageType.Physical, distance: 4, abilityConsumption: 100, manaConsumption: 100, levelRequirement: 200, elementalModifier: ElementalType.Wind); this.CreateSkill(SkillNumber.IgnoreDefense, "Ignore Defense", CharacterClasses.AllFighters, DamageType.Physical, distance: 3, abilityConsumption: 10, manaConsumption: 50, levelRequirement: 120, energyRequirement: 404, skillType: SkillType.Buff, skillTarget: SkillTarget.ImplicitPlayer); this.CreateSkill(SkillNumber.IncreaseHealth, "Increase Health", CharacterClasses.AllFighters, DamageType.Physical, distance: 7, abilityConsumption: 10, manaConsumption: 50, levelRequirement: 80, energyRequirement: 132, skillType: SkillType.Buff, skillTarget: SkillTarget.ImplicitParty); this.CreateSkill(SkillNumber.IncreaseBlock, "Increase Block", CharacterClasses.AllFighters, DamageType.Physical, distance: 7, abilityConsumption: 10, manaConsumption: 50, levelRequirement: 50, energyRequirement: 80, skillType: SkillType.Buff, skillTarget: SkillTarget.ImplicitParty); this.CreateSkill(SkillNumber.Charge, "Charge", CharacterClasses.AllFighters, DamageType.Physical, 90, 4, 15, 20); - this.CreateSkill(SkillNumber.PhoenixShot, "Phoenix Shot", CharacterClasses.AllFighters, DamageType.Physical, distance: 4, manaConsumption: 30, elementalModifier: ElementalType.Earth, skillType: SkillType.AreaSkillExplicitTarget); + this.CreateSkill(SkillNumber.PhoenixShot, "Phoenix Shot", CharacterClasses.AllFighters, DamageType.Physical, distance: 4, manaConsumption: 30, elementalModifier: ElementalType.Earth, skillType: SkillType.AreaSkillExplicitTarget, hitsPerAttack: 4); // 1 packet => 1*4 hits // Generic monster skills: this.CreateSkill(SkillNumber.MonsterSkill, "Generic Monster Skill", distance: 5, skillType: SkillType.Other); @@ -670,6 +672,8 @@ 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 DefenseReductionBeastUppercutEffectInitializer(this.Context, this.GameConfiguration).Initialize(); + new DecreaseBlockEffectInitializer(this.Context, this.GameConfiguration).Initialize(); } private void MapSkillsToEffects() @@ -686,6 +690,9 @@ private void MapSkillsToEffects() case (short)MagicEffectNumber.WeaknessSummoner: effect.Number = (short)MagicEffectNumber.Weakness; break; + case (short)MagicEffectNumber.DefenseReductionBeastUppercut: + effect.Number = (short)MagicEffectNumber.DefenseReduction; + break; default: // no change needed break; From c20283a66ce2948a365116a5cad772415869e5ff Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 2 Jan 2026 17:46:25 +0000 Subject: [PATCH 02/13] Changed parameter name --- src/GameLogic/NPC/AttackableNpcBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameLogic/NPC/AttackableNpcBase.cs b/src/GameLogic/NPC/AttackableNpcBase.cs index ca2ef9a08..a1a6d7d34 100644 --- a/src/GameLogic/NPC/AttackableNpcBase.cs +++ b/src/GameLogic/NPC/AttackableNpcBase.cs @@ -98,7 +98,7 @@ public int Health || (this.SpawnArea.SpawnTrigger == SpawnTrigger.AutomaticDuringWave && (this._eventStateProvider?.IsSpawnWaveActive(this.SpawnArea.WaveNumber) ?? false)); /// - public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0, bool? isStreakFinalHit = null) + public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0, bool? isFinalStreakHit = null) { if (this.Definition.ObjectKind == NpcObjectKind.Guard) { @@ -112,7 +112,7 @@ public int Health attacker.ApplyAmmunitionConsumption(hitInfo); } - await this.HitAsync(hitInfo, attacker, skill?.Skill, isStreakFinalHit).ConfigureAwait(false); + await this.HitAsync(hitInfo, attacker, skill?.Skill, isFinalStreakHit).ConfigureAwait(false); if (hitInfo.HealthDamage > 0) { From b204f6b2c3a0f7265b733a22f37ea246b5f09b42 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 2 Jan 2026 18:49:06 +0000 Subject: [PATCH 03/13] Added update plugin and master skills changes --- .../Skills/AreaSkillAttackAction.cs | 3 +- .../Skills/DecreaseBlockEffectInitializer.cs | 9 +- ...ReductionBeastUppercutEffectInitializer.cs | 2 - .../Skills/IncreaseHealthEffectInitializer.cs | 1 + .../FixRageFighterMultipleHitSkillsPlugIn.cs | 197 ++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 5 + .../VersionSeasonSix/SkillsInitializer.cs | 12 +- 7 files changed, 213 insertions(+), 16 deletions(-) create mode 100644 src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs diff --git a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs index 809d5f552..82f21f139 100644 --- a/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs +++ b/src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs @@ -253,8 +253,9 @@ 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) + if (skill.SkillType == SkillType.Buff) { await target.ApplyMagicEffectAsync(player, skillEntry).ConfigureAwait(false); return; diff --git a/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs b/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs index 2982edeb7..494f1b9af 100644 --- a/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs @@ -34,16 +34,11 @@ public override void Initialize() magicEffect.InformObservers = true; magicEffect.SendDuration = false; magicEffect.StopByDeath = true; - - // Chance to apply the effect - magicEffect.Chance = this.Context.CreateNew(); - magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% - - // Duration of the effect magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = 10; // 10 Seconds + magicEffect.Chance = this.Context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% - // Power-down: target's defense rate decreases X% var decDefRatePowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDefRatePowerUpDefinition); decDefRatePowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(this.GameConfiguration); diff --git a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs index 05b82855a..785ef60e6 100644 --- a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs @@ -36,8 +36,6 @@ public override void Initialize() magicEffect.StopByDeath = true; magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = (float)TimeSpan.FromSeconds(10).TotalSeconds; - - // Chance to apply the effect magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% diff --git a/src/Persistence/Initialization/Skills/IncreaseHealthEffectInitializer.cs b/src/Persistence/Initialization/Skills/IncreaseHealthEffectInitializer.cs index 8b64fe730..48392c444 100644 --- a/src/Persistence/Initialization/Skills/IncreaseHealthEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/IncreaseHealthEffectInitializer.cs @@ -57,6 +57,7 @@ public override void Initialize() powerUpDefinition.Boost = this.Context.CreateNew(); powerUpDefinition.Boost.ConstantValue.Value = 16f; powerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddFinal; + powerUpDefinition.Boost.MaximumValue = 200f; powerUpDefinition.Boost.RelatedValues.Add(boostPerEnergy); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs new file mode 100644 index 000000000..0daa555e3 --- /dev/null +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -0,0 +1,197 @@ +// +// 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 missing multiple hits to the Killing Blow, Beast Uppercut, Chain Drive, Dragon Roar and Phoenix Shot Rage Fighter skills, as well as their magic effects. +/// +[PlugIn(PlugInName, PlugInDescription)] +[Guid("EDDD17F9-BEA5-40F0-A653-8567566C40E7")] +public class FixRageFighterMultipleHitSkillsPlugIn : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Fix Rage Fighter Multiple Hit Skills"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This update adds the missing multiple hits to the Killing Blow, Beast Uppercut, Chain Drive, Dragon Roar and Phoenix Shot Rage Fighter skills, as well as their magic effects."; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override UpdateVersion Version => UpdateVersion.FixRageFighterMultipleHitSkills; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2026, 1, 3, 16, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + // Update Increase Health (Vitality) magic effect + if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.IncreaseHealth) is { } increaseHealthEffect + && increaseHealthEffect.PowerUpDefinitions.FirstOrDefault() is { } increaseHealthPowerUp) + { + increaseHealthPowerUp.Boost!.MaximumValue = 200f; + } + + var defensereductionBeastUppercut = this.CreateDefenseReductionBeastUppercutMagicEffect(context, gameConfiguration); + var decreaseBlockEffect = this.CreateDecreaseBlockMagicEffect(context, gameConfiguration); + + // Apply default value of NumberOfHitsPerAttack to all skills + foreach (var skill in gameConfiguration.Skills) + { + skill.NumberOfHitsPerAttack = 1; + } + + // Update existing skills + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlow) is { } killingBlow) + { + killingBlow.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowStrengthener) is { } killingBlowStrengthener) + { + killingBlowStrengthener.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowMastery) is { } killingBlowMastery) + { + killingBlowMastery.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercut) is { } beastUppercut) + { + beastUppercut.MagicEffectDef = defensereductionBeastUppercut; + beastUppercut.NumberOfHitsPerAttack = 2; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercutStrengthener) is { } beastUppercutStrengthener) + { + beastUppercutStrengthener.MagicEffectDef = defensereductionBeastUppercut; + beastUppercutStrengthener.NumberOfHitsPerAttack = 2; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercutMastery) is { } beastUppercutMastery) + { + beastUppercutMastery.MagicEffectDef = defensereductionBeastUppercut; + beastUppercutMastery.NumberOfHitsPerAttack = 2; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDrive) is { } chainDrive) + { + chainDrive.MagicEffectDef!.Chance = context.CreateNew(); + chainDrive.MagicEffectDef.Chance.ConstantValue.Value = 0.4f; + chainDrive.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDriveStrengthener) is { } chainDriveStrengthener) + { + chainDriveStrengthener.MagicEffectDef!.Chance = context.CreateNew(); + chainDriveStrengthener.MagicEffectDef.Chance.ConstantValue.Value = 0.4f; + chainDriveStrengthener.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DragonRoar) is { } dragonRoar) + { + dragonRoar.SkillType = SkillType.AreaSkillExplicitTarget; + dragonRoar.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.DragonRoarStrengthener) is { } dragonRoarStrengthener) + { + dragonRoarStrengthener.SkillType = SkillType.AreaSkillExplicitTarget; + dragonRoarStrengthener.NumberOfHitsPerAttack = 4; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.PhoenixShot) is { } phoenixShot) + { + phoenixShot.MagicEffectDef = decreaseBlockEffect; + phoenixShot.NumberOfHitsPerAttack = 4; + } + } + + private MagicEffectDefinition CreateDecreaseBlockMagicEffect(IContext context, GameConfiguration gameConfiguration) + { + var magicEffect = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.DecreaseBlock; + magicEffect.Name = "Decrease Block Effect (Phoenix Shot)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = false; + magicEffect.StopByDeath = true; + magicEffect.Duration = context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = 10; // 10 Seconds + magicEffect.Chance = context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% + + var decDefRatePowerUpDefinition = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(decDefRatePowerUpDefinition); + decDefRatePowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(gameConfiguration); + decDefRatePowerUpDefinition.Boost = context.CreateNew(); + decDefRatePowerUpDefinition.Boost.ConstantValue.Value = 0.5f; // 50% decrease + decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + var decDefRatePowerUpDefinitionPvp = context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(decDefRatePowerUpDefinitionPvp); + decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvp.GetPersistent(gameConfiguration); + decDefRatePowerUpDefinitionPvp.Boost = context.CreateNew(); + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.8f; // 20% decrease + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + return magicEffect; + } + + private MagicEffectDefinition CreateDefenseReductionBeastUppercutMagicEffect(IContext context, GameConfiguration gameConfiguration) + { + var magicEffect = context.CreateNew(); + gameConfiguration.MagicEffects.Add(magicEffect); + magicEffect.Number = (short)MagicEffectNumber.DefenseReduction; // We will map skill to effect by hand in this update, so we use this number instead of DefenseReductionBeastUppercut + magicEffect.Name = "Defense Reduction Effect (Beast Uppercut)"; + magicEffect.InformObservers = true; + magicEffect.SendDuration = true; + magicEffect.StopByDeath = true; + magicEffect.Duration = context.CreateNew(); + magicEffect.Duration.ConstantValue.Value = (float)TimeSpan.FromSeconds(10).TotalSeconds; + magicEffect.Chance = context.CreateNew(); + magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% + + var reducePvmDefenseEffect = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(reducePvmDefenseEffect); + reducePvmDefenseEffect.TargetAttribute = Stats.DefensePvm.GetPersistent(gameConfiguration); + reducePvmDefenseEffect.Boost = context.CreateNew(); + reducePvmDefenseEffect.Boost.ConstantValue.Value = 0.9f; + reducePvmDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + var reducePvpDefenseEffect = context.CreateNew(); + magicEffect.PowerUpDefinitionsPvp.Add(reducePvpDefenseEffect); + reducePvpDefenseEffect.TargetAttribute = Stats.DefensePvp.GetPersistent(gameConfiguration); + reducePvpDefenseEffect.Boost = context.CreateNew(); + reducePvpDefenseEffect.Boost.ConstantValue.Value = 0.9f; + reducePvpDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + + return magicEffect; + } +} \ No newline at end of file diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 02468a338..acd130df6 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -339,4 +339,9 @@ public enum UpdateVersion /// The version of the . /// AddSummonerBuffSkills = 66, + + /// + /// The version of the . + /// + FixRageFighterMultipleHitSkills = 67, } \ No newline at end of file diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index c98ef5ba3..a64bb1637 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -383,14 +383,14 @@ public override void Initialize() this.CreateSkill(SkillNumber.PetDurabilityStr, "Pet Durability Str", CharacterClasses.LordEmperor, damage: 17, skillType: SkillType.PassiveBoost); // Fist Master (Rage Fighter): - this.CreateSkill(SkillNumber.KillingBlowStrengthener, "Killing Blow Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 2, manaConsumption: 10, elementalModifier: ElementalType.Earth); - this.CreateSkill(SkillNumber.BeastUppercutStrengthener, "Beast Uppercut Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 2, manaConsumption: 10, elementalModifier: ElementalType.Fire); - this.CreateSkill(SkillNumber.KillingBlowMastery, "Killing Blow Mastery", CharacterClasses.FistMaster, DamageType.Physical, 1, 2, manaConsumption: 10, elementalModifier: ElementalType.Earth); - this.CreateSkill(SkillNumber.BeastUppercutMastery, "Beast Uppercut Mastery", CharacterClasses.FistMaster, DamageType.Physical, 1, 2, manaConsumption: 10, elementalModifier: ElementalType.Fire); + this.CreateSkill(SkillNumber.KillingBlowStrengthener, "Killing Blow Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 2, manaConsumption: 10, elementalModifier: ElementalType.Earth, hitsPerAttack: 4); + this.CreateSkill(SkillNumber.BeastUppercutStrengthener, "Beast Uppercut Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 2, manaConsumption: 10, elementalModifier: ElementalType.Fire, hitsPerAttack: 2); + this.CreateSkill(SkillNumber.KillingBlowMastery, "Killing Blow Mastery", CharacterClasses.FistMaster, DamageType.Physical, 1, 2, manaConsumption: 10, elementalModifier: ElementalType.Earth, hitsPerAttack: 4); + this.CreateSkill(SkillNumber.BeastUppercutMastery, "Beast Uppercut Mastery", CharacterClasses.FistMaster, DamageType.Physical, 1, 2, manaConsumption: 10, elementalModifier: ElementalType.Fire, hitsPerAttack: 2); this.CreateSkill(SkillNumber.WeaponMasteryFistMaster, "Weapon Mastery", CharacterClasses.FistMaster, damage: 22, skillType: SkillType.PassiveBoost); - this.CreateSkill(SkillNumber.ChainDriveStrengthener, "Chain Drive Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 4, 22, 22, 150, elementalModifier: ElementalType.Ice); + this.CreateSkill(SkillNumber.ChainDriveStrengthener, "Chain Drive Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 4, 22, 22, 150, elementalModifier: ElementalType.Ice, hitsPerAttack: 4); this.CreateSkill(SkillNumber.DarkSideStrengthener, "Dark Side Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 4, manaConsumption: 84, levelRequirement: 180, elementalModifier: ElementalType.Wind); - this.CreateSkill(SkillNumber.DragonRoarStrengthener, "Dragon Roar Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 3, 33, 60, 150, elementalModifier: ElementalType.Earth); + this.CreateSkill(SkillNumber.DragonRoarStrengthener, "Dragon Roar Strengthener", CharacterClasses.FistMaster, DamageType.Physical, 22, 3, 33, 60, 150, elementalModifier: ElementalType.Earth, skillType: SkillType.AreaSkillExplicitTarget, hitsPerAttack: 4); this.CreateSkill(SkillNumber.EquippedWeaponStrengthener, "Equipped Weapon Strengthener", CharacterClasses.FistMaster, damage: 22, skillType: SkillType.PassiveBoost); this.CreateSkill(SkillNumber.DefSuccessRateIncPowUp, "Def SuccessRate IncPowUp", CharacterClasses.FistMaster, DamageType.Physical, 22, 7, 11, 55, 50, 30); this.CreateSkill(SkillNumber.EquippedWeaponMastery, "Equipped Weapon Mastery", CharacterClasses.FistMaster, damage: 1, skillType: SkillType.PassiveBoost); From 89f9d874b987e70ec8f1b923f5ddc76ebab3a3cf Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 8 Jan 2026 18:14:38 +0000 Subject: [PATCH 04/13] Fixed master skills for killing blow and beast uppercut --- src/GameLogic/AttackableExtensions.cs | 27 ++++++-- .../Attributes/MonsterAttributeHolder.cs | 8 ++- src/GameLogic/Attributes/Stats.cs | 21 +++--- src/GameLogic/Player.cs | 4 +- .../CharacterClasses/ClassSummoner.cs | 3 +- .../Skills/DecreaseBlockEffectInitializer.cs | 10 +-- ...ReductionBeastUppercutEffectInitializer.cs | 19 ++---- .../DefenseReductionEffectInitializer.cs | 19 ++---- .../Skills/InnovationEffectInitializer.cs | 6 +- .../Updates/AddSummonerBuffSkillsPlugIn.cs | 6 +- .../FixRageFighterMultipleHitSkillsPlugIn.cs | 68 +++++++++++++------ .../VersionSeasonSix/SkillsInitializer.cs | 4 +- 12 files changed, 111 insertions(+), 84 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 63684c730..9fb9bc91f 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -64,7 +64,7 @@ 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]); + defense -= (int)(defense * defender.Attributes[Stats.DefenseDecrement]); if (defense < 0) { defense = 0; @@ -624,14 +624,27 @@ private static bool Overrates(this IAttackable defender, IAttacker attacker) return defender.Attributes[Stats.DefenseRatePvm] > attacker.Attributes[Stats.AttackRatePvm]; } - private static int GetDamage(this SkillEntry skill) + private static int GetDamage(this SkillEntry skillEntry, IAttacker attacker) { - skill.ThrowNotInitializedProperty(skill.Skill is null, nameof(skill.Skill)); + skillEntry.ThrowNotInitializedProperty(skillEntry.Skill is null, nameof(skillEntry.Skill)); + var skill = skillEntry.Skill; - var result = skill.Skill.AttackDamage; - if (skill.Skill.MasterDefinition != null) + var result = skill.AttackDamage; + if (attacker is Player { } player && skill.MasterDefinition is { } masterDefinition) { - result += (int)skill.CalculateValue(); + if (masterDefinition.TargetAttribute is null) + { + result += (int)skillEntry.CalculateValue(); + } + + foreach (var masterSkill in skill.GetBaseSkills(true)) + { + if (masterSkill.MasterDefinition!.TargetAttribute is null + && player.SkillList!.GetSkill((ushort)masterSkill.Number) is { } masterSkillEntry) + { + result += (int)masterSkillEntry.CalculateValue(); + } + } } return result; @@ -684,7 +697,7 @@ private static void GetSkillDmg(this IAttacker attacker, SkillEntry? skillEntry, } damageType = skill.DamageType; - var skillDamage = skillEntry.GetDamage(); + var skillDamage = skillEntry.GetDamage(attacker); skillMinimumDamage += skillDamage; skillMaximumDamage += skillDamage + (skillDamage / 2); diff --git a/src/GameLogic/Attributes/MonsterAttributeHolder.cs b/src/GameLogic/Attributes/MonsterAttributeHolder.cs index 7eb2ad332..d34420011 100644 --- a/src/GameLogic/Attributes/MonsterAttributeHolder.cs +++ b/src/GameLogic/Attributes/MonsterAttributeHolder.cs @@ -104,9 +104,11 @@ public void AddElement(IElement element, AttributeDefinition targetAttribute) { attribute = new ComposableAttribute(targetAttribute); var attrValue = this.GetValueOfAttribute(targetAttribute); - var nullValue = element.AggregateType == AggregateType.Multiplicate ? 1 : 0; - attrValue = Math.Abs(attrValue) < 0.01f ? nullValue : attrValue; - attribute.AddElement(new SimpleElement { Value = attrValue }); + if (Math.Abs(attrValue) > 0.01f) + { + attribute.AddElement(new SimpleElement { Value = attrValue }); + } + attributeDictionary.Add(targetAttribute, attribute); } diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 0d2e0bded..3df941e5c 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -737,7 +737,7 @@ public class Stats /// values include: /// . /// values include: - /// Greater defense buff; MST bonus defense with shield (shield strengthener); MST dark horse strengthener; Jack O'Lantern Cry bonus (halved). + /// Greater defense buff; MST bonus defense with shield (shield strengthener); MST dark horse strengthener; Jack O'Lantern Cry bonus (halved); Berserker defense reduction. /// public static AttributeDefinition DefenseFinal { get; } = new(new Guid("0888AD48-0CC8-47CA-B6A3-99F3771AA5FC"), "Final Defense", string.Empty); @@ -747,10 +747,6 @@ public class Stats /// /// values include: /// . - /// values include: - /// Fire slash defense reduction. - /// values include: - /// Berserker defense reduction. /// public static AttributeDefinition DefensePvm { get; } = new(new Guid("B4201610-2824-4EC1-A145-76B15DB9DEC6"), "Defense (PvM)", string.Empty); @@ -760,10 +756,6 @@ public class Stats /// /// values include: /// ; pants guardian option (halved). - /// values include: - /// Fire slash defense reduction. - /// values include: - /// Berserker defense reduction. /// public static AttributeDefinition DefensePvp { get; } = new(new Guid("28D14EB7-1049-45BE-A7B7-D5E28E63943B"), "Defense (PvP)", string.Empty); @@ -781,7 +773,7 @@ public class Stats /// values include: /// Complete set bonus multiplier (+10%); excellent DR option; socket DR option; MST PvM defense rate increase. /// values include: - /// MST bonus defense rate with shield (shield mastery). + /// MST bonus defense rate with shield (shield mastery); Phoenix Shot decrease block effect. /// public static AttributeDefinition DefenseRatePvm { get; } = new(new Guid("C520DD2D-1B06-4392-95EE-3C41F33E68DA"), "Defense Rate (PvM)", string.Empty); @@ -921,9 +913,14 @@ public class Stats 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. + /// Gets the defense decrement 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."); + /// + /// Bucket attribute that collects the multipliers of the magic effects of innovation (Sum), beast uppercut (RF) and fire slash (MG). + /// Beast uppercut and fire slash share the same magic effect number, while innovation's is different. + /// This means that the first two effects can only exist 1 at a time, while innovation can coexist with either of them. + /// + public static AttributeDefinition DefenseDecrement { get; } = new(new Guid("D8B3B1C9-B409-4A07-8F4D-8F315DCB173A"), "Defense Decrement", "The defense decrement due to magic effects of various skills, 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 33d7b3c8e..60d57a085 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1548,8 +1548,8 @@ IElement AppedMasterSkillPowerUp(SkillEntry masterSkillEntry, PowerUpDefinition return powerUp; } - if (masterSkillDefinition.TargetAttribute is not { } masterSkillTargetAttribute - || masterSkillTargetAttribute == powerUpDef.TargetAttribute) + if (masterSkillDefinition.TargetAttribute is { } masterSkillTargetAttribute + && masterSkillTargetAttribute == powerUpDef.TargetAttribute) { var additionalValue = new SimpleElement(masterSkillEntry.CalculateValue(), masterSkillEntry.Skill.MasterDefinition?.Aggregation ?? powerUp.AggregateType); powerUp = new CombinedElement(powerUp, additionalValue); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs index a52b17d1d..e857fb0bc 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs @@ -127,8 +127,7 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name, result.AttributeCombinations.Add(this.CreateAttributeRelationship(finalBerserkerHealthDecrement, -0.1f, Stats.BerserkerHealthDecrement, InputOperator.Minimum)); result.AttributeCombinations.Add(this.CreateAttributeRelationship(finalBerserkerHealthDecrement, 1, Stats.BerserkerMinPhysDmgBonus, InputOperator.Minimum, AggregateType.Multiplicate)); result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.MaximumHealth, 1, finalBerserkerHealthDecrement, InputOperator.Add, AggregateType.Multiplicate)); - result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefensePvm, statsDefense, finalBerserkerHealthDecrement, AggregateType.AddFinal)); - result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefensePvp, statsDefense, finalBerserkerHealthDecrement, AggregateType.AddFinal)); + result.AttributeCombinations.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, statsDefense, finalBerserkerHealthDecrement, AggregateType.AddFinal)); result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMinPhysDmgBonus, 1, Stats.BerserkerManaMultiplier, aggregateType: AggregateType.Multiplicate)); result.AttributeCombinations.Add(this.CreateAttributeRelationship(Stats.BerserkerMaxPhysDmgBonus, 1, Stats.BerserkerManaMultiplier, aggregateType: AggregateType.Multiplicate)); diff --git a/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs b/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs index 494f1b9af..62c3f19bd 100644 --- a/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DecreaseBlockEffectInitializer.cs @@ -43,14 +43,14 @@ public override void Initialize() magicEffect.PowerUpDefinitions.Add(decDefRatePowerUpDefinition); decDefRatePowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(this.GameConfiguration); decDefRatePowerUpDefinition.Boost = this.Context.CreateNew(); - decDefRatePowerUpDefinition.Boost.ConstantValue.Value = 0.5f; // 50% decrease - decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + decDefRatePowerUpDefinition.Boost.ConstantValue.Value = -50f; + decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddFinal; var decDefRatePowerUpDefinitionPvp = this.Context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefRatePowerUpDefinitionPvp); - decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvp.GetPersistent(this.GameConfiguration); + decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(this.GameConfiguration); decDefRatePowerUpDefinitionPvp.Boost = this.Context.CreateNew(); - decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.8f; // 20% decrease - decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = -20f; + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.AddFinal; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs index 785ef60e6..8e3156f18 100644 --- a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs @@ -39,18 +39,11 @@ public override void Initialize() magicEffect.Chance = this.Context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% - var reducePvmDefenseEffect = this.Context.CreateNew(); - magicEffect.PowerUpDefinitions.Add(reducePvmDefenseEffect); - reducePvmDefenseEffect.TargetAttribute = Stats.DefensePvm.GetPersistent(this.GameConfiguration); - reducePvmDefenseEffect.Boost = this.Context.CreateNew(); - reducePvmDefenseEffect.Boost.ConstantValue.Value = 0.9f; - reducePvmDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; - - var reducePvpDefenseEffect = this.Context.CreateNew(); - magicEffect.PowerUpDefinitionsPvp.Add(reducePvpDefenseEffect); - reducePvpDefenseEffect.TargetAttribute = Stats.DefensePvp.GetPersistent(this.GameConfiguration); - reducePvpDefenseEffect.Boost = this.Context.CreateNew(); - reducePvpDefenseEffect.Boost.ConstantValue.Value = 0.9f; - reducePvpDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + var reduceDefenseEffect = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect); + reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); + reduceDefenseEffect.Boost = this.Context.CreateNew(); + reduceDefenseEffect.Boost.ConstantValue.Value = 0.1f; // 10% decrease + reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs b/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs index e66338a6f..b7c7a5c9a 100644 --- a/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs @@ -37,18 +37,11 @@ public override void Initialize() magicEffect.Duration = this.Context.CreateNew(); magicEffect.Duration.ConstantValue.Value = (float)TimeSpan.FromSeconds(10).TotalSeconds; - var reducePvmDefenseEffect = this.Context.CreateNew(); - magicEffect.PowerUpDefinitions.Add(reducePvmDefenseEffect); - reducePvmDefenseEffect.TargetAttribute = Stats.DefensePvm.GetPersistent(this.GameConfiguration); - reducePvmDefenseEffect.Boost = this.Context.CreateNew(); - reducePvmDefenseEffect.Boost.ConstantValue.Value = 0.9f; - reducePvmDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; - - var reducePvpDefenseEffect = this.Context.CreateNew(); - magicEffect.PowerUpDefinitions.Add(reducePvpDefenseEffect); - reducePvpDefenseEffect.TargetAttribute = Stats.DefensePvp.GetPersistent(this.GameConfiguration); - reducePvpDefenseEffect.Boost = this.Context.CreateNew(); - reducePvpDefenseEffect.Boost.ConstantValue.Value = 0.9f; - reducePvpDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + var reduceDefenseEffect = this.Context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect); + reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); + reduceDefenseEffect.Boost = this.Context.CreateNew(); + reduceDefenseEffect.Boost.ConstantValue.Value = 0.1f; // 10% decrease + reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs index 254a2ec3c..fd21aabb4 100644 --- a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -101,9 +101,10 @@ public override void Initialize() // Defense decrease % (applies last) = 20 + (Energy / 90) var decDefPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); - decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); + decDefPowerUpDefinition.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); decDefPowerUpDefinition.Boost = this.Context.CreateNew(); decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease + decDefPowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; decDefPowerUpDefinition.Boost.MaximumValue = 0.64f; // 64% decrease (based on 4k total energy cap) var decDefPerEnergy = this.Context.CreateNew(); @@ -115,9 +116,10 @@ public override void Initialize() // Defense decrease PvP % (applies last) = 12 + (Energy / 110) var decDefPowerUpDefinitionPvp = this.Context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); - decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); + decDefPowerUpDefinitionPvp.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); decDefPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease + decDefPowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; decDefPowerUpDefinitionPvp.Boost.MaximumValue = 0.48f; // 48% decrease (based on 4k total energy cap) var decDefPerEnergyPvp = this.Context.CreateNew(); diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs index c3b8da1dd..f47e67782 100644 --- a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -51,7 +51,7 @@ public class AddSummonerBuffSkillsPlugIn : UpdatePlugInBase 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); + var innovationDefDecrement = context.CreateNew(Stats.DefenseDecrement.Id, Stats.DefenseDecrement.Designation, Stats.DefenseDecrement.Description); gameConfiguration.Attributes.Add(innovationDefDecrement); var isAsleep = context.CreateNew(Stats.IsAsleep.Id, Stats.IsAsleep.Designation, Stats.IsAsleep.Description); gameConfiguration.Attributes.Add(isAsleep); @@ -195,7 +195,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game // Defense decrease % (applies last) = 20 + (Energy / 90) var decDefPowerUpDefinition = context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); - decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); + decDefPowerUpDefinition.TargetAttribute = Stats.DefenseDecrement.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) @@ -209,7 +209,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game // Defense decrease PvP % (applies last) = 12 + (Energy / 110) var decDefPowerUpDefinitionPvp = context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); - decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); + decDefPowerUpDefinitionPvp.TargetAttribute = Stats.DefenseDecrement.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) diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs index 0daa555e3..748d93be2 100644 --- a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -57,6 +57,31 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio increaseHealthPowerUp.Boost!.MaximumValue = 200f; } + // Update Innvoation magic effect + if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.Innovation) is { } innovationEffect) + { + if (innovationEffect.PowerUpDefinitions.FirstOrDefault(p => p.TargetAttribute == Stats.DefenseDecrement) is { } innovationDefDecrement) + { + innovationDefDecrement.Boost!.ConstantValue.AggregateType = AggregateType.Multiplicate; + } + + if (innovationEffect.PowerUpDefinitionsPvp.FirstOrDefault(p => p.TargetAttribute == Stats.DefenseDecrement) is { } innovationDefDecrementPvp) + { + innovationDefDecrementPvp.Boost!.ConstantValue.AggregateType = AggregateType.Multiplicate; + } + } + + // Update Defense Reduction (Fire Slash) magic effect + if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.DefenseReduction) is { } defenseReductionEffect + && defenseReductionEffect.PowerUpDefinitions.FirstOrDefault() is { } powerUp) + { + powerUp.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + powerUp.Boost!.ConstantValue.Value = 0.1f; // 10% decrease + defenseReductionEffect.PowerUpDefinitions.Clear(); + defenseReductionEffect.PowerUpDefinitions.Add(powerUp); + } + + // Add new magic effects var defensereductionBeastUppercut = this.CreateDefenseReductionBeastUppercutMagicEffect(context, gameConfiguration); var decreaseBlockEffect = this.CreateDecreaseBlockMagicEffect(context, gameConfiguration); @@ -72,7 +97,8 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio killingBlow.NumberOfHitsPerAttack = 4; } - if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowStrengthener) is { } killingBlowStrengthener) + var killingBlowStrengthener = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowStrengthener); + if (killingBlowStrengthener is not null) { killingBlowStrengthener.NumberOfHitsPerAttack = 4; } @@ -80,6 +106,10 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.KillingBlowMastery) is { } killingBlowMastery) { killingBlowMastery.NumberOfHitsPerAttack = 4; + killingBlowMastery.MasterDefinition!.ReplacedSkill = killingBlowStrengthener; + killingBlowMastery.MasterDefinition.ValueFormula = $"{killingBlowMastery.MasterDefinition.ValueFormula} / 100"; + killingBlowMastery.MasterDefinition.TargetAttribute = Stats.WeaknessPhysDmgDecrement.GetPersistent(gameConfiguration); + killingBlowMastery.MasterDefinition.Aggregation = AggregateType.AddRaw; } if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercut) is { } beastUppercut) @@ -88,7 +118,8 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio beastUppercut.NumberOfHitsPerAttack = 2; } - if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercutStrengthener) is { } beastUppercutStrengthener) + var beastUppercutStrengthener = gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.BeastUppercutStrengthener); + if (beastUppercutStrengthener is not null) { beastUppercutStrengthener.MagicEffectDef = defensereductionBeastUppercut; beastUppercutStrengthener.NumberOfHitsPerAttack = 2; @@ -98,6 +129,10 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio { beastUppercutMastery.MagicEffectDef = defensereductionBeastUppercut; beastUppercutMastery.NumberOfHitsPerAttack = 2; + beastUppercutMastery.MasterDefinition!.ReplacedSkill = beastUppercutStrengthener; + beastUppercutMastery.MasterDefinition.ValueFormula = $"{beastUppercutMastery.MasterDefinition.ValueFormula} / 100"; + beastUppercutMastery.MasterDefinition.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + beastUppercutMastery.MasterDefinition.Aggregation = AggregateType.Multiplicate; } if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.ChainDrive) is { } chainDrive) @@ -151,15 +186,15 @@ private MagicEffectDefinition CreateDecreaseBlockMagicEffect(IContext context, G magicEffect.PowerUpDefinitions.Add(decDefRatePowerUpDefinition); decDefRatePowerUpDefinition.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(gameConfiguration); decDefRatePowerUpDefinition.Boost = context.CreateNew(); - decDefRatePowerUpDefinition.Boost.ConstantValue.Value = 0.5f; // 50% decrease - decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + decDefRatePowerUpDefinition.Boost.ConstantValue.Value = -50f; + decDefRatePowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.AddFinal; var decDefRatePowerUpDefinitionPvp = context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefRatePowerUpDefinitionPvp); - decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvp.GetPersistent(gameConfiguration); + decDefRatePowerUpDefinitionPvp.TargetAttribute = Stats.DefenseRatePvm.GetPersistent(gameConfiguration); decDefRatePowerUpDefinitionPvp.Boost = context.CreateNew(); - decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.8f; // 20% decrease - decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.Value = -20f; + decDefRatePowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.AddFinal; return magicEffect; } @@ -178,19 +213,12 @@ private MagicEffectDefinition CreateDefenseReductionBeastUppercutMagicEffect(ICo magicEffect.Chance = context.CreateNew(); magicEffect.Chance.ConstantValue.Value = 0.1f; // 10% - var reducePvmDefenseEffect = context.CreateNew(); - magicEffect.PowerUpDefinitions.Add(reducePvmDefenseEffect); - reducePvmDefenseEffect.TargetAttribute = Stats.DefensePvm.GetPersistent(gameConfiguration); - reducePvmDefenseEffect.Boost = context.CreateNew(); - reducePvmDefenseEffect.Boost.ConstantValue.Value = 0.9f; - reducePvmDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; - - var reducePvpDefenseEffect = context.CreateNew(); - magicEffect.PowerUpDefinitionsPvp.Add(reducePvpDefenseEffect); - reducePvpDefenseEffect.TargetAttribute = Stats.DefensePvp.GetPersistent(gameConfiguration); - reducePvpDefenseEffect.Boost = context.CreateNew(); - reducePvpDefenseEffect.Boost.ConstantValue.Value = 0.9f; - reducePvpDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; + var reduceDefenseEffect = context.CreateNew(); + magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect); + reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + reduceDefenseEffect.Boost = context.CreateNew(); + reduceDefenseEffect.Boost.ConstantValue.Value = 0.1f; // 10% decrease + reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; return magicEffect; } diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index b2635edb3..a7c6c087b 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -894,8 +894,8 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseAttackSuccessRate, Stats.AttackRatePvm, AggregateType.AddRaw, Formula20469, 1, 2); this.AddMasterSkillDefinition(SkillNumber.KillingBlowStrengthener, SkillNumber.Undefined, SkillNumber.Undefined, 2, 2, SkillNumber.KillingBlow, 20, Formula502); this.AddMasterSkillDefinition(SkillNumber.BeastUppercutStrengthener, SkillNumber.Undefined, SkillNumber.Undefined, 2, 2, SkillNumber.BeastUppercut, 20, Formula502); - this.AddMasterSkillDefinition(SkillNumber.KillingBlowMastery, SkillNumber.KillingBlowStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.KillingBlow, 20, Formula120); - this.AddMasterSkillDefinition(SkillNumber.BeastUppercutMastery, SkillNumber.BeastUppercutStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.BeastUppercut, 20, Formula120); + this.AddMasterSkillDefinition(SkillNumber.KillingBlowMastery, SkillNumber.KillingBlowStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.KillingBlowStrengthener, 20, $"{Formula120} / 100", Formula120, Stats.WeaknessPhysDmgDecrement, AggregateType.AddRaw); + this.AddMasterSkillDefinition(SkillNumber.BeastUppercutMastery, SkillNumber.BeastUppercutStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.BeastUppercutStrengthener, 20, $"{Formula120} / 100", Formula120, Stats.DefenseDecrement, AggregateType.Multiplicate); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseMaximumHp, Stats.MaximumHealth, AggregateType.AddRaw, Formula5418, 4, 2); this.AddPassiveMasterSkillDefinition(SkillNumber.WeaponMasteryFistMaster, Stats.MasterSkillPhysBonusDmg, AggregateType.AddRaw, Formula502, 4, 2); this.AddMasterSkillDefinition(SkillNumber.ChainDriveStrengthener, SkillNumber.ChainDrive, SkillNumber.Undefined, 2, 5, SkillNumber.ChainDrive, 20, Formula502); From d22890a1a1c81776499f9b6bea69bc7e2a0f17ff Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 8 Jan 2026 18:39:13 +0000 Subject: [PATCH 05/13] Updated update plugin --- .../FixRageFighterMultipleHitSkillsPlugIn.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs index 748d93be2..813bb2e6d 100644 --- a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -8,6 +8,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using MUnique.OpenMU.AttributeSystem; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.Persistence.Initialization.Skills; using MUnique.OpenMU.PlugIns; @@ -50,6 +51,23 @@ public class FixRageFighterMultipleHitSkillsPlugIn : UpdatePlugInBase /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { + // Update summoner berserker defense reduction attributes + gameConfiguration.CharacterClasses.ForEach(charClass => + { + if (charClass.Number == 20 || charClass.Number == 22 || charClass.Number == 23) // Summoner classes + { + if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvm) is { } finalBerserkerHealthDecrementToDefensePvm) + { + finalBerserkerHealthDecrementToDefensePvm.TargetAttribute = Stats.DefenseFinal.GetPersistent(gameConfiguration); + } + + if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvp) is { } finalBerserkerHealthDecrementToDefensePvp) + { + charClass.AttributeCombinations.Remove(finalBerserkerHealthDecrementToDefensePvp); + } + } + }); + // Update Increase Health (Vitality) magic effect if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.IncreaseHealth) is { } increaseHealthEffect && increaseHealthEffect.PowerUpDefinitions.FirstOrDefault() is { } increaseHealthPowerUp) @@ -57,7 +75,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio increaseHealthPowerUp.Boost!.MaximumValue = 200f; } - // Update Innvoation magic effect + // Update Innovation magic effect if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.Innovation) is { } innovationEffect) { if (innovationEffect.PowerUpDefinitions.FirstOrDefault(p => p.TargetAttribute == Stats.DefenseDecrement) is { } innovationDefDecrement) From b2464fe066d3c62c934d49e633a913cf1957f350 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 9 Jan 2026 17:05:40 +0000 Subject: [PATCH 06/13] Fixed Stats.DefenseDecrease relationships logic --- src/GameLogic/AttackableExtensions.cs | 2 +- .../Attributes/MonsterAttributeHolder.cs | 9 ++- src/GameLogic/Attributes/Stats.cs | 9 ++- .../CharacterClassInitialization.cs | 6 ++ ...ReductionBeastUppercutEffectInitializer.cs | 2 +- .../DefenseReductionEffectInitializer.cs | 2 +- .../Skills/InnovationEffectInitializer.cs | 6 +- .../Updates/AddSummonerBuffSkillsPlugIn.cs | 4 +- .../FixRageFighterMultipleHitSkillsPlugIn.cs | 62 ++++++++++++------- 9 files changed, 63 insertions(+), 39 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index 9fb9bc91f..cc11d3d2f 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -64,7 +64,7 @@ public static async ValueTask CalculateDamageAsync(this IAttacker attac { var defenseAttribute = defender.GetDefenseAttribute(attacker); defense = (int)defender.Attributes[defenseAttribute]; - defense -= (int)(defense * defender.Attributes[Stats.DefenseDecrement]); + defense = (int)(defense * defender.Attributes[Stats.DefenseDecrement]); if (defense < 0) { defense = 0; diff --git a/src/GameLogic/Attributes/MonsterAttributeHolder.cs b/src/GameLogic/Attributes/MonsterAttributeHolder.cs index d34420011..45b88bc2d 100644 --- a/src/GameLogic/Attributes/MonsterAttributeHolder.cs +++ b/src/GameLogic/Attributes/MonsterAttributeHolder.cs @@ -22,6 +22,7 @@ public class MonsterAttributeHolder : IAttributeSystem { Stats.DamageReceiveDecrement, m => 1.0f }, { Stats.AttackDamageIncrease, m => 1.0f }, { Stats.ShieldBypassChance, m => 1.0f }, + { Stats.DefenseDecrement, m => 1.0f - m.Attributes.GetValueOfAttribute(Stats.InnovationDefDecrement) }, }; private static readonly IDictionary> SetterMapping = @@ -104,11 +105,9 @@ public void AddElement(IElement element, AttributeDefinition targetAttribute) { attribute = new ComposableAttribute(targetAttribute); var attrValue = this.GetValueOfAttribute(targetAttribute); - if (Math.Abs(attrValue) > 0.01f) - { - attribute.AddElement(new SimpleElement { Value = attrValue }); - } - + var nullValue = element.AggregateType == AggregateType.Multiplicate ? 1 : 0; + attrValue = Math.Abs(attrValue) < 0.01f ? nullValue : attrValue; + attribute.AddElement(new SimpleElement { Value = attrValue }); attributeDictionary.Add(targetAttribute, attribute); } diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 3df941e5c..a1a541dbc 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -912,15 +912,20 @@ 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 defense decrement attribute definition. /// /// - /// Bucket attribute that collects the multipliers of the magic effects of innovation (Sum), beast uppercut (RF) and fire slash (MG). + /// Includes the multiplier , and the ones from the magic effects of beast uppercut (RF) and fire slash (MG). /// Beast uppercut and fire slash share the same magic effect number, while innovation's is different. /// This means that the first two effects can only exist 1 at a time, while innovation can coexist with either of them. /// - public static AttributeDefinition DefenseDecrement { get; } = new(new Guid("D8B3B1C9-B409-4A07-8F4D-8F315DCB173A"), "Defense Decrement", "The defense decrement due to magic effects of various skills, which is multiplied with the final defense and subtracted from it."); + public static AttributeDefinition DefenseDecrement { get; } = new(new Guid("D19A0E33-5C9A-4B8E-AF12-3C4D5E6F7890"), "Defense Decrement", "The defense decrement due to magic effects of various skills, which is multiplied with the final defense and subtracted from it."); /// /// Gets the 'is shield equipped' attribute definition. diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 480d48a94..1b944968b 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -134,6 +134,11 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseFinal, Stats.IsShieldEquipped, Stats.BonusDefenseWithShield, AggregateType.AddFinal)); attributeRelationships.Add(this.CreateConditionalRelationship(Stats.DefenseRatePvm, Stats.IsShieldEquipped, Stats.BonusDefenseRateWithShield, AggregateType.AddFinal)); + var tempInnovDefDec = this.Context.CreateNew(Guid.NewGuid(), "Temp Innovation defense decrement", string.Empty); + this.GameConfiguration.Attributes.Add(tempInnovDefDec); + attributeRelationships.Add(this.CreateAttributeRelationship(tempInnovDefDec, -1, Stats.InnovationDefDecrement)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseDecrement, 1, tempInnovDefDec, InputOperator.Add, AggregateType.Multiplicate)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.01f, Stats.IsInSafezone)); if (this.UseClassicPvp) { @@ -162,6 +167,7 @@ private void AddCommonBaseAttributeValues(ICollection baseA baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.PhysicalBaseDmgIncrease)); baseAttributeValues.Add(this.CreateConstValueAttribute(-1, Stats.AreTwoWeaponsEquipped)); baseAttributeValues.Add(this.CreateConstValueAttribute(-1, Stats.HasDoubleWield)); + baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.DefenseDecrement)); if (isMaster) { diff --git a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs index 8e3156f18..f74744cc4 100644 --- a/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DefenseReductionBeastUppercutEffectInitializer.cs @@ -43,7 +43,7 @@ public override void Initialize() magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect); reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); reduceDefenseEffect.Boost = this.Context.CreateNew(); - reduceDefenseEffect.Boost.ConstantValue.Value = 0.1f; // 10% decrease + reduceDefenseEffect.Boost.ConstantValue.Value = 0.9f; // 10% decrease reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs b/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs index b7c7a5c9a..52120128a 100644 --- a/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/DefenseReductionEffectInitializer.cs @@ -41,7 +41,7 @@ public override void Initialize() magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect); reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); reduceDefenseEffect.Boost = this.Context.CreateNew(); - reduceDefenseEffect.Boost.ConstantValue.Value = 0.1f; // 10% decrease + reduceDefenseEffect.Boost.ConstantValue.Value = 0.9f; // 10% decrease reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; } } \ No newline at end of file diff --git a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs index fd21aabb4..254a2ec3c 100644 --- a/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs +++ b/src/Persistence/Initialization/Skills/InnovationEffectInitializer.cs @@ -101,10 +101,9 @@ public override void Initialize() // Defense decrease % (applies last) = 20 + (Energy / 90) var decDefPowerUpDefinition = this.Context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); - decDefPowerUpDefinition.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); + decDefPowerUpDefinition.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); decDefPowerUpDefinition.Boost = this.Context.CreateNew(); decDefPowerUpDefinition.Boost.ConstantValue.Value = 0.20f; // 20% decrease - decDefPowerUpDefinition.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; decDefPowerUpDefinition.Boost.MaximumValue = 0.64f; // 64% decrease (based on 4k total energy cap) var decDefPerEnergy = this.Context.CreateNew(); @@ -116,10 +115,9 @@ public override void Initialize() // Defense decrease PvP % (applies last) = 12 + (Energy / 110) var decDefPowerUpDefinitionPvp = this.Context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); - decDefPowerUpDefinitionPvp.TargetAttribute = Stats.DefenseDecrement.GetPersistent(this.GameConfiguration); + decDefPowerUpDefinitionPvp.TargetAttribute = Stats.InnovationDefDecrement.GetPersistent(this.GameConfiguration); decDefPowerUpDefinitionPvp.Boost = this.Context.CreateNew(); decDefPowerUpDefinitionPvp.Boost.ConstantValue.Value = 0.12f; // 12% decrease - decDefPowerUpDefinitionPvp.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; decDefPowerUpDefinitionPvp.Boost.MaximumValue = 0.48f; // 48% decrease (based on 4k total energy cap) var decDefPerEnergyPvp = this.Context.CreateNew(); diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs index f47e67782..476603c03 100644 --- a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -195,7 +195,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game // Defense decrease % (applies last) = 20 + (Energy / 90) var decDefPowerUpDefinition = context.CreateNew(); magicEffect.PowerUpDefinitions.Add(decDefPowerUpDefinition); - decDefPowerUpDefinition.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + 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) @@ -209,7 +209,7 @@ private MagicEffectDefinition CreateInnovationMagicEffect(IContext context, Game // Defense decrease PvP % (applies last) = 12 + (Energy / 110) var decDefPowerUpDefinitionPvp = context.CreateNew(); magicEffect.PowerUpDefinitionsPvp.Add(decDefPowerUpDefinitionPvp); - decDefPowerUpDefinitionPvp.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + 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) diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs index 813bb2e6d..25e5428d6 100644 --- a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -6,6 +6,7 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using System.Runtime.InteropServices; using MUnique.OpenMU.AttributeSystem; +using MUnique.OpenMU.DataModel; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.GameLogic; @@ -46,26 +47,56 @@ public class FixRageFighterMultipleHitSkillsPlugIn : UpdatePlugInBase public override bool IsMandatory => true; /// - public override DateTime CreatedAt => new(2026, 1, 3, 16, 0, 0, DateTimeKind.Utc); + public override DateTime CreatedAt => new(2026, 1, 9, 16, 0, 0, DateTimeKind.Utc); /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { - // Update summoner berserker defense reduction attributes + // Update attributes + var defenseDecrement = context.CreateNew(Stats.DefenseDecrement.Id, Stats.DefenseDecrement.Designation, Stats.DefenseDecrement.Description); + gameConfiguration.Attributes.Add(defenseDecrement); + + var innovationDefDecrement = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); + gameConfiguration.CharacterClasses.ForEach(charClass => { + // Update summoner berserker defense reduction attributes if (charClass.Number == 20 || charClass.Number == 22 || charClass.Number == 23) // Summoner classes { - if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvm) is { } finalBerserkerHealthDecrementToDefensePvm) + if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvm && attr.AggregateType == AggregateType.AddFinal) is { } finalBerserkerHealthDecrementToDefensePvm) { finalBerserkerHealthDecrementToDefensePvm.TargetAttribute = Stats.DefenseFinal.GetPersistent(gameConfiguration); } - if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvp) is { } finalBerserkerHealthDecrementToDefensePvp) + if (charClass.AttributeCombinations.FirstOrDefault(attr => attr.TargetAttribute == Stats.DefensePvp && attr.AggregateType == AggregateType.AddFinal) is { } finalBerserkerHealthDecrementToDefensePvp) { charClass.AttributeCombinations.Remove(finalBerserkerHealthDecrementToDefensePvp); } } + + // Add defense decrement relationships + var tempInnovDefDec = context.CreateNew(Guid.NewGuid(), "Temp Innovation defense decrement", string.Empty); + gameConfiguration.Attributes.Add(tempInnovDefDec); + + var innovationDefDecrementToTempInnovDefDec = context.CreateNew( + tempInnovDefDec, + -1, + innovationDefDecrement, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var tempInnovDefDecToDefenseDecrement = context.CreateNew( + defenseDecrement, + 1, + tempInnovDefDec, + InputOperator.Add, + default(AttributeDefinition?), + AggregateType.Multiplicate); + + charClass.AttributeCombinations.Add(innovationDefDecrementToTempInnovDefDec); + charClass.AttributeCombinations.Add(tempInnovDefDecToDefenseDecrement); + charClass.BaseAttributeValues.Add(context.CreateNew(1, defenseDecrement)); }); // Update Increase Health (Vitality) magic effect @@ -75,26 +106,11 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio increaseHealthPowerUp.Boost!.MaximumValue = 200f; } - // Update Innovation magic effect - if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.Innovation) is { } innovationEffect) - { - if (innovationEffect.PowerUpDefinitions.FirstOrDefault(p => p.TargetAttribute == Stats.DefenseDecrement) is { } innovationDefDecrement) - { - innovationDefDecrement.Boost!.ConstantValue.AggregateType = AggregateType.Multiplicate; - } - - if (innovationEffect.PowerUpDefinitionsPvp.FirstOrDefault(p => p.TargetAttribute == Stats.DefenseDecrement) is { } innovationDefDecrementPvp) - { - innovationDefDecrementPvp.Boost!.ConstantValue.AggregateType = AggregateType.Multiplicate; - } - } - // Update Defense Reduction (Fire Slash) magic effect if (gameConfiguration.MagicEffects.FirstOrDefault(m => m.Number == (short)MagicEffectNumber.DefenseReduction) is { } defenseReductionEffect && defenseReductionEffect.PowerUpDefinitions.FirstOrDefault() is { } powerUp) { - powerUp.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); - powerUp.Boost!.ConstantValue.Value = 0.1f; // 10% decrease + powerUp.TargetAttribute = defenseDecrement; defenseReductionEffect.PowerUpDefinitions.Clear(); defenseReductionEffect.PowerUpDefinitions.Add(powerUp); } @@ -148,8 +164,8 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio beastUppercutMastery.MagicEffectDef = defensereductionBeastUppercut; beastUppercutMastery.NumberOfHitsPerAttack = 2; beastUppercutMastery.MasterDefinition!.ReplacedSkill = beastUppercutStrengthener; - beastUppercutMastery.MasterDefinition.ValueFormula = $"{beastUppercutMastery.MasterDefinition.ValueFormula} / 100"; - beastUppercutMastery.MasterDefinition.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + beastUppercutMastery.MasterDefinition.ValueFormula = $"{beastUppercutMastery.MasterDefinition.ValueFormula} / -100"; + beastUppercutMastery.MasterDefinition.TargetAttribute = defenseDecrement; beastUppercutMastery.MasterDefinition.Aggregation = AggregateType.Multiplicate; } @@ -235,7 +251,7 @@ private MagicEffectDefinition CreateDefenseReductionBeastUppercutMagicEffect(ICo magicEffect.PowerUpDefinitions.Add(reduceDefenseEffect); reduceDefenseEffect.TargetAttribute = Stats.DefenseDecrement.GetPersistent(gameConfiguration); reduceDefenseEffect.Boost = context.CreateNew(); - reduceDefenseEffect.Boost.ConstantValue.Value = 0.1f; // 10% decrease + reduceDefenseEffect.Boost.ConstantValue.Value = 0.9f; // 10% decrease reduceDefenseEffect.Boost.ConstantValue.AggregateType = AggregateType.Multiplicate; return magicEffect; From e41e61670afbe5c440df9f77bfc28ebfcf3756e3 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 12 Jan 2026 10:55:14 +0000 Subject: [PATCH 07/13] Updated master formula --- .../Updates/FixRageFighterMultipleHitSkillsPlugIn.cs | 2 +- .../Initialization/VersionSeasonSix/SkillsInitializer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs index 25e5428d6..ab845423b 100644 --- a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -47,7 +47,7 @@ public class FixRageFighterMultipleHitSkillsPlugIn : UpdatePlugInBase public override bool IsMandatory => true; /// - public override DateTime CreatedAt => new(2026, 1, 9, 16, 0, 0, DateTimeKind.Utc); + public override DateTime CreatedAt => new(2026, 1, 12, 16, 0, 0, DateTimeKind.Utc); /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index a7c6c087b..57219fe03 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -895,7 +895,7 @@ private void InitializeMasterSkillData() this.AddMasterSkillDefinition(SkillNumber.KillingBlowStrengthener, SkillNumber.Undefined, SkillNumber.Undefined, 2, 2, SkillNumber.KillingBlow, 20, Formula502); this.AddMasterSkillDefinition(SkillNumber.BeastUppercutStrengthener, SkillNumber.Undefined, SkillNumber.Undefined, 2, 2, SkillNumber.BeastUppercut, 20, Formula502); this.AddMasterSkillDefinition(SkillNumber.KillingBlowMastery, SkillNumber.KillingBlowStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.KillingBlowStrengthener, 20, $"{Formula120} / 100", Formula120, Stats.WeaknessPhysDmgDecrement, AggregateType.AddRaw); - this.AddMasterSkillDefinition(SkillNumber.BeastUppercutMastery, SkillNumber.BeastUppercutStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.BeastUppercutStrengthener, 20, $"{Formula120} / 100", Formula120, Stats.DefenseDecrement, AggregateType.Multiplicate); + this.AddMasterSkillDefinition(SkillNumber.BeastUppercutMastery, SkillNumber.BeastUppercutStrengthener, SkillNumber.Undefined, 2, 3, SkillNumber.BeastUppercutStrengthener, 20, $"{Formula120} / -100", Formula120, Stats.DefenseDecrement, AggregateType.Multiplicate); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseMaximumHp, Stats.MaximumHealth, AggregateType.AddRaw, Formula5418, 4, 2); this.AddPassiveMasterSkillDefinition(SkillNumber.WeaponMasteryFistMaster, Stats.MasterSkillPhysBonusDmg, AggregateType.AddRaw, Formula502, 4, 2); this.AddMasterSkillDefinition(SkillNumber.ChainDriveStrengthener, SkillNumber.ChainDrive, SkillNumber.Undefined, 2, 5, SkillNumber.ChainDrive, 20, Formula502); From bb31fab17970b4536d3eb43998eb5cb4cab863c7 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 12 Jan 2026 14:48:37 +0000 Subject: [PATCH 08/13] Fixed RF weapon skills from appearing in skill list when master skills are available --- src/GameLogic/AttackableExtensions.cs | 3 +-- .../Character/SkillListViewPlugIn.cs | 18 ++++++++++++++++++ .../FixRageFighterMultipleHitSkillsPlugIn.cs | 1 - 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/GameLogic/AttackableExtensions.cs b/src/GameLogic/AttackableExtensions.cs index cc11d3d2f..23bc45453 100644 --- a/src/GameLogic/AttackableExtensions.cs +++ b/src/GameLogic/AttackableExtensions.cs @@ -63,8 +63,7 @@ public static async ValueTask CalculateDamageAsync(this IAttacker attac else { var defenseAttribute = defender.GetDefenseAttribute(attacker); - defense = (int)defender.Attributes[defenseAttribute]; - defense = (int)(defense * defender.Attributes[Stats.DefenseDecrement]); + defense = (int)(defender.Attributes[defenseAttribute] * defender.Attributes[Stats.DefenseDecrement]); if (defense < 0) { defense = 0; diff --git a/src/GameServer/RemoteView/Character/SkillListViewPlugIn.cs b/src/GameServer/RemoteView/Character/SkillListViewPlugIn.cs index 2b01cfafa..df59a08a9 100644 --- a/src/GameServer/RemoteView/Character/SkillListViewPlugIn.cs +++ b/src/GameServer/RemoteView/Character/SkillListViewPlugIn.cs @@ -25,6 +25,12 @@ public class SkillListViewPlugIn : ISkillListViewPlugIn private const short ForceSkillId = 60; private const short ForceWaveSkillId = 66; private const short ForceWaveStrengSkillId = 509; + private const short KillingBlowSkillId = 260; + private const short BeastUppercutSkillId = 261; + private const short KillingBlowStrengSkillId = 551; + private const short BeastUppercutStrengSkillId = 552; + private const short KillingBlowMasterySkillId = 554; + private const short BeastUppercutMasterySkillId = 555; private readonly RemotePlayer _player; @@ -60,6 +66,18 @@ public virtual async ValueTask AddSkillAsync(Skill skill) return; } + if (skill.Number == KillingBlowSkillId + && this.SkillList.Any(s => s?.Number == KillingBlowStrengSkillId || s?.Number == KillingBlowMasterySkillId)) + { + return; + } + + if (skill.Number == BeastUppercutSkillId + && this.SkillList.Any(s => s?.Number == BeastUppercutStrengSkillId || s?.Number == BeastUppercutMasterySkillId)) + { + return; + } + var skillIndex = this.AddSkillToList(skill); await this._player.Connection.SendSkillAddedAsync(skillIndex, (ushort)skill.Number, 0).ConfigureAwait(false); } diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs index ab845423b..b63349fa2 100644 --- a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -6,7 +6,6 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; using System.Runtime.InteropServices; using MUnique.OpenMU.AttributeSystem; -using MUnique.OpenMU.DataModel; using MUnique.OpenMU.DataModel.Attributes; using MUnique.OpenMU.DataModel.Configuration; using MUnique.OpenMU.GameLogic; From 662d29c0f6a0d29a5281fe4ce5e148898bdf1e48 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 12 Jan 2026 15:03:02 +0000 Subject: [PATCH 09/13] revert --- .../Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs index 476603c03..c3b8da1dd 100644 --- a/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/AddSummonerBuffSkillsPlugIn.cs @@ -51,7 +51,7 @@ public class AddSummonerBuffSkillsPlugIn : UpdatePlugInBase protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { // Add new attributes - var innovationDefDecrement = context.CreateNew(Stats.DefenseDecrement.Id, Stats.DefenseDecrement.Designation, Stats.DefenseDecrement.Description); + 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); From 2a6b60cd47707572dca4ea8dd4b0404c6d12c91d Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 26 Jan 2026 12:50:46 +0000 Subject: [PATCH 10/13] Updated skill plugin attributes --- src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs | 5 +++-- src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs b/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs index bc5617c7d..5493e23f4 100644 --- a/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs +++ b/src/GameLogic/PlayerActions/Skills/DragonRoarSkillPlugIn.cs @@ -14,8 +14,9 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; /// /// Handles the dragon roar skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets. /// -[PlugIn(nameof(DragonRoarSkillPlugIn), "Handles the dragon roar skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets.")] -[Guid("a797a6ad-ac92-4731-a0fb-d46d4c1dd0df")] +[PlugIn] +[Display(Name = nameof(PlugInResources.DragonRoarSkillPlugIn_Name), Description = nameof(PlugInResources.DragonRoarSkillPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("A797A6AD-AC92-4731-A0FB-D46D4C1DD0DF")] public class DragonRoarSkillPlugIn : IAreaSkillPlugIn { /// diff --git a/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs b/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs index 4e84a90a8..b26ffe307 100644 --- a/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs +++ b/src/GameLogic/PlayerActions/Skills/PhoenixShotSkillPlugIn.cs @@ -10,8 +10,9 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills; /// /// Handles the phoenix shot weapon skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets. /// -[PlugIn(nameof(PhoenixShotSkillPlugIn), "Handles the phoenix shot weapon skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets.")] -[Guid("2c78e3db-ddc7-4bc6-8539-707f81638abf")] +[PlugIn] +[Display(Name = nameof(PlugInResources.PhoenixShotSkillPlugIn_Name), Description = nameof(PlugInResources.PhoenixShotSkillPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("2C78E3DB-DDC7-4BC6-8539-707F81638ABF")] public class PhoenixShotSkillPlugIn : DragonRoarSkillPlugIn { /// From 63d4be8e3909d07b164fc64648c2b69003ef5e87 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 26 Jan 2026 16:31:49 +0000 Subject: [PATCH 11/13] Updated resources and update plugin --- src/GameLogic/Properties/PlugInResources.resx | 12 ++++++++++++ .../Updates/FixRageFighterMultipleHitSkillsPlugIn.cs | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/GameLogic/Properties/PlugInResources.resx b/src/GameLogic/Properties/PlugInResources.resx index cd9958fe4..4e828642e 100644 --- a/src/GameLogic/Properties/PlugInResources.resx +++ b/src/GameLogic/Properties/PlugInResources.resx @@ -369,6 +369,12 @@ Handles the chain lightning skill of the summoner class. Additionally to the attacked target, it will hit up to two additional targets. + + Dragon Roar Skill + + + Handles the dragon roar skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets. + Drain Life Skill @@ -405,6 +411,12 @@ Handles the stopping of nova skill of the wizard class. + + Phoenix Shot Skill + + + Handles the phoenix shot weapon skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets. + Plasma Storm Skill diff --git a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs index b63349fa2..810284765 100644 --- a/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs +++ b/src/Persistence/Initialization/Updates/FixRageFighterMultipleHitSkillsPlugIn.cs @@ -16,7 +16,8 @@ namespace MUnique.OpenMU.Persistence.Initialization.Updates; /// /// This update adds the missing multiple hits to the Killing Blow, Beast Uppercut, Chain Drive, Dragon Roar and Phoenix Shot Rage Fighter skills, as well as their magic effects. /// -[PlugIn(PlugInName, PlugInDescription)] +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] [Guid("EDDD17F9-BEA5-40F0-A653-8567566C40E7")] public class FixRageFighterMultipleHitSkillsPlugIn : UpdatePlugInBase { From 656167e0c13fd16f3d790f9fe48b2c9166bf4b91 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 26 Jan 2026 16:32:27 +0000 Subject: [PATCH 12/13] packet tests changes --- .../ServerToClientPacketTests.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/MUnique.OpenMU.Network.Packets.Tests/ServerToClientPacketTests.cs b/tests/MUnique.OpenMU.Network.Packets.Tests/ServerToClientPacketTests.cs index e6d0a7642..ea3a5f67d 100644 --- a/tests/MUnique.OpenMU.Network.Packets.Tests/ServerToClientPacketTests.cs +++ b/tests/MUnique.OpenMU.Network.Packets.Tests/ServerToClientPacketTests.cs @@ -1203,6 +1203,14 @@ public void ObjectHit_PacketSizeValidation() Assert.That(7 + 1, Is.LessThanOrEqualTo(expectedLength), "Field 'Kind' exceeds packet boundary"); + // Validate field 'IsRageFighterStreakHit' boundary + Assert.That(7 + 1, Is.LessThanOrEqualTo(expectedLength), + "Field 'IsRageFighterStreakHit' exceeds packet boundary"); + + // Validate field 'IsRageFighterStreakFinalHit' boundary + Assert.That(7 + 1, Is.LessThanOrEqualTo(expectedLength), + "Field 'IsRageFighterStreakFinalHit' exceeds packet boundary"); + // Validate field 'IsDoubleDamage' boundary Assert.That(7 + 1, Is.LessThanOrEqualTo(expectedLength), "Field 'IsDoubleDamage' exceeds packet boundary"); @@ -1233,6 +1241,14 @@ public void ObjectHitExtended_PacketSizeValidation() Assert.That(3 + 1, Is.LessThanOrEqualTo(expectedLength), "Field 'Kind' exceeds packet boundary"); + // Validate field 'IsRageFighterStreakHit' boundary + Assert.That(3 + 1, Is.LessThanOrEqualTo(expectedLength), + "Field 'IsRageFighterStreakHit' exceeds packet boundary"); + + // Validate field 'IsRageFighterStreakFinalHit' boundary + Assert.That(3 + 1, Is.LessThanOrEqualTo(expectedLength), + "Field 'IsRageFighterStreakFinalHit' exceeds packet boundary"); + // Validate field 'IsDoubleDamage' boundary Assert.That(3 + 1, Is.LessThanOrEqualTo(expectedLength), "Field 'IsDoubleDamage' exceeds packet boundary"); From 116691035f722da5bea2f853e9d37b3f8f58d2e2 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 27 Jan 2026 11:07:38 +0000 Subject: [PATCH 13/13] PluginResources .Designer.cs update --- .../Properties/PlugInResources.Designer.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/GameLogic/Properties/PlugInResources.Designer.cs b/src/GameLogic/Properties/PlugInResources.Designer.cs index 4ae51954c..258a76f79 100644 --- a/src/GameLogic/Properties/PlugInResources.Designer.cs +++ b/src/GameLogic/Properties/PlugInResources.Designer.cs @@ -519,6 +519,24 @@ public static string DisconnectChatCommandPlugIn_Name { } } + /// + /// Looks up a localized string similar to Handles the dragon roar skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets.. + /// + public static string DragonRoarSkillPlugIn_Description { + get { + return ResourceManager.GetString("DragonRoarSkillPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dragon Roar Skill. + /// + public static string DragonRoarSkillPlugIn_Name { + get { + return ResourceManager.GetString("DragonRoarSkillPlugIn_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Handles the drain life skill of the summoner class. Additionally to the attacked target, it regains life for damage dealt.. /// @@ -1617,6 +1635,24 @@ public static string PeriodicTaskConfiguration_Timetable_Name { } } + /// + /// Looks up a localized string similar to Handles the phoenix shot weapon skill of the rage fighter class. Additionally to the attacked target, it will hit up to seven additional targets.. + /// + public static string PhoenixShotSkillPlugIn_Description { + get { + return ResourceManager.GetString("PhoenixShotSkillPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Phoenix Shot Skill. + /// + public static string PhoenixShotSkillPlugIn_Name { + get { + return ResourceManager.GetString("PhoenixShotSkillPlugIn_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Handles the chat command '/pk <char> <pk_lvl> <pk_count>'. Sets player kill level and count of a character.. ///