From 81c3443d12889bdb064d1792c149e6c0df642365 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 19 Aug 2026 12:15:11 +0100 Subject: [PATCH 01/15] First commit --- src/AttributeSystem/ConstValueAttribute.cs | 8 +- src/GameLogic/Attributes/Stats.cs | 15 +- src/GameLogic/Player.cs | 11 +- .../UpdateIsInSafezoneAfterPlayerMoved.cs | 4 +- .../MessageHandler/AnimationHandlerPlugIn.cs | 6 + .../ConstValueAttribute.Generated.cs | 4 +- .../EntityFramework/EntityDataContext.cs | 2 +- .../ModelBuilder/AttributeExtensions.cs | 9 - ...nstValueAttributeAggregateType.Designer.cs | 5820 +++++++++++++++++ ...503_AddConstValueAttributeAggregateType.cs | 31 + .../EntityDataContextModelSnapshot.cs | 3 + .../Model/ConstValueAttribute.Generated.cs | 4 +- .../Model/ExtendedTypes.Custom.cs | 7 + .../CharacterClasses/CharacterClassHelper.cs | 5 +- .../CharacterClassInitialization.cs | 32 +- .../CharacterClasses/ClassDarkKnight.cs | 1 + .../CharacterClasses/ClassDarkLord.cs | 19 +- .../CharacterClasses/ClassDarkWizard.cs | 4 +- .../CharacterClasses/ClassFairyElf.cs | 3 +- .../CharacterClasses/ClassMagicGladiator.cs | 5 +- .../CharacterClasses/ClassRageFighter.cs | 19 +- .../CharacterClasses/ClassSummoner.cs | 27 +- .../GameConfigurationInitializerBase.cs | 11 +- .../GameConfigurationInitializer.cs | 4 +- .../VersionSeasonSix/Items/GuardianOptions.cs | 2 +- .../VersionSeasonSix/SkillsInitializer.cs | 2 +- 26 files changed, 5970 insertions(+), 88 deletions(-) create mode 100644 src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.Designer.cs create mode 100644 src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.cs diff --git a/src/AttributeSystem/ConstValueAttribute.cs b/src/AttributeSystem/ConstValueAttribute.cs index 038d864a53..44e41a0e70 100644 --- a/src/AttributeSystem/ConstValueAttribute.cs +++ b/src/AttributeSystem/ConstValueAttribute.cs @@ -16,10 +16,12 @@ public class ConstValueAttribute : IAttribute /// /// The value. /// The definition. - public ConstValueAttribute(float value, AttributeDefinition definition) + /// The aggregate type. + public ConstValueAttribute(float value, AttributeDefinition definition, AggregateType aggregateType = AggregateType.AddRaw) { this.Value = value; this._definition = definition; + this.AggregateType = aggregateType; } /// @@ -57,11 +59,11 @@ public virtual AttributeDefinition Definition public float Value { get; protected set; } /// - public AggregateType AggregateType => AggregateType.AddRaw; + public AggregateType AggregateType { get; protected set; } /// public override string ToString() { - return $"{this.Definition.Designation}: {this.Value}"; + return $"{this.Definition.Designation}: {this.Value} ({this.AggregateType})"; } } \ No newline at end of file diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 3e7c69bfa0..7c7324bf7e 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1437,7 +1437,20 @@ public class Stats /// /// Gets the attribute which defines if the character is located in a safezone of a game map. /// - public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover additional health and shield."); + public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover additional health, shield, mana and ability."); + + /// + /// Gets the attribute which defines if the character is sitting, leaning or hanging. + /// + public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana, if not already in a safezone."); + + /// + /// Gets the attribute which is activated either by or . + /// + public static AttributeDefinition IsRecovering { get; } = new(new Guid("4E8F1A2B-3C5D-6E7F-890A-1B2C3D4E5F6A"), "Flag, if the character is in a safezone or on a resting state", string.Empty) + { + MaximumValue = 1, + }; /// /// Gets the attribute which defines if the character is located on an underwater game map. diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 6d9d6e3a6a..ef8ae90972 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -237,8 +237,7 @@ public CharacterPose Pose set { - var character = this._selectedCharacter; - if (character is null || character.Pose == this.Pose) + if (this._selectedCharacter is not { } character || character.Pose == value) { return; } @@ -1061,15 +1060,15 @@ public async Task RegenerateAsync() if (r.CurrentAttribute == Stats.CurrentShield && !this.IsAtSafezone() && attributes[Stats.ShieldRecoveryEverywhere] < 1) { - // Shield recovery is only possible at safe-zone, except the character has a specific attribute which has the effect that it's recovered everywhere. - // This attribute is usually provided by level 380 armor and a Guardian Option. + // Shield recovery is only possible at safe-zone, except if the character has Stats.ShieldRecoveryEverywhere. + // This attribute is usually provided by a level 380 armor with a Guardian Option. continue; } attributes[r.CurrentAttribute] = Math.Min( attributes[r.CurrentAttribute] + - ((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + - attributes[r.AbsoluteAttribute]), + ((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + + attributes[r.AbsoluteAttribute]), attributes[r.MaximumAttribute]); } diff --git a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs index ca09af0346..1a28456c69 100644 --- a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs +++ b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs @@ -9,7 +9,8 @@ namespace MUnique.OpenMU.GameLogic.PlugIns; using MUnique.OpenMU.PlugIns; /// -/// Updates the . For example, this activates the automatic health and shield recover. +/// Updates the and attributes. +/// For example, these activate the automatic health, shield, mana and ability recovery. /// [PlugIn] [Display(Name = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Name), Description = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Description), ResourceType = typeof(PlugInResources))] @@ -22,6 +23,7 @@ public void AttackableMoved(IAttackable attackable) if (attackable is Player player) { player.Attributes?.SetStatAttribute(Stats.IsInSafezone, attackable.IsAtSafezone() ? 1.0f : 0.0f); + player.Attributes?.SetStatAttribute(Stats.IsResting, 0.0f); } } } \ No newline at end of file diff --git a/src/GameServer/MessageHandler/AnimationHandlerPlugIn.cs b/src/GameServer/MessageHandler/AnimationHandlerPlugIn.cs index 491caf8f81..567d6935dc 100644 --- a/src/GameServer/MessageHandler/AnimationHandlerPlugIn.cs +++ b/src/GameServer/MessageHandler/AnimationHandlerPlugIn.cs @@ -7,6 +7,7 @@ namespace MUnique.OpenMU.GameServer.MessageHandler; using System.Runtime.InteropServices; using MUnique.OpenMU.DataModel.Entities; using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.Views.World; using MUnique.OpenMU.Network.Packets.ClientToServer; using MUnique.OpenMU.PlugIns; @@ -47,6 +48,11 @@ public async ValueTask HandlePacketAsync(Player player, Memory packet) _ => default, }; + if (player.Pose > CharacterPose.Standing) + { + player.Attributes?.SetStatAttribute(Stats.IsResting, 1.0f); + } + await player.ForEachWorldObserverAsync(p => p.ShowAnimationAsync(player, animation, null, rotation), false).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Persistence/BasicModel/ConstValueAttribute.Generated.cs b/src/Persistence/BasicModel/ConstValueAttribute.Generated.cs index 5597321094..872e0c9602 100644 --- a/src/Persistence/BasicModel/ConstValueAttribute.Generated.cs +++ b/src/Persistence/BasicModel/ConstValueAttribute.Generated.cs @@ -21,8 +21,8 @@ public partial class ConstValueAttribute : MUnique.OpenMU.AttributeSystem.ConstV { /// - public ConstValueAttribute(System.Single value, MUnique.OpenMU.AttributeSystem.AttributeDefinition definition) - : base(value, definition) + public ConstValueAttribute(System.Single value, MUnique.OpenMU.AttributeSystem.AttributeDefinition definition, MUnique.OpenMU.AttributeSystem.AggregateType aggregateType) + : base(value, definition, aggregateType) { } diff --git a/src/Persistence/EntityFramework/EntityDataContext.cs b/src/Persistence/EntityFramework/EntityDataContext.cs index 1950668898..e0f8144938 100644 --- a/src/Persistence/EntityFramework/EntityDataContext.cs +++ b/src/Persistence/EntityFramework/EntityDataContext.cs @@ -64,7 +64,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(); modelBuilder.Entity().Apply(); - modelBuilder.Entity().Apply(); + modelBuilder.Entity(); modelBuilder.Entity().Apply(); modelBuilder.Entity().Apply(); modelBuilder.Entity().Apply(); diff --git a/src/Persistence/EntityFramework/Extensions/ModelBuilder/AttributeExtensions.cs b/src/Persistence/EntityFramework/Extensions/ModelBuilder/AttributeExtensions.cs index a6c32d15dd..1e2c88312b 100644 --- a/src/Persistence/EntityFramework/Extensions/ModelBuilder/AttributeExtensions.cs +++ b/src/Persistence/EntityFramework/Extensions/ModelBuilder/AttributeExtensions.cs @@ -12,15 +12,6 @@ namespace MUnique.OpenMU.Persistence.EntityFramework.Extensions.ModelBuilder; /// internal static class AttributeExtensions { - /// - /// Applies the settings for the entity. - /// - /// The builder. - public static void Apply(this EntityTypeBuilder builder) - { - builder.Ignore(c => c.AggregateType); - } - /// /// Applies the settings for the entity. /// diff --git a/src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.Designer.cs b/src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.Designer.cs new file mode 100644 index 0000000000..3e27000668 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.Designer.cs @@ -0,0 +1,5820 @@ +// +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("20260819100503_AddConstValueAttributeAggregateType")] + partial class AddConstValueAttributeAggregateType + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .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("IsBot") + .HasColumnType("boolean"); + + b.Property("IsTemplate") + .HasColumnType("boolean"); + + b.Property("IsVaultExtended") + .HasColumnType("boolean"); + + b.Property("LanguageIsoCode") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("character varying(3)") + .HasDefaultValue("en"); + + 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("EffectRange") + .HasColumnType("integer"); + + 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("MinimumNumberOfHitsPerAttack") + .HasColumnType("integer"); + + b.Property("MinimumNumberOfHitsPerTarget") + .HasColumnType("integer"); + + b.Property("ProjectileCount") + .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("GameConfigurationId") + .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("GameConfigurationId"); + + 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.Buff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MagicEffectDefinitionId") + .HasColumnType("uuid"); + + b.Property("MaximumLevel") + .HasColumnType("integer"); + + b.Property("MinimumLevel") + .HasColumnType("integer"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MagicEffectDefinitionId") + .IsUnique(); + + b.HasIndex("MonsterDefinitionId"); + + b.ToTable("Buff", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttackRespawnAreaId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("CrownHoldTimeSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(30); + + b.Property("DefenseRespawnAreaId") + .HasColumnType("uuid"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("GateBuyPrice") + .HasColumnType("integer"); + + b.Property("GateRepairCostPerHealthPoint") + .HasColumnType("integer"); + + b.Property("GuildScoreCastleSiege") + .HasColumnType("integer"); + + b.Property("GuildScoreCastleSiegeMembers") + .HasColumnType("integer"); + + b.Property("LandOfTrialsMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("MaxAttackingGuilds") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(3); + + b.Property("ParticipantRewardMinSeconds") + .HasColumnType("integer"); + + b.Property("RegisterMinLevel") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(200); + + b.Property("RegisterMinMembers") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(20); + + b.Property("RepairCostPerUpgradeLevel") + .HasColumnType("integer"); + + b.Property("RewardItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemLevel") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)3); + + b.Property("StatueBuyPrice") + .HasColumnType("integer"); + + b.Property("StatueRepairCostPerHealthPoint") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AttackRespawnAreaId") + .IsUnique(); + + b.HasIndex("CastleSiegeMapDefinitionId"); + + b.HasIndex("DefenseRespawnAreaId") + .IsUnique(); + + b.HasIndex("LandOfTrialsMapDefinitionId"); + + b.HasIndex("RewardItemDefinitionId"); + + b.HasIndex("SignOfLordItemDefinitionId"); + + b.ToTable("CastleSiegeConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsHuntZoneEnabled") + .HasColumnType("boolean"); + + b.Property("IsOccupied") + .HasColumnType("boolean"); + + b.Property("OwnerGuildId") + .HasColumnType("uuid"); + + b.Property("TaxChaos") + .HasColumnType("smallint"); + + b.Property("TaxHunt") + .HasColumnType("integer"); + + b.Property("TaxStore") + .HasColumnType("smallint"); + + b.Property("TributeMoney") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("OwnerGuildId"); + + b.ToTable("CastleSiegeData", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeGuildRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("GuildName") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Marks") + .HasColumnType("integer"); + + b.Property("RegistrationOrder") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.ToTable("CastleSiegeGuildRegistration", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("DefaultSide") + .HasColumnType("smallint"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("InstanceId") + .HasColumnType("smallint"); + + b.Property("IsPersistedToDatabase") + .HasColumnType("boolean"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("SpawnX") + .HasColumnType("smallint"); + + b.Property("SpawnY") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.HasIndex("MonsterDefinitionId", "InstanceId"); + + b.ToTable("CastleSiegeNpcDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeDataId") + .HasColumnType("uuid"); + + b.Property("CurrentHp") + .HasColumnType("integer"); + + b.Property("DefenseLevel") + .HasColumnType("smallint"); + + b.Property("InstanceId") + .HasColumnType("smallint"); + + b.Property("LifeLevel") + .HasColumnType("smallint"); + + b.Property("MonsterNumber") + .HasColumnType("smallint"); + + b.Property("RegenLevel") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeDataId"); + + b.HasIndex("MonsterNumber", "InstanceId") + .IsUnique(); + + b.ToTable("CastleSiegeNpcState", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeStateScheduleEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("DayOfWeek") + .HasColumnType("integer"); + + b.Property("Hour") + .HasColumnType("smallint"); + + b.Property("Minute") + .HasColumnType("smallint"); + + b.Property("State") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.ToTable("CastleSiegeStateScheduleEntry", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeUpgradeDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId1") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId2") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId3") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId4") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("RequiredJewelOfGuardianCount") + .HasColumnType("integer"); + + b.Property("RequiredZen") + .HasColumnType("integer"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.HasIndex("CastleSiegeConfigurationId1"); + + b.HasIndex("CastleSiegeConfigurationId2"); + + b.HasIndex("CastleSiegeConfigurationId3"); + + b.HasIndex("CastleSiegeConfigurationId4"); + + b.ToTable("CastleSiegeUpgradeDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId1") + .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("CastleSiegeConfigurationId"); + + b.HasIndex("CastleSiegeConfigurationId1"); + + b.ToTable("CastleSiegeZoneDefinition", "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") + .IsRequired() + .HasColumnType("text"); + + b.Property("InstalledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .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("AggregateType") + .HasColumnType("integer"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("DefinitionId"); + + b.HasIndex("GameConfigurationId"); + + 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("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + 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("ExcellentItemDropLevelDelta") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)25); + + 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("MasterExperienceRate") + .HasColumnType("real"); + + 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("CastleSiegeConfigurationId") + .IsUnique(); + + 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("IsQuestItem") + .HasColumnType("boolean"); + + b.Property("ItemSlotId") + .HasColumnType("uuid"); + + b.Property("MaximumDropLevel") + .HasColumnType("smallint"); + + 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") + .HasJsonPropertyName("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") + .IsRequired() + .HasColumnType("text"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("Message") + .IsRequired() + .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") + .IsRequired() + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("interval"); + + b.Property("Message") + .IsRequired() + .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("SkipElementalModifier") + .HasColumnType("boolean"); + + 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.Property("TimeZoneId") + .HasColumnType("text"); + + 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.GameConfiguration", null) + .WithMany("RawGlobalAttributeCombinations") + .HasForeignKey("GameConfigurationId") + .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.Buff", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "RawMagicEffectDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Buff", "MagicEffectDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawBuffs") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawMagicEffectDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", "RawAttackRespawnArea") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", "AttackRespawnAreaId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawCastleSiegeMapDefinition") + .WithMany() + .HasForeignKey("CastleSiegeMapDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", "RawDefenseRespawnArea") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", "DefenseRespawnAreaId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawLandOfTrialsMapDefinition") + .WithMany() + .HasForeignKey("LandOfTrialsMapDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawRewardItemDefinition") + .WithMany() + .HasForeignKey("RewardItemDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawSignOfLordItemDefinition") + .WithMany() + .HasForeignKey("SignOfLordItemDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RawAttackRespawnArea"); + + b.Navigation("RawCastleSiegeMapDefinition"); + + b.Navigation("RawDefenseRespawnArea"); + + b.Navigation("RawLandOfTrialsMapDefinition"); + + b.Navigation("RawRewardItemDefinition"); + + b.Navigation("RawSignOfLordItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany() + .HasForeignKey("OwnerGuildId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeGuildRegistration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany() + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawNpcDefinitions") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonsterDefinition") + .WithMany() + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("RawMonsterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcState", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", null) + .WithMany("RawNpcStates") + .HasForeignKey("CastleSiegeDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeStateScheduleEntry", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStateSchedule") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeUpgradeDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawGateDefenseUpgrades") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawGateLifeUpgrades") + .HasForeignKey("CastleSiegeConfigurationId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~1"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStatueDefenseUpgrades") + .HasForeignKey("CastleSiegeConfigurationId2") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~2"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStatueLifeUpgrades") + .HasForeignKey("CastleSiegeConfigurationId3") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~3"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStatueRegenUpgrades") + .HasForeignKey("CastleSiegeConfigurationId4") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~4"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawAttackMachineZones") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawDefenseMachineZones") + .HasForeignKey("CastleSiegeConfigurationId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeZoneDefinition_CastleSiegeConfiguration_CastleS~1"); + }); + + 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); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "GameConfiguration") + .WithMany("RawGlobalBaseAttributeValues") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CharacterClass"); + + b.Navigation("GameConfiguration"); + + 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.CastleSiegeConfiguration", "RawCastleSiegeConfiguration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", "RawDuelConfiguration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "DuelConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawCastleSiegeConfiguration"); + + 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") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", "RawMiniGame") + .WithMany() + .HasForeignKey("MiniGameId") + .OnDelete(DeleteBehavior.Cascade); + + 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.CastleSiegeConfiguration", b => + { + b.Navigation("RawAttackMachineZones"); + + b.Navigation("RawDefenseMachineZones"); + + b.Navigation("RawGateDefenseUpgrades"); + + b.Navigation("RawGateLifeUpgrades"); + + b.Navigation("RawNpcDefinitions"); + + b.Navigation("RawStateSchedule"); + + b.Navigation("RawStatueDefenseUpgrades"); + + b.Navigation("RawStatueLifeUpgrades"); + + b.Navigation("RawStatueRegenUpgrades"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => + { + b.Navigation("RawNpcStates"); + }); + + 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("RawGlobalAttributeCombinations"); + + b.Navigation("RawGlobalBaseAttributeValues"); + + 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("RawBuffs"); + + 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/20260819100503_AddConstValueAttributeAggregateType.cs b/src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.cs new file mode 100644 index 0000000000..27f4e29ad8 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20260819100503_AddConstValueAttributeAggregateType.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + /// + public partial class AddConstValueAttributeAggregateType : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "AggregateType", + schema: "config", + table: "ConstValueAttribute", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "AggregateType", + schema: "config", + table: "ConstValueAttribute"); + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs index 5b3b73fb54..8285fcb464 100644 --- a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs +++ b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs @@ -1119,6 +1119,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("uuid"); + b.Property("AggregateType") + .HasColumnType("integer"); + b.Property("CharacterClassId") .HasColumnType("uuid"); diff --git a/src/Persistence/EntityFramework/Model/ConstValueAttribute.Generated.cs b/src/Persistence/EntityFramework/Model/ConstValueAttribute.Generated.cs index 4325178a06..eeb064b245 100644 --- a/src/Persistence/EntityFramework/Model/ConstValueAttribute.Generated.cs +++ b/src/Persistence/EntityFramework/Model/ConstValueAttribute.Generated.cs @@ -23,8 +23,8 @@ internal partial class ConstValueAttribute : MUnique.OpenMU.AttributeSystem.Cons { /// - public ConstValueAttribute(System.Single value, MUnique.OpenMU.AttributeSystem.AttributeDefinition definition) - : base(value, definition) + public ConstValueAttribute(System.Single value, MUnique.OpenMU.AttributeSystem.AttributeDefinition definition, MUnique.OpenMU.AttributeSystem.AggregateType aggregateType) + : base(value, definition, aggregateType) { } diff --git a/src/Persistence/EntityFramework/Model/ExtendedTypes.Custom.cs b/src/Persistence/EntityFramework/Model/ExtendedTypes.Custom.cs index 624270cb25..2d15057ca2 100644 --- a/src/Persistence/EntityFramework/Model/ExtendedTypes.Custom.cs +++ b/src/Persistence/EntityFramework/Model/ExtendedTypes.Custom.cs @@ -98,6 +98,13 @@ public ConstValueAttribute() get => base.Value; set => base.Value = value; } + + /// + public new AggregateType AggregateType + { + get => base.AggregateType; + set => base.AggregateType = value; + } } /// diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassHelper.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassHelper.cs index 839cb0d667..52060fb7e6 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassHelper.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassHelper.cs @@ -200,9 +200,10 @@ public static AttributeRelationship CreateConditionalRelationship(IContext conte /// The game configuration. /// The value. /// The attribute. + /// The aggregate type. /// The constant value attribute. - public static ConstValueAttribute CreateConstValueAttribute(IContext context, GameConfiguration gameConfiguration, float value, AttributeDefinition attribute) + public static ConstValueAttribute CreateConstValueAttribute(IContext context, GameConfiguration gameConfiguration, float value, AttributeDefinition attribute, AggregateType aggregateType = AggregateType.AddRaw) { - return context.CreateNew(value, attribute.GetPersistent(gameConfiguration)); + return context.CreateNew(value, attribute.GetPersistent(gameConfiguration), aggregateType); } } \ No newline at end of file diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index d2caf067f0..684421e2a0 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -84,9 +84,9 @@ private AttributeRelationship CreateConditionalRelationship(AttributeDefinition return CharacterClassHelper.CreateConditionalRelationship(this.Context, this.GameConfiguration, targetAttribute, conditionalAttribute, sourceAttribute, aggregateType); } - private ConstValueAttribute CreateConstValueAttribute(float value, AttributeDefinition attribute) + private ConstValueAttribute CreateConstValueAttribute(float value, AttributeDefinition attribute, AggregateType aggregateType = AggregateType.AddRaw) { - return CharacterClassHelper.CreateConstValueAttribute(this.Context, this.GameConfiguration, value, attribute); + return CharacterClassHelper.CreateConstValueAttribute(this.Context, this.GameConfiguration, value, attribute, aggregateType); } private void AddCommonAttributeRelationships(ICollection attributeRelationships) @@ -122,12 +122,9 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumMana, 1, Stats.SwellLifeManaIncrease, aggregateType: AggregateType.Multiplicate)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IncreaseBlockBonus, 1, Stats.DefenseRatePvm, aggregateType: AggregateType.Multiplicate)); - // If two weapons are equipped (DK, MG, Sum, RF) we subtract the half of the sum of the speeds again from the attack speed - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount)); - var tempSpeed = this.Context.CreateNew(Guid.NewGuid(), "Temp Half weapon attack speed", string.Empty); - this.GameConfiguration.Attributes.Add(tempSpeed); - attributeRelationships.Add(this.CreateAttributeRelationship(tempSpeed, -0.5f, Stats.AttackSpeedByWeapon)); - attributeRelationships.Add(this.CreateConditionalRelationship(Stats.AttackSpeedAny, Stats.AreTwoWeaponsEquipped, tempSpeed)); + // If two weapons are equipped (DK, MG, Sum, RF) we average the weapon attack speed + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount, InputOperator.Maximum)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeedByWeapon, 0.5f, Stats.AreTwoWeaponsEquipped, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate)); var tempDefense = this.Context.CreateNew(Guid.NewGuid(), "Temp Defense Bonus multiplier with Shield", string.Empty); this.GameConfiguration.Attributes.Add(tempDefense); @@ -138,12 +135,13 @@ 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.DefenseDecrement, -1, Stats.InnovationDefDecrement)); + + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsRecovering)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.03f, Stats.IsRecovering)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsRecovering, Stats.IsResting, Stats.IsInSafezone, InputOperator.Maximum)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AbilityRecoveryAbsolute, 3, Stats.IsInSafezone)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.01f, Stats.IsInSafezone)); if (this.UseClassicPvp) { attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 1, Stats.DefenseRatePvm)); @@ -151,7 +149,8 @@ private void AddCommonAttributeRelationships(ICollection } else { - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 0.01f, Stats.IsInSafezone)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 100, Stats.IsInSafezone)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 0.000008f, Stats.IsInSafezone, aggregateType: AggregateType.Multiplicate)); // (0.2 * 3)/(30 * 100 * 25) } attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumGuildSize, 0.1f, Stats.Level)); @@ -181,11 +180,6 @@ private void AddCommonBaseAttributeValues(ICollection baseA baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.MasterPointsPerLevelUp)); baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.MasterExperienceRate)); } - - if (!this.UseClassicPvp) - { - baseAttributeValues.Add(this.CreateConstValueAttribute(0.01f, Stats.ShieldRecoveryMultiplier)); - } } /// diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs index 9a2a72c7ee..b8a0148ce4 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs @@ -43,6 +43,7 @@ protected CharacterClass CreateDarkKnight(CharacterClassNumber number, string na result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 20, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs index 1cdb51defe..326b1c2767 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs @@ -13,13 +13,6 @@ namespace MUnique.OpenMU.Persistence.Initialization.CharacterClasses; /// internal partial class CharacterClassInitialization { - private CharacterClass CreateLordEmperor() - { - var result = this.CreateDarkLord(CharacterClassNumber.LordEmperor, "Lord Emperor", true, null, false); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false)); - return result; - } - private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated) { var energyMinus15 = this.Context.CreateNew(Guid.NewGuid(), "TotalEnergy minus 15", "TotalEnergy minus 15"); @@ -48,6 +41,7 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 40, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); @@ -128,8 +122,10 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, result.BaseAttributeValues.Add(this.CreateConstValueAttribute(38, Stats.MaximumMana)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(48.5f, Stats.MaximumHealth)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.SkillMultiplier)); - result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.AbilityRecoveryMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.PetDurationIncrease)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeed, AggregateType.Maximum)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeedUnderwater, AggregateType.Maximum)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(180, Stats.RavenMinimumDamage)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(200, Stats.RavenMaximumDamage)); @@ -141,4 +137,11 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, return result; } + + private CharacterClass CreateLordEmperor() + { + var result = this.CreateDarkLord(CharacterClassNumber.LordEmperor, "Lord Emperor", true, null, false); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false)); + return result; + } } \ No newline at end of file diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs index 5d6ec2807e..18ea11df8b 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs @@ -42,8 +42,8 @@ protected CharacterClass CreateDarkWizard(CharacterClassNumber number, string na result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentHealth, 60, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 60, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); @@ -111,7 +111,7 @@ protected CharacterClass CreateDarkWizard(CharacterClassNumber number, string na result.BaseAttributeValues.Add(this.CreateConstValueAttribute(30, Stats.MaximumHealth)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SkillMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.WizardryAttackDamageIncrease)); - result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.AbilityRecoveryMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.WizardryBaseDmgIncrease)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.NovaStageDamage)); // placeholder value diff --git a/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs b/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs index b79df65532..a7476b1911 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs @@ -47,6 +47,7 @@ protected CharacterClass CreateFairyElf(CharacterClassNumber number, string name result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.AmmunitionAmount, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); @@ -119,7 +120,7 @@ protected CharacterClass CreateFairyElf(CharacterClassNumber number, string name result.BaseAttributeValues.Add(this.CreateConstValueAttribute(39, Stats.MaximumHealth)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(6, Stats.MaximumMana)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SkillMultiplier)); - result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.AbilityRecoveryMultiplier)); this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs b/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs index 930c9b1f5f..0e867c697c 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs @@ -46,6 +46,7 @@ protected CharacterClass CreateMagicGladiator(CharacterClassNumber number, strin result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentMana, 60, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); @@ -122,9 +123,11 @@ protected CharacterClass CreateMagicGladiator(CharacterClassNumber number, strin result.BaseAttributeValues.Add(this.CreateConstValueAttribute(7, Stats.MaximumMana)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(2, Stats.SkillMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.WizardryAttackDamageIncrease)); - result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.AbilityRecoveryMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.WizardryBaseDmgIncrease)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.IsOneHandedSwordEquipped)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeed, AggregateType.Maximum)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeedUnderwater, AggregateType.Maximum)); this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs b/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs index 0dc2a10b46..f3bf07c38b 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs @@ -13,13 +13,6 @@ namespace MUnique.OpenMU.Persistence.Initialization.CharacterClasses; /// internal partial class CharacterClassInitialization { - private CharacterClass CreateFistMaster() - { - var result = this.CreateRageFighter(CharacterClassNumber.FistMaster, "Fist Master", true, null, false); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false)); - return result; - } - private CharacterClass CreateRageFighter(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated) { var result = this.Context.CreateNew(); @@ -45,6 +38,7 @@ private CharacterClass CreateRageFighter(CharacterClassNumber number, string nam result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); @@ -101,10 +95,19 @@ private CharacterClass CreateRageFighter(CharacterClassNumber number, string nam result.BaseAttributeValues.Add(this.CreateConstValueAttribute(7, Stats.MaximumMana)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.5f, Stats.SkillMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.5f, Stats.VitalitySkillMultiplier)); - result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeed, AggregateType.Maximum)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeedUnderwater, AggregateType.Maximum)); this.AddCommonBaseAttributeValues(result.BaseAttributeValues, isMaster); return result; } + + private CharacterClass CreateFistMaster() + { + var result = this.CreateRageFighter(CharacterClassNumber.FistMaster, "Fist Master", true, null, false); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false)); + return result; + } } \ No newline at end of file diff --git a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs index 6942934780..37cae6a720 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs @@ -13,18 +13,6 @@ namespace MUnique.OpenMU.Persistence.Initialization.CharacterClasses; /// internal partial class CharacterClassInitialization { - private CharacterClass CreateBloodySummoner(CharacterClass dimensionMaster) - { - return this.CreateSummoner(CharacterClassNumber.BloodySummoner, "Bloody Summoner", false, dimensionMaster, false); - } - - private CharacterClass CreateDimensionMaster() - { - var result = this.CreateSummoner(CharacterClassNumber.DimensionMaster, "Dimension Master", true, null, false); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false)); - return result; - } - private CharacterClass CreateSummoner(CharacterClassNumber number, string name, bool isMaster, CharacterClass? nextGenerationClass, bool canGetCreated) { var statsDefense = this.Context.CreateNew(Guid.NewGuid(), "Stats defense", string.Empty); @@ -59,6 +47,7 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name, result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentAbility, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.CurrentShield, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); @@ -150,7 +139,7 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name, result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SkillMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.WizardryAttackDamageIncrease)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f, Stats.CurseAttackDamageIncrease)); - result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 33f, Stats.AbilityRecoveryMultiplier)); + result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0.03f, Stats.AbilityRecoveryMultiplier)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.WizardryBaseDmgIncrease)); result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerManaMultiplier)); // placeholder value result.BaseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.BerserkerHealthDecrement)); // placeholder value @@ -161,4 +150,16 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name, return result; } + + private CharacterClass CreateBloodySummoner(CharacterClass dimensionMaster) + { + return this.CreateSummoner(CharacterClassNumber.BloodySummoner, "Bloody Summoner", false, dimensionMaster, false); + } + + private CharacterClass CreateDimensionMaster() + { + var result = this.CreateSummoner(CharacterClassNumber.DimensionMaster, "Dimension Master", true, null, false); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.MasterLevel, 0, false)); + return result; + } } \ No newline at end of file diff --git a/src/Persistence/Initialization/GameConfigurationInitializerBase.cs b/src/Persistence/Initialization/GameConfigurationInitializerBase.cs index 1d966d76c6..b2d78ef966 100644 --- a/src/Persistence/Initialization/GameConfigurationInitializerBase.cs +++ b/src/Persistence/Initialization/GameConfigurationInitializerBase.cs @@ -1,4 +1,4 @@ -// +// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -272,15 +272,16 @@ private void CreateStatAttributes() /// private void AddGlobalBaseAttributeValues() { - var moneyAmountRate = this.Context.CreateNew(1f, Stats.MoneyAmountRate.GetPersistent(this.GameConfiguration)); + var moneyAmountRate = this.Context.CreateNew(1f, Stats.MoneyAmountRate.GetPersistent(this.GameConfiguration), AggregateType.AddRaw); this.GameConfiguration.GlobalBaseAttributeValues.Add(moneyAmountRate); - var randomExperienceMinMultiplier = this.Context.CreateNew(0.8f, Stats.RandomExperienceMinMultiplier.GetPersistent(this.GameConfiguration)); + var randomExperienceMinMultiplier = this.Context.CreateNew(0.8f, Stats.RandomExperienceMinMultiplier.GetPersistent(this.GameConfiguration), AggregateType.AddRaw); this.GameConfiguration.GlobalBaseAttributeValues.Add(randomExperienceMinMultiplier); - var randomExperienceMaxMultiplier = this.Context.CreateNew(1.2f, Stats.RandomExperienceMaxMultiplier.GetPersistent(this.GameConfiguration)); + var randomExperienceMaxMultiplier = this.Context.CreateNew(1.2f, Stats.RandomExperienceMaxMultiplier.GetPersistent(this.GameConfiguration), AggregateType.AddRaw); this.GameConfiguration.GlobalBaseAttributeValues.Add(randomExperienceMaxMultiplier); - this.GameConfiguration.GlobalBaseAttributeValues.Add(this.Context.CreateNew(1f, Stats.MovementSpeedFactor.GetPersistent(this.GameConfiguration))); + var movementSpeedFactor = this.Context.CreateNew(1f, Stats.MovementSpeedFactor.GetPersistent(this.GameConfiguration), AggregateType.AddRaw); + this.GameConfiguration.GlobalBaseAttributeValues.Add(movementSpeedFactor); } } diff --git a/src/Persistence/Initialization/VersionSeasonSix/GameConfigurationInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/GameConfigurationInitializer.cs index d95ae5595e..d6a480b5a7 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/GameConfigurationInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/GameConfigurationInitializer.cs @@ -1,4 +1,4 @@ -// +// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // @@ -53,7 +53,7 @@ public override void Initialize() this.GameConfiguration.ItemOptions.Add(this.CreateOptionDefinition(Stats.CurseBaseDmg, ItemOptionDefinitionNumbers.CurseAttack)); var maximumAllianceSizeDef = Stats.MaximumAllianceSize.GetPersistent(this.GameConfiguration); - var maximumAllianceSizeValue = this.Context.CreateNew(5f, maximumAllianceSizeDef); + var maximumAllianceSizeValue = this.Context.CreateNew(5f, maximumAllianceSizeDef, AggregateType.AddRaw); this.GameConfiguration.GlobalBaseAttributeValues.Add(maximumAllianceSizeValue); new CharacterClassInitialization(this.Context, this.GameConfiguration).Initialize(); diff --git a/src/Persistence/Initialization/VersionSeasonSix/Items/GuardianOptions.cs b/src/Persistence/Initialization/VersionSeasonSix/Items/GuardianOptions.cs index 0ececa34fb..83482c452b 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Items/GuardianOptions.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Items/GuardianOptions.cs @@ -79,7 +79,7 @@ private void CreateHelmOption() definition.AddsRandomly = false; definition.PossibleOptions.Add(this.CreateOption(ItemGroups.Helm, Stats.DefenseRatePvp, 10, AggregateType.AddRaw, ItemOptionDefinitionNumbers.GuardianOption1)); - definition.PossibleOptions.Add(this.CreateOption(ItemGroups.Helm, Stats.ShieldRecoveryMultiplier, 20, AggregateType.AddRaw, ItemOptionDefinitionNumbers.GuardianOption2)); // 20 absolute, need test + definition.PossibleOptions.Add(this.CreateOption(ItemGroups.Helm, Stats.ShieldRecoveryMultiplier, 20, AggregateType.AddRaw, ItemOptionDefinitionNumbers.GuardianOption2)); } private void CreateGlovesOption() diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index 70c537a203..dee1a97bde 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -919,7 +919,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseManaRecoveryRate, Stats.ManaRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease181, Formula181, 2, 1, SkillNumber.Undefined, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreasePoisonResistance, Stats.PoisonResistance, AggregateType.AddRaw, Formula120Value, Formula120, 2, 1); this.AddPassiveMasterSkillDefinition(SkillNumber.DurabilityReduction2FistMaster, Stats.ItemDurationIncrease, AggregateType.Multiplicate, Formula1204, 3, 1, SkillNumber.DurabilityReduction1FistMaster); - this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseSdRecoveryRate, Stats.ShieldRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseMaximumSd, SkillNumber.Undefined, 20); + this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseSdRecoveryRate, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate, 1 + FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseMaximumSd, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseHpRecoveryRate, Stats.HealthRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseManaRecoveryRate, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseLightningResistance, Stats.LightningResistance, AggregateType.AddRaw, Formula120Value, Formula120, 3, 1, requiredSkill1: SkillNumber.IncreasePoisonResistance); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreasesDefense, Stats.DefenseBase, AggregateType.AddFinal, Formula3371, 4, 1); From 5eaa28a72626f77807d18dda115162dcd5715011 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 19 Aug 2026 12:59:19 +0100 Subject: [PATCH 02/15] Fixed mana value --- .../CharacterClasses/CharacterClassInitialization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 684421e2a0..906f8629a3 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -138,7 +138,7 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseDecrement, -1, Stats.InnovationDefDecrement)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsRecovering)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.03f, Stats.IsRecovering)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.037f, Stats.IsRecovering)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsRecovering, Stats.IsResting, Stats.IsInSafezone, InputOperator.Maximum)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AbilityRecoveryAbsolute, 3, Stats.IsInSafezone)); From 9dd90dec7e50ca49186bc2f59be525b66b998f41 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 20 Aug 2026 16:59:28 +0100 Subject: [PATCH 03/15] Fixed bad initial interpretation --- src/GameLogic/Attributes/Stats.cs | 9 ++------- .../PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs | 2 +- .../CharacterClasses/CharacterClassInitialization.cs | 4 +--- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 7c7324bf7e..1592632fef 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1437,17 +1437,12 @@ public class Stats /// /// Gets the attribute which defines if the character is located in a safezone of a game map. /// - public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover additional health, shield, mana and ability."); + public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover additional shield and ability."); /// /// Gets the attribute which defines if the character is sitting, leaning or hanging. /// - public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana, if not already in a safezone."); - - /// - /// Gets the attribute which is activated either by or . - /// - public static AttributeDefinition IsRecovering { get; } = new(new Guid("4E8F1A2B-3C5D-6E7F-890A-1B2C3D4E5F6A"), "Flag, if the character is in a safezone or on a resting state", string.Empty) + public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health.") { MaximumValue = 1, }; diff --git a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs index 1a28456c69..19b13314a4 100644 --- a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs +++ b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs @@ -10,7 +10,7 @@ namespace MUnique.OpenMU.GameLogic.PlugIns; /// /// Updates the and attributes. -/// For example, these activate the automatic health, shield, mana and ability recovery. +/// For example, these can activate automatic shield or add extra ability and health recovery. /// [PlugIn] [Display(Name = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Name), Description = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Description), ResourceType = typeof(PlugInResources))] diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 906f8629a3..d03965102d 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -137,9 +137,7 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseDecrement, -1, Stats.InnovationDefDecrement)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsRecovering)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.037f, Stats.IsRecovering)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsRecovering, Stats.IsResting, Stats.IsInSafezone, InputOperator.Maximum)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsResting)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AbilityRecoveryAbsolute, 3, Stats.IsInSafezone)); if (this.UseClassicPvp) From 36432c4c6e68c240103f258e6e871a9873048476 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Thu, 20 Aug 2026 17:19:57 +0100 Subject: [PATCH 04/15] Fix previous wrong commit --- src/GameLogic/Attributes/Stats.cs | 2 +- src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs | 2 +- .../CharacterClasses/CharacterClassInitialization.cs | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 1592632fef..8106784c3c 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1442,7 +1442,7 @@ public class Stats /// /// Gets the attribute which defines if the character is sitting, leaning or hanging. /// - public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health.") + public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana.") { MaximumValue = 1, }; diff --git a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs index 19b13314a4..85dd8e39aa 100644 --- a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs +++ b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs @@ -10,7 +10,7 @@ namespace MUnique.OpenMU.GameLogic.PlugIns; /// /// Updates the and attributes. -/// For example, these can activate automatic shield or add extra ability and health recovery. +/// For example, these can activate or increase shield, ability, health and mana recovery. /// [PlugIn] [Display(Name = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Name), Description = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Description), ResourceType = typeof(PlugInResources))] diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index d03965102d..60193a36fb 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -138,6 +138,7 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseDecrement, -1, Stats.InnovationDefDecrement)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsResting)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.03f, Stats.IsResting)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AbilityRecoveryAbsolute, 3, Stats.IsInSafezone)); if (this.UseClassicPvp) From faaaa1ad9873d40052b466e15c039211207d25ac Mon Sep 17 00:00:00 2001 From: ze-dom Date: Fri, 21 Aug 2026 11:47:08 +0100 Subject: [PATCH 05/15] Fixed relationships shield --- src/GameLogic/Player.cs | 4 +--- .../CharacterClasses/CharacterClassInitialization.cs | 11 ++++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index ef8ae90972..33008805dd 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -24,7 +24,6 @@ namespace MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Views.Guild; using MUnique.OpenMU.GameLogic.Views.Inventory; using MUnique.OpenMU.GameLogic.Views.MuHelper; -using MUnique.OpenMU.GameLogic.Views.Pet; using MUnique.OpenMU.GameLogic.Views.Quest; using MUnique.OpenMU.GameLogic.Views.World; using MUnique.OpenMU.Interfaces; @@ -1048,8 +1047,7 @@ public async Task RegenerateAsync() { try { - var attributes = this.Attributes; - if (attributes is null) + if (this.Attributes is not { } attributes) { return; } diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 60193a36fb..770181e209 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -146,11 +146,6 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 1, Stats.DefenseRatePvm)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 1, Stats.AttackRatePvm)); } - else - { - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 100, Stats.IsInSafezone)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 0.000008f, Stats.IsInSafezone, aggregateType: AggregateType.Multiplicate)); // (0.2 * 3)/(30 * 100 * 25) - } attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumGuildSize, 0.1f, Stats.Level)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.CanFly, 1, Stats.IsDinorantEquipped)); @@ -179,6 +174,12 @@ private void AddCommonBaseAttributeValues(ICollection baseA baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.MasterPointsPerLevelUp)); baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.MasterExperienceRate)); } + + if (!this.UseClassicPvp) + { + baseAttributeValues.Add(this.CreateConstValueAttribute(100, Stats.ShieldRecoveryMultiplier)); + baseAttributeValues.Add(this.CreateConstValueAttribute(0.000008f, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate)); // (0.2 * 3)/(30 * 100 * 25) + } } /// From 2529abfa574677333ed310ae4ce2ae1bfc3f3621 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Sat, 22 Aug 2026 14:11:09 +0100 Subject: [PATCH 06/15] Added resting timer --- src/GameLogic/GameContext.cs | 21 +++++- src/GameLogic/Player.cs | 74 ++++++++++++++++--- .../CharacterClassInitialization.cs | 2 +- .../VersionSeasonSix/SkillsInitializer.cs | 4 +- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/GameLogic/GameContext.cs b/src/GameLogic/GameContext.cs index 6bb434b600..ff35e1752c 100644 --- a/src/GameLogic/GameContext.cs +++ b/src/GameLogic/GameContext.cs @@ -9,6 +9,7 @@ namespace MUnique.OpenMU.GameLogic; using System.Diagnostics.Metrics; using System.Globalization; using System.Threading; +using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.MiniGames; using MUnique.OpenMU.GameLogic.PlugIns; using MUnique.OpenMU.GameLogic.Views; @@ -43,6 +44,10 @@ public class GameContext : AsyncDisposable, IGameContext private readonly Timer _recoverTimer; + private readonly int _restingRecoveryInterval; + + private readonly Timer _restingRecoverTimer; + private readonly IMapInitializer _mapInitializer; private readonly AsyncLock _mapInitializerLock = new(); @@ -82,6 +87,8 @@ public GameContext(GameConfiguration configuration, IPersistenceContextProvider this.ItemPowerUpFactory = new ItemPowerUpFactory(loggerFactory.CreateLogger()); this.PartyManager = new PartyManager(configuration.MaximumPartySize, loggerFactory.CreateLogger()); this._recoverTimer = new Timer(this.RecoverTimerElapsed, null, this.Configuration.RecoveryInterval, this.Configuration.RecoveryInterval); + this._restingRecoveryInterval = (int)Math.Round(this.Configuration.RecoveryInterval * (1 + (2f / 3))); // Originally, resting recovery interval is 5s + this._restingRecoverTimer = new Timer(this.RecoverTimerElapsed, true, this._restingRecoveryInterval, this._restingRecoveryInterval); this._tasksTimer = new Timer(this.ExecutePeriodicTasks, null, 1000, 1000); this.FeaturePlugIns = new FeaturePlugInContainer(this.PlugInManager); this._configChangeHandlerRegistration = this.ConfigurationChangeMediator.RegisterObject(this.Configuration, this, this.OnGameConfigurationChangeAsync); @@ -458,6 +465,7 @@ protected override async ValueTask DisposeAsyncCore() { this._configChangeHandlerRegistration.Dispose(); await this._recoverTimer.DisposeAsync().ConfigureAwait(false); + await this._restingRecoverTimer.DisposeAsync().ConfigureAwait(false); await this._tasksTimer.DisposeAsync().ConfigureAwait(false); await base.DisposeAsyncCore().ConfigureAwait(false); } @@ -482,6 +490,7 @@ private async ValueTask OnGameConfigurationChangeAsync(Action unregisterAction, #pragma warning restore CS1998 { this._recoverTimer.Change(gameConfiguration.RecoveryInterval, gameConfiguration.RecoveryInterval); + this._restingRecoverTimer.Change(this._restingRecoveryInterval, this._restingRecoveryInterval); this.ExperienceTable = CreateExpTable(gameConfiguration.ExperienceFormula ?? DefaultExperienceFormula, gameConfiguration.MaximumLevel); this.MasterExperienceTable = CreateExpTable(gameConfiguration.MasterExperienceFormula ?? DefaultMasterExperienceFormula, gameConfiguration.MaximumMasterLevel); } @@ -505,13 +514,23 @@ private async void ExecutePeriodicTasks(object? state) [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Catching all Exceptions.")] private async void RecoverTimerElapsed(object? state) { + var hasRestingTimerElapsed = (bool?)state ?? false; + try { await this.ForEachPlayerAsync(player => { if (player.SelectedCharacter != null && !player.PlayerState.CurrentState.IsDisconnectedOrFinished()) { - return player.RegenerateAsync(); + if (!hasRestingTimerElapsed) + { + return player.RegenerateAsync(); + } + + if (player.Attributes?[Stats.IsResting] > 0) + { + return player.RegenerateAsync(true); + } } return Task.CompletedTask; diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 33008805dd..2bc9bedc03 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -74,6 +74,8 @@ public class Player : AsyncDisposable, IBucketMapObserver, IAttackable, IAttacke private DateTime _lastRegenerate = DateTime.UtcNow; + private DateTime _lastShieldRegenerateStop = DateTime.UtcNow; + private GameMap? _currentMap; private IDisposable? _accountLoggingScope; @@ -1043,7 +1045,12 @@ public async ValueTask ClientReadyAfterMapChangeAsync() /// /// Regenerates the attributes specified in . /// - public async Task RegenerateAsync() + /// If set to true, health and mana will be regenerated exclusively due to the player being in a resting state. + /// + /// All regenerations are subservient to , which has a default value of 3 seconds. + /// Here we apply a compensation multiplier to mimic the original different regen rates, thus maintaining game design and configurabilty. + /// + public async Task RegenerateAsync(bool isRestingCycle = false) { try { @@ -1052,21 +1059,68 @@ public async Task RegenerateAsync() return; } - foreach (var r in Stats.IntervalRegenerationAttributes.Where(r => - attributes[r.RegenerationMultiplier] > 0 || attributes[r.AbsoluteAttribute] > 0)) + var targetRegenerationAttributes = Stats.IntervalRegenerationAttributes.Where(r => + attributes[r.RegenerationMultiplier] > 0 || attributes[r.AbsoluteAttribute] > 0); + if (isRestingCycle) + { + targetRegenerationAttributes = targetRegenerationAttributes.Where(r => + r.CurrentAttribute == Stats.CurrentHealth || r.CurrentAttribute == Stats.CurrentMana); + } + + foreach (var r in targetRegenerationAttributes) { - if (r.CurrentAttribute == Stats.CurrentShield && !this.IsAtSafezone() && - attributes[Stats.ShieldRecoveryEverywhere] < 1) + float multiplier; + if (r.CurrentAttribute == Stats.CurrentShield) { - // Shield recovery is only possible at safe-zone, except if the character has Stats.ShieldRecoveryEverywhere. - // This attribute is usually provided by a level 380 armor with a Guardian Option. - continue; + if ((!this.IsAtSafezone() && attributes[Stats.ShieldRecoveryEverywhere] < 1) + || attributes[r.CurrentAttribute] == attributes[r.MaximumAttribute]) + { + // Shield recovery is only possible at safe-zone, except if the character has Stats.ShieldRecoveryEverywhere. + // This attribute is usually provided by a level 380 armor with a Guardian Option. + this._lastShieldRegenerateStop = DateTime.UtcNow; + continue; + } + + var secondsSinceLastShieldRegenerate = (int)Math.Round(DateTime.UtcNow.Subtract(this._lastShieldRegenerateStop).TotalSeconds); + switch (secondsSinceLastShieldRegenerate) + { + case >= 25: + multiplier = 3; + break; + case >= 15: + multiplier = 2.5f; + break; + case >= 10: + multiplier = 2; + break; + default: + continue; + } + + // Originally shield is recovered every second (after the 10s wait period). + multiplier *= 3; + } + else if (r.CurrentAttribute == Stats.CurrentHealth && !isRestingCycle) + { + if (attributes[Stats.IsResting] > 0) + { + // We only regen health on the quicker resting cycle (for mana both cycles apply). + continue; + } + + // Originally, health is recovered every 7s + multiplier = 3f / 7f; + } + else + { + // Originally, mana and ability are recovered every 3s (the default recovery interval). + multiplier = 1; } attributes[r.CurrentAttribute] = Math.Min( attributes[r.CurrentAttribute] + - ((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + - attributes[r.AbsoluteAttribute]), + (((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + + attributes[r.AbsoluteAttribute]) * multiplier), attributes[r.MaximumAttribute]); } diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 770181e209..774ce1450e 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -178,7 +178,7 @@ private void AddCommonBaseAttributeValues(ICollection baseA if (!this.UseClassicPvp) { baseAttributeValues.Add(this.CreateConstValueAttribute(100, Stats.ShieldRecoveryMultiplier)); - baseAttributeValues.Add(this.CreateConstValueAttribute(0.000008f, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate)); // (0.2 * 3)/(30 * 100 * 25) + baseAttributeValues.Add(this.CreateConstValueAttribute(1f / 75000, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate)); // 1 / (30 * 100 * 25) } } diff --git a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs index dee1a97bde..7ba9bcfb83 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/SkillsInitializer.cs @@ -769,7 +769,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.AutomaticManaRecInc, Stats.ManaRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease181, Formula181, 2, 1, SkillNumber.Undefined, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.PoisonResistanceInc, Stats.PoisonResistance, AggregateType.AddRaw, Formula120, Formula120, 2, 1); this.AddPassiveMasterSkillDefinition(SkillNumber.DurabilityReduction2, Stats.ItemDurationIncrease, AggregateType.Multiplicate, Formula1204, 3, 1, SkillNumber.DurabilityReduction1); - this.AddMasterSkillDefinition(SkillNumber.SdRecoverySpeedInc, SkillNumber.MaximumSDincrease, SkillNumber.Undefined, 1, 3, SkillNumber.Undefined, 20, Formula120); + this.AddPassiveMasterSkillDefinition(SkillNumber.SdRecoverySpeedInc, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate, $"1 + {FormulaRecoveryIncrease120}", Formula120, 3, 1, SkillNumber.MaximumSDincrease, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.AutomaticHpRecInc, Stats.HealthRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.AutomaticManaRecInc, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.LightningResistanceInc, Stats.LightningResistance, AggregateType.AddRaw, Formula120, Formula120, 2, 1, requiredSkill1: SkillNumber.PoisonResistanceInc); this.AddPassiveMasterSkillDefinition(SkillNumber.DefenseIncrease, Stats.DefenseBase, AggregateType.AddFinal, Formula6020, 4, 1); @@ -919,7 +919,7 @@ private void InitializeMasterSkillData() this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseManaRecoveryRate, Stats.ManaRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease181, Formula181, 2, 1, SkillNumber.Undefined, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreasePoisonResistance, Stats.PoisonResistance, AggregateType.AddRaw, Formula120Value, Formula120, 2, 1); this.AddPassiveMasterSkillDefinition(SkillNumber.DurabilityReduction2FistMaster, Stats.ItemDurationIncrease, AggregateType.Multiplicate, Formula1204, 3, 1, SkillNumber.DurabilityReduction1FistMaster); - this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseSdRecoveryRate, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate, 1 + FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseMaximumSd, SkillNumber.Undefined, 20); + this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseSdRecoveryRate, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate, $"1 + {FormulaRecoveryIncrease120}", Formula120, 3, 1, SkillNumber.IncreaseMaximumSd, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseHpRecoveryRate, Stats.HealthRecoveryMultiplier, AggregateType.AddRaw, FormulaRecoveryIncrease120, Formula120, 3, 1, SkillNumber.IncreaseManaRecoveryRate, SkillNumber.Undefined, 20); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreaseLightningResistance, Stats.LightningResistance, AggregateType.AddRaw, Formula120Value, Formula120, 3, 1, requiredSkill1: SkillNumber.IncreasePoisonResistance); this.AddPassiveMasterSkillDefinition(SkillNumber.IncreasesDefense, Stats.DefenseBase, AggregateType.AddFinal, Formula3371, 4, 1); From 2b776c05eb61fc2d4630e1868f481a72a7570d32 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Sat, 22 Aug 2026 16:34:57 +0100 Subject: [PATCH 07/15] Simplified resting recovery interval value calc --- src/GameLogic/Attributes/Stats.cs | 5 +---- src/GameLogic/GameContext.cs | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 8106784c3c..18a2db18eb 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1437,15 +1437,12 @@ public class Stats /// /// Gets the attribute which defines if the character is located in a safezone of a game map. /// - public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover additional shield and ability."); + public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover shield and additional ability."); /// /// Gets the attribute which defines if the character is sitting, leaning or hanging. /// public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana.") - { - MaximumValue = 1, - }; /// /// Gets the attribute which defines if the character is located on an underwater game map. diff --git a/src/GameLogic/GameContext.cs b/src/GameLogic/GameContext.cs index ff35e1752c..932f0504cb 100644 --- a/src/GameLogic/GameContext.cs +++ b/src/GameLogic/GameContext.cs @@ -87,7 +87,7 @@ public GameContext(GameConfiguration configuration, IPersistenceContextProvider this.ItemPowerUpFactory = new ItemPowerUpFactory(loggerFactory.CreateLogger()); this.PartyManager = new PartyManager(configuration.MaximumPartySize, loggerFactory.CreateLogger()); this._recoverTimer = new Timer(this.RecoverTimerElapsed, null, this.Configuration.RecoveryInterval, this.Configuration.RecoveryInterval); - this._restingRecoveryInterval = (int)Math.Round(this.Configuration.RecoveryInterval * (1 + (2f / 3))); // Originally, resting recovery interval is 5s + this._restingRecoveryInterval = (int)Math.Round(this.Configuration.RecoveryInterval * 5f / 3); // Originally, resting recovery interval is 5s this._restingRecoverTimer = new Timer(this.RecoverTimerElapsed, true, this._restingRecoveryInterval, this._restingRecoveryInterval); this._tasksTimer = new Timer(this.ExecutePeriodicTasks, null, 1000, 1000); this.FeaturePlugIns = new FeaturePlugInContainer(this.PlugInManager); From c60d5d18b6fafa3bf9a6ca734490cdd5cac75231 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Sat, 22 Aug 2026 17:30:26 +0100 Subject: [PATCH 08/15] Adjusted indentation and value --- src/GameLogic/Player.cs | 3 +-- .../CharacterClasses/CharacterClassInitialization.cs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 2bc9bedc03..f1292ee842 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1119,8 +1119,7 @@ public async Task RegenerateAsync(bool isRestingCycle = false) attributes[r.CurrentAttribute] = Math.Min( attributes[r.CurrentAttribute] + - (((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + - attributes[r.AbsoluteAttribute]) * multiplier), + (((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + attributes[r.AbsoluteAttribute]) * multiplier), attributes[r.MaximumAttribute]); } diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 774ce1450e..2171385d3c 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -153,7 +153,7 @@ private void AddCommonAttributeRelationships(ICollection private void AddCommonBaseAttributeValues(ICollection baseAttributeValues, bool isMaster) { - baseAttributeValues.Add(this.CreateConstValueAttribute(1.0f / 27.5f, Stats.ManaRecoveryMultiplier)); + baseAttributeValues.Add(this.CreateConstValueAttribute(0.037f, Stats.ManaRecoveryMultiplier)); baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.DamageReceiveDecrement)); baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.AttackDamageIncrease)); baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.ExperienceRate)); From 0ede60a8c73f65773e6f9c504eb65d8ed73533d9 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Mon, 24 Aug 2026 23:49:22 +0100 Subject: [PATCH 09/15] Some refactoring based on Claude's input --- src/GameLogic/Attributes/Stats.cs | 98 ++++++++++++++-- src/GameLogic/GameContext.cs | 21 +--- src/GameLogic/Player.cs | 110 +++++++----------- .../PlugIns/ShieldRecoveryHiatusPlugIn.cs | 73 ++++++++++++ .../UpdateIsInSafezoneAfterPlayerMoved.cs | 2 +- .../Properties/PlugInResources.Designer.cs | 18 +++ src/GameLogic/Properties/PlugInResources.resx | 6 + .../CharacterClassInitialization.cs | 5 + .../CharacterClasses/ClassDarkKnight.cs | 2 + .../CharacterClasses/ClassDarkLord.cs | 2 + .../CharacterClasses/ClassDarkWizard.cs | 2 + .../CharacterClasses/ClassFairyElf.cs | 2 + .../CharacterClasses/ClassMagicGladiator.cs | 2 + .../CharacterClasses/ClassRageFighter.cs | 2 + .../CharacterClasses/ClassSummoner.cs | 2 + 15 files changed, 246 insertions(+), 101 deletions(-) create mode 100644 src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 18a2db18eb..1f4c2991d7 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1173,7 +1173,31 @@ public class Stats /// Gets the shield recovery everywhere attribute definition. /// By default, shield recovery is limited to the safezone only. With this attribute (value >= 1), recovery works everywhere on a map. /// - public static AttributeDefinition ShieldRecoveryEverywhere { get; } = new(new Guid("3D0A78FF-CCD4-442E-8B4E-64E5082ABD78"), "Is Shield Recovery Active Everwhere", "By default, shield recovery is limited to the safezone only. With this attribute (value >= 1), recovery works everywhere on a map."); + public static AttributeDefinition ShieldRecoveryEverywhere { get; } = new(new Guid("3D0A78FF-CCD4-442E-8B4E-64E5082ABD78"), "Shield Recovery Active Everywhere", "By default, shield recovery is limited to the safezone only. With this attribute (value >= 1), recovery works everywhere on a map."); + + /// + /// Gets the is shield recovery active attribute definition. + /// + public static AttributeDefinition IsShieldRecoveryActive { get; } = new(new Guid("8F2C4D7E-B1A9-4E3F-9C5D-2A1B7E8F3C4D"), "Is Shield Recovery Active", string.Empty); + + /// + /// Gets the shield recovery hiatus (in seconds) attribute definition. + /// + /// + /// Must be equal or greater than .HiatusThreshold for shield to regenerate. See . + /// + public static AttributeDefinition ShieldRecoveryHiatus { get; } = new(new Guid("5A7E2B9C-3D1F-4A8E-B6C2-1E7D4A9F2B3C"), "Shield Recovery Hiatus", "The seconds since when shield recovery was last interrupted, either by leaving a safezone, the shield being damaged or maxing out."); + + /// + /// Gets the shield recovery ramp factor attribute definition. + /// + /// + /// The factor by which shield recovery increases. Rises linearly with . Original range is [2,3]. + /// + public static AttributeDefinition ShieldRecoveryRampFactor { get; } = new(new Guid("C3E8F1A7-4B9D-4C5E-8A2F-3D6E1C9B5A7F"), "Shield Recovery Ramp Factor", "The factor by which shield recovery increases. Rises linearly with the uninterrupted shield recovery duration.") + { + MaximumValue = 3, + }; /// /// Gets the ability usage reduction attribute definition. Value ranges from 0 (no reduction) to 1 (full reduction). @@ -1440,9 +1464,9 @@ public class Stats public static AttributeDefinition IsInSafezone { get; } = new(new Guid("82044DF9-F528-4AD6-9AAA-6FEAA4C786E7"), "Flag, if the character is located in a safezone of a game map", "Characters at the safezone recover shield and additional ability."); /// - /// Gets the attribute which defines if the character is sitting, leaning or hanging. + /// Gets the attribute which defines if the character is in a resting state: sitting, leaning or hanging. /// - public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana.") + public static AttributeDefinition IsResting { get; } = new(new Guid("7A4E2D9F-B1C3-48F5-9D2E-1A6F8C3E5B7D"), "Flag, if the character is resting (sitting, leaning or hanging)", "Characters resting recover additional health and mana."); /// /// Gets the attribute which defines if the character is located on an underwater game map. @@ -1593,13 +1617,38 @@ public static IEnumerable AfterMonsterKillRegenerationAttributes } } - private static Regeneration ManaRegeneration { get; } = new(ManaRecoveryMultiplier, MaximumMana, CurrentMana, ManaRecoveryAbsolute); + /// + /// Gets the mana regeneration. + /// + public static Regeneration ManaRegeneration { get; } = new(ManaRecoveryMultiplier, MaximumMana, CurrentMana, ManaRecoveryAbsolute) + { + IntervalResting = TimeSpan.FromSeconds(5), + }; - private static Regeneration HealthRegeneration { get; } = new(HealthRecoveryMultiplier, MaximumHealth, CurrentHealth, HealthRecoveryAbsolute); + /// + /// Gets the health regeneration. + /// + public static Regeneration HealthRegeneration { get; } = new(HealthRecoveryMultiplier, MaximumHealth, CurrentHealth, HealthRecoveryAbsolute) + { + Interval = TimeSpan.FromSeconds(7), + IntervalResting = TimeSpan.FromSeconds(5), + }; - private static Regeneration AbilityRegeneration { get; } = new(AbilityRecoveryMultiplier, MaximumAbility, CurrentAbility, AbilityRecoveryAbsolute); + /// + /// Gets the ability regeneration. + /// + public static Regeneration AbilityRegeneration { get; } = new(AbilityRecoveryMultiplier, MaximumAbility, CurrentAbility, AbilityRecoveryAbsolute); - private static Regeneration ShieldRegeneration { get; } = new(ShieldRecoveryMultiplier, MaximumShield, CurrentShield, ShieldRecoveryAbsolute); + /// + /// Gets the shield regeneration. + /// + public static Regeneration ShieldRegeneration { get; } = new(ShieldRecoveryMultiplier, MaximumShield, CurrentShield, ShieldRecoveryAbsolute) + { + Interval = TimeSpan.FromSeconds(1), + IntervalResting = TimeSpan.FromSeconds(1), + EnablerAttribute = IsShieldRecoveryActive, + HiatusAttribute = ShieldRecoveryHiatus, + }; private static Regeneration ManaRegenerationAfterMonsterKill { get; } = new(ManaAfterMonsterKillMultiplier, MaximumMana, CurrentMana, ManaAfterMonsterKillAbsolute); @@ -1618,8 +1667,8 @@ public class Regeneration { /// /// Initializes a new instance of the class. - /// At regeneration the value of * is getting added to - /// , until the value of is reached. + /// At regeneration the value of ( * ) + + /// is getting added to , until the value of is reached. /// /// The regeneration multiplier. /// The maximum attribute. @@ -1652,5 +1701,36 @@ public Regeneration(AttributeDefinition regenerationMultiplier, AttributeDefinit /// Gets the current attribute. /// public AttributeDefinition CurrentAttribute { get; } + + /// + /// Gets the interval at which the attribute would complete a full regeneration cycle. + /// + /// + /// Originally, the game had different regeneration cycles depending on attributes and the player's state (). + /// To avoid having different timers each with a regeneration interval, we run just one set to . + /// Then, we apply a compensation factor which is the ratio between elapsed time and . + /// This way we can mimic original regeneration over time while still keeping it configurable. + /// + public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(3); + + /// + /// Gets the interval at which the attribute would complete a full regeneration cycle when the player is resting (). + /// + public TimeSpan IntervalResting { get; init; } = TimeSpan.FromSeconds(3); + + /// + /// Gets the attribute which gates the regeneration, if it exists. + /// + public AttributeDefinition? EnablerAttribute { get; init; } + + /// + /// Gets the attribute which keeps the regeneration hiatus duration, if it exists. + /// + public AttributeDefinition? HiatusAttribute { get; init; } + + /// + /// Gets the hiatus threshold (in seconds) against wich is checked to allow the regeneration. + /// + public int HiatusThreshold { get; init; } = 10; } } diff --git a/src/GameLogic/GameContext.cs b/src/GameLogic/GameContext.cs index 932f0504cb..6bb434b600 100644 --- a/src/GameLogic/GameContext.cs +++ b/src/GameLogic/GameContext.cs @@ -9,7 +9,6 @@ namespace MUnique.OpenMU.GameLogic; using System.Diagnostics.Metrics; using System.Globalization; using System.Threading; -using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.MiniGames; using MUnique.OpenMU.GameLogic.PlugIns; using MUnique.OpenMU.GameLogic.Views; @@ -44,10 +43,6 @@ public class GameContext : AsyncDisposable, IGameContext private readonly Timer _recoverTimer; - private readonly int _restingRecoveryInterval; - - private readonly Timer _restingRecoverTimer; - private readonly IMapInitializer _mapInitializer; private readonly AsyncLock _mapInitializerLock = new(); @@ -87,8 +82,6 @@ public GameContext(GameConfiguration configuration, IPersistenceContextProvider this.ItemPowerUpFactory = new ItemPowerUpFactory(loggerFactory.CreateLogger()); this.PartyManager = new PartyManager(configuration.MaximumPartySize, loggerFactory.CreateLogger()); this._recoverTimer = new Timer(this.RecoverTimerElapsed, null, this.Configuration.RecoveryInterval, this.Configuration.RecoveryInterval); - this._restingRecoveryInterval = (int)Math.Round(this.Configuration.RecoveryInterval * 5f / 3); // Originally, resting recovery interval is 5s - this._restingRecoverTimer = new Timer(this.RecoverTimerElapsed, true, this._restingRecoveryInterval, this._restingRecoveryInterval); this._tasksTimer = new Timer(this.ExecutePeriodicTasks, null, 1000, 1000); this.FeaturePlugIns = new FeaturePlugInContainer(this.PlugInManager); this._configChangeHandlerRegistration = this.ConfigurationChangeMediator.RegisterObject(this.Configuration, this, this.OnGameConfigurationChangeAsync); @@ -465,7 +458,6 @@ protected override async ValueTask DisposeAsyncCore() { this._configChangeHandlerRegistration.Dispose(); await this._recoverTimer.DisposeAsync().ConfigureAwait(false); - await this._restingRecoverTimer.DisposeAsync().ConfigureAwait(false); await this._tasksTimer.DisposeAsync().ConfigureAwait(false); await base.DisposeAsyncCore().ConfigureAwait(false); } @@ -490,7 +482,6 @@ private async ValueTask OnGameConfigurationChangeAsync(Action unregisterAction, #pragma warning restore CS1998 { this._recoverTimer.Change(gameConfiguration.RecoveryInterval, gameConfiguration.RecoveryInterval); - this._restingRecoverTimer.Change(this._restingRecoveryInterval, this._restingRecoveryInterval); this.ExperienceTable = CreateExpTable(gameConfiguration.ExperienceFormula ?? DefaultExperienceFormula, gameConfiguration.MaximumLevel); this.MasterExperienceTable = CreateExpTable(gameConfiguration.MasterExperienceFormula ?? DefaultMasterExperienceFormula, gameConfiguration.MaximumMasterLevel); } @@ -514,23 +505,13 @@ private async void ExecutePeriodicTasks(object? state) [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Catching all Exceptions.")] private async void RecoverTimerElapsed(object? state) { - var hasRestingTimerElapsed = (bool?)state ?? false; - try { await this.ForEachPlayerAsync(player => { if (player.SelectedCharacter != null && !player.PlayerState.CurrentState.IsDisconnectedOrFinished()) { - if (!hasRestingTimerElapsed) - { - return player.RegenerateAsync(); - } - - if (player.Attributes?[Stats.IsResting] > 0) - { - return player.RegenerateAsync(true); - } + return player.RegenerateAsync(); } return Task.CompletedTask; diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index f1292ee842..1c5db2a1d4 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -74,7 +74,13 @@ public class Player : AsyncDisposable, IBucketMapObserver, IAttackable, IAttacke private DateTime _lastRegenerate = DateTime.UtcNow; - private DateTime _lastShieldRegenerateStop = DateTime.UtcNow; + private Dictionary _lastRegeneration = new() + { + [Stats.ManaRegeneration] = DateTime.UtcNow, + [Stats.HealthRegeneration] = DateTime.UtcNow, + [Stats.AbilityRegeneration] = DateTime.UtcNow, + [Stats.ShieldRegeneration] = DateTime.UtcNow, + }; private GameMap? _currentMap; @@ -1045,12 +1051,7 @@ public async ValueTask ClientReadyAfterMapChangeAsync() /// /// Regenerates the attributes specified in . /// - /// If set to true, health and mana will be regenerated exclusively due to the player being in a resting state. - /// - /// All regenerations are subservient to , which has a default value of 3 seconds. - /// Here we apply a compensation multiplier to mimic the original different regen rates, thus maintaining game design and configurabilty. - /// - public async Task RegenerateAsync(bool isRestingCycle = false) + public async Task RegenerateAsync() { try { @@ -1059,68 +1060,36 @@ public async Task RegenerateAsync(bool isRestingCycle = false) return; } - var targetRegenerationAttributes = Stats.IntervalRegenerationAttributes.Where(r => - attributes[r.RegenerationMultiplier] > 0 || attributes[r.AbsoluteAttribute] > 0); - if (isRestingCycle) + var now = DateTime.Now; + foreach (var r in Stats.IntervalRegenerationAttributes) { - targetRegenerationAttributes = targetRegenerationAttributes.Where(r => - r.CurrentAttribute == Stats.CurrentHealth || r.CurrentAttribute == Stats.CurrentMana); - } + if ((r.EnablerAttribute is { } enabler && attributes[enabler] < 1) + || (r.HiatusAttribute is { } hiatus && attributes[hiatus] < r.HiatusThreshold)) + { + continue; + } - foreach (var r in targetRegenerationAttributes) - { - float multiplier; - if (r.CurrentAttribute == Stats.CurrentShield) + var factor = 0f; + var interval = r.Interval; + if (attributes[Stats.IsResting] > 0) { - if ((!this.IsAtSafezone() && attributes[Stats.ShieldRecoveryEverywhere] < 1) - || attributes[r.CurrentAttribute] == attributes[r.MaximumAttribute]) - { - // Shield recovery is only possible at safe-zone, except if the character has Stats.ShieldRecoveryEverywhere. - // This attribute is usually provided by a level 380 armor with a Guardian Option. - this._lastShieldRegenerateStop = DateTime.UtcNow; - continue; - } + interval = r.IntervalResting; - var secondsSinceLastShieldRegenerate = (int)Math.Round(DateTime.UtcNow.Subtract(this._lastShieldRegenerateStop).TotalSeconds); - switch (secondsSinceLastShieldRegenerate) + if (r.CurrentAttribute == Stats.CurrentMana) { - case >= 25: - multiplier = 3; - break; - case >= 15: - multiplier = 2.5f; - break; - case >= 10: - multiplier = 2; - break; - default: - continue; + // Mana recovery while resting is on top of regular recovery + factor += (float)((now - this._lastRegeneration[r]) / r.Interval); } - - // Originally shield is recovered every second (after the 10s wait period). - multiplier *= 3; } - else if (r.CurrentAttribute == Stats.CurrentHealth && !isRestingCycle) - { - if (attributes[Stats.IsResting] > 0) - { - // We only regen health on the quicker resting cycle (for mana both cycles apply). - continue; - } - // Originally, health is recovered every 7s - multiplier = 3f / 7f; - } - else - { - // Originally, mana and ability are recovered every 3s (the default recovery interval). - multiplier = 1; - } + factor += (float)((now - this._lastRegeneration[r]) / interval); attributes[r.CurrentAttribute] = Math.Min( attributes[r.CurrentAttribute] + - (((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + attributes[r.AbsoluteAttribute]) * multiplier), + (((attributes[r.MaximumAttribute] * attributes[r.RegenerationMultiplier]) + attributes[r.AbsoluteAttribute]) * factor), attributes[r.MaximumAttribute]); + + this._lastRegeneration[r] = now; } await this.RegenerateHeroStateAsync().ConfigureAwait(false); @@ -1178,7 +1147,7 @@ public async ValueTask AddObserverAsync(IWorldObserver observer) && observingPlayer.Party == this.Party && observingPlayer.Attributes is { } attributes) { - attributes[Stats.NearbyPartyMemberCount]++; + attributes[Stats.NearbyPartyMemberCount] += 1; } } @@ -1192,7 +1161,7 @@ public async ValueTask RemoveObserverAsync(IWorldObserver observer) && observingPlayer.Party == this.Party && observingPlayer.Attributes is { } attributes) { - attributes[Stats.NearbyPartyMemberCount]--; + attributes[Stats.NearbyPartyMemberCount] -= 1; } } @@ -1425,6 +1394,17 @@ internal async ValueTask AfterKilledPlayerAsync(Player killedPlayer) await this.ForEachWorldObserverAsync(o => o.UpdateCharacterHeroStateAsync(this), true).ConfigureAwait(false); } + /// + /// Sets the current values of the regeneration attributes to their maximum values. + /// + internal void SetReclaimableAttributesToMaximum() + { + foreach (var regeneration in Stats.IntervalRegenerationAttributes) + { + this.Attributes![regeneration.CurrentAttribute] = this.Attributes[regeneration.MaximumAttribute]; + } + } + /// protected override async ValueTask DisposeAsyncCore() { @@ -1874,7 +1854,6 @@ private async ValueTask OnPlayerEnteredWorldAsync() this.AddMissingStatAttributes(); this.Attributes = new ItemAwareAttributeSystem(this.Account!, selectedCharacter, this.GameContext.Configuration); - this.Attributes[Stats.NearbyPartyMemberCount] = 0; this.LogInvalidInventoryItems(); this._storages.CreateForCharacter(selectedCharacter); @@ -1949,17 +1928,6 @@ private void SetReclaimableAttributesBeforeEnterGame() this.Attributes[Stats.CurrentHealth] = Math.Min(this.Attributes[Stats.CurrentHealth], this.Attributes[Stats.MaximumHealth]); } - /// - /// Sets the current values of the regeneration attributes to their maximum values. - /// - internal void SetReclaimableAttributesToMaximum() - { - foreach (var regeneration in Stats.IntervalRegenerationAttributes) - { - this.Attributes![regeneration.CurrentAttribute] = this.Attributes[regeneration.MaximumAttribute]; - } - } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Catching all Exceptions.")] private async void OnAttributeValueChanged(object? sender, IAttribute attribute) { diff --git a/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs b/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs new file mode 100644 index 0000000000..51484898d0 --- /dev/null +++ b/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs @@ -0,0 +1,73 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.PlugIns; + +using System; +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.GameLogic.NPC; +using MUnique.OpenMU.PlugIns; + +/// +/// Updates and resets the shield recovery hiatus attribute. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.ShieldRecoveryHiatusPlugIn_Name), Description = nameof(PlugInResources.ShieldRecoveryHiatusPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("7B4A9E2D-C1F5-48E3-A6B2-1E7D4C8F3A5B")] +public class ShieldRecoveryHiatusPlugIn : IPeriodicTaskPlugIn, IAttackableMovedPlugIn, IAttackableGotHitPlugIn +{ + /// + public async ValueTask ExecuteTaskAsync(GameContext gameContext) + { + await gameContext.ForEachPlayerAsync(player => + { + if (player.SelectedCharacter != null + && !player.PlayerState.CurrentState.IsDisconnectedOrFinished() + && player.Attributes is { } attributes) + { + attributes[Stats.ShieldRecoveryHiatus] += 1; + } + + return Task.CompletedTask; + }).ConfigureAwait(false); + } + + /// + public void ForceStart() + { + // do nothing. + } + + /// + public void AttackableGotHit(IAttackable attackable, IAttacker attacker, HitInfo hitInfo) + { + var defender = attackable as Player ?? (attackable as Monster)?.SummonedBy; + var attackerPlayer = attacker as Player ?? (attacker as Monster)?.SummonedBy; + if (defender is null || attackerPlayer is null || defender == attackerPlayer) + { + return; + } + + if (hitInfo is { ShieldDamage: > 0 }) + { + attackable.Attributes[Stats.ShieldRecoveryHiatus] = 0; + } + } + + /// + public void AttackableMoved(IAttackable attackable) + { + if (attackable is not Player) + { + return; + } + + var attributes = attackable.Attributes; + if (attributes[Stats.IsShieldRecoveryActive] < 1 || attributes[Stats.CurrentShield] == attributes[Stats.MaximumShield]) + { + attributes[Stats.ShieldRecoveryHiatus] = 0; + } + } +} \ No newline at end of file diff --git a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs index 85dd8e39aa..c8958bf84f 100644 --- a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs +++ b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs @@ -10,7 +10,7 @@ namespace MUnique.OpenMU.GameLogic.PlugIns; /// /// Updates the and attributes. -/// For example, these can activate or increase shield, ability, health and mana recovery. +/// For example, these activate or increase shield, ability, health and mana recoveries. /// [PlugIn] [Display(Name = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Name), Description = nameof(PlugInResources.UpdateIsInSafezoneAfterPlayerMoved_Description), ResourceType = typeof(PlugInResources))] diff --git a/src/GameLogic/Properties/PlugInResources.Designer.cs b/src/GameLogic/Properties/PlugInResources.Designer.cs index 8a5df7f1ed..94d74e705e 100644 --- a/src/GameLogic/Properties/PlugInResources.Designer.cs +++ b/src/GameLogic/Properties/PlugInResources.Designer.cs @@ -2483,6 +2483,24 @@ public static string SetStatChatCommandPlugIn_Name { } } + /// + /// Looks up a localized string similar to Updates and resets the shield recovery hiatus attribute.. + /// + public static string ShieldRecoveryHiatusPlugIn_Description { + get { + return ResourceManager.GetString("ShieldRecoveryHiatusPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shield Recovery Hiatus. + /// + public static string ShieldRecoveryHiatusPlugIn_Name { + get { + return ResourceManager.GetString("ShieldRecoveryHiatusPlugIn_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to Handles the chat command '/fireworks <x> <y>'. Shows an fireworks effect at the specified coordinates.. /// diff --git a/src/GameLogic/Properties/PlugInResources.resx b/src/GameLogic/Properties/PlugInResources.resx index 81eca0bbf8..ded0ae0aec 100644 --- a/src/GameLogic/Properties/PlugInResources.resx +++ b/src/GameLogic/Properties/PlugInResources.resx @@ -171,6 +171,12 @@ Updates the state of the self defense system. + + Shield Recovery Hiatus + + + Updates and resets the shield recovery hiatus attribute. + Show Message To All When Player Entered World diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index 2171385d3c..a73e9d8963 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -140,6 +140,10 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsResting)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.03f, Stats.IsResting)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AbilityRecoveryAbsolute, 3, Stats.IsInSafezone)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsShieldRecoveryActive, 1, Stats.IsInSafezone)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsShieldRecoveryActive, 1, Stats.ShieldRecoveryEverywhere)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryRampFactor, 1f / 15f, Stats.ShieldRecoveryHiatus)); // (3 - 2) / (25 - 10) + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 1, Stats.ShieldRecoveryRampFactor, aggregateType: AggregateType.Multiplicate)); if (this.UseClassicPvp) { @@ -168,6 +172,7 @@ private void AddCommonBaseAttributeValues(ICollection baseA baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SwellLifeManaIncrease)); baseAttributeValues.Add(this.CreateConstValueAttribute(0.1f, Stats.DurabilityReductionFactor)); baseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.IncreaseBlockBonus)); // Nullify the Multiplicate values until DefSuccessRateIncPowUp master Skill is learned + baseAttributeValues.Add(this.CreateConstValueAttribute(4f / 3f, Stats.ShieldRecoveryRampFactor)); // y=mx+b: m=1/15, y=2, x=10 => b=4/3 if (isMaster) { diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs index b8a0148ce4..27e6cce807 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs @@ -45,6 +45,8 @@ protected CharacterClass CreateDarkKnight(CharacterClassNumber number, string na result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs index 326b1c2767..02b4bd6a89 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs @@ -43,6 +43,8 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs index 18ea11df8b..e150109d98 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs @@ -45,6 +45,8 @@ protected CharacterClass CreateDarkWizard(CharacterClassNumber number, string na result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs b/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs index a7476b1911..9e1b93a41f 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs @@ -49,6 +49,8 @@ protected CharacterClass CreateFairyElf(CharacterClassNumber number, string name result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs b/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs index 0e867c697c..c5cd20d456 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs @@ -48,6 +48,8 @@ protected CharacterClass CreateMagicGladiator(CharacterClassNumber number, strin result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs b/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs index f3bf07c38b..46013d122b 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs @@ -40,6 +40,8 @@ private CharacterClass CreateRageFighter(CharacterClassNumber number, string nam result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs index 37cae6a720..9486a95481 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs @@ -49,6 +49,8 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name, result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsInSafezone, 1, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); + result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); From b71641bfac7289718e921ebbe868e1f08f5bf9dc Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 25 Aug 2026 15:27:51 +0100 Subject: [PATCH 10/15] Added update plugin --- src/GameLogic/Attributes/Stats.cs | 5 + src/GameLogic/Player.cs | 7 +- .../CharacterClassInitialization.cs | 2 +- .../Updates/RegenerationsRefactorPlugIn.cs | 276 ++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 5 + 5 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs diff --git a/src/GameLogic/Attributes/Stats.cs b/src/GameLogic/Attributes/Stats.cs index 1f4c2991d7..5dabae4096 100644 --- a/src/GameLogic/Attributes/Stats.cs +++ b/src/GameLogic/Attributes/Stats.cs @@ -1642,6 +1642,11 @@ public static IEnumerable AfterMonsterKillRegenerationAttributes /// /// Gets the shield regeneration. /// + /// + /// Shield recovery is only possible at safe zone, except the character has a specific attribute which has the effect that it's recovered everywhere. + /// This attribute is usually provided by level 380 armor with a Guardian Option. + /// Also, shield has a penalty (hiatus) period before the recovery starts, after which it increases linearly (see ). + /// public static Regeneration ShieldRegeneration { get; } = new(ShieldRecoveryMultiplier, MaximumShield, CurrentShield, ShieldRecoveryAbsolute) { Interval = TimeSpan.FromSeconds(1), diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 1c5db2a1d4..8d42f1fe5b 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1061,7 +1061,8 @@ public async Task RegenerateAsync() } var now = DateTime.Now; - foreach (var r in Stats.IntervalRegenerationAttributes) + foreach (var r in Stats.IntervalRegenerationAttributes.Where(r => + attributes[r.RegenerationMultiplier] > 0 || attributes[r.AbsoluteAttribute] > 0)) { if ((r.EnablerAttribute is { } enabler && attributes[enabler] < 1) || (r.HiatusAttribute is { } hiatus && attributes[hiatus] < r.HiatusThreshold)) @@ -1147,7 +1148,7 @@ public async ValueTask AddObserverAsync(IWorldObserver observer) && observingPlayer.Party == this.Party && observingPlayer.Attributes is { } attributes) { - attributes[Stats.NearbyPartyMemberCount] += 1; + attributes[Stats.NearbyPartyMemberCount]++; } } @@ -1161,7 +1162,7 @@ public async ValueTask RemoveObserverAsync(IWorldObserver observer) && observingPlayer.Party == this.Party && observingPlayer.Attributes is { } attributes) { - attributes[Stats.NearbyPartyMemberCount] -= 1; + attributes[Stats.NearbyPartyMemberCount]--; } } diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index a73e9d8963..add2b6cd2e 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -123,7 +123,7 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IncreaseBlockBonus, 1, Stats.DefenseRatePvm, aggregateType: AggregateType.Multiplicate)); // If two weapons are equipped (DK, MG, Sum, RF) we average the weapon attack speed - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount, InputOperator.Maximum)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeedByWeapon, 0.5f, Stats.AreTwoWeaponsEquipped, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate)); var tempDefense = this.Context.CreateNew(Guid.NewGuid(), "Temp Defense Bonus multiplier with Shield", string.Empty); diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs new file mode 100644 index 0000000000..a40f11a043 --- /dev/null +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs @@ -0,0 +1,276 @@ +// +// 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.Configuration; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.Persistence.Initialization.Skills; +using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes and reworks some regeneration attributes (health, shield, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG, DL, RF). +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("E6A3B9F1-7C4D-48E2-A5B8-1F9D3C6E2A7B")] +public class RegenerationsRefactorPlugIn : UpdatePlugInBase +{ + /// + /// The plug in name. + /// + internal const string PlugInName = "Regenerations Refactor PlugIn"; + + /// + /// The plug in description. + /// + internal const string PlugInDescription = "This update fixes and reworks some regeneration attributes (health, shield, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG, DL, RF)."; + + /// + public override UpdateVersion Version => UpdateVersion.RegenerationsRefactor; + + /// + public override string DataInitializationKey => DataInitialization.Id; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2026, 8, 25, 16, 0, 0, DateTimeKind.Utc); + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + // Create new Stats + var isShieldRecoveryActive = context.CreateNew(Stats.IsShieldRecoveryActive.Id, Stats.IsShieldRecoveryActive.Designation, Stats.IsShieldRecoveryActive.Description); + gameConfiguration.Attributes.Add(isShieldRecoveryActive); + var shieldRecoveryHiatus = context.CreateNew(Stats.ShieldRecoveryHiatus.Id, Stats.ShieldRecoveryHiatus.Designation, Stats.ShieldRecoveryHiatus.Description); + gameConfiguration.Attributes.Add(shieldRecoveryHiatus); + var shieldRecoveryRampFactor = context.CreateNew(Stats.ShieldRecoveryRampFactor.Id, Stats.ShieldRecoveryRampFactor.Designation, Stats.ShieldRecoveryRampFactor.Description); + gameConfiguration.Attributes.Add(shieldRecoveryRampFactor); + var isResting = context.CreateNew(Stats.IsResting.Id, Stats.IsResting.Designation, Stats.IsResting.Description); + gameConfiguration.Attributes.Add(isResting); + + var areTwoWeaponsEquipped = Stats.AreTwoWeaponsEquipped.GetPersistent(gameConfiguration); + var equippedWeaponCount = Stats.EquippedWeaponCount.GetPersistent(gameConfiguration); + var attackSpeedByWeapon = Stats.AttackSpeedByWeapon.GetPersistent(gameConfiguration); + var innovationDefDecrement = Stats.InnovationDefDecrement.GetPersistent(gameConfiguration); + var defenseDecrement = Stats.DefenseDecrement.GetPersistent(gameConfiguration); + + var healthRecoveryMultiplier = Stats.HealthRecoveryMultiplier.GetPersistent(gameConfiguration); + var manaRecoveryMultiplier = Stats.ManaRecoveryMultiplier.GetPersistent(gameConfiguration); + var shieldRecoveryMultiplier = Stats.ShieldRecoveryMultiplier.GetPersistent(gameConfiguration); + var abilityRecoveryMultiplier = Stats.AbilityRecoveryMultiplier.GetPersistent(gameConfiguration); + var abilityRecoveryAbsolute = Stats.AbilityRecoveryAbsolute.GetPersistent(gameConfiguration); + var isInSafezone = Stats.IsInSafezone.GetPersistent(gameConfiguration); + var shieldRecoveryEverywhere = Stats.ShieldRecoveryEverywhere.GetPersistent(gameConfiguration); + var nearbyPartyMemberCount = Stats.NearbyPartyMemberCount.GetPersistent(gameConfiguration); + + var movementSpeed = Stats.MovementSpeed.GetPersistent(gameConfiguration); + var movementSpeedUnderwater = Stats.MovementSpeedUnderwater.GetPersistent(gameConfiguration); + + gameConfiguration.CharacterClasses.ForEach(charClass => + { + var attrCombos = charClass.AttributeCombinations.ToList(); + + // Remove temp attack speed combos + var tempAttackSpeeds = gameConfiguration.Attributes.Where(a => a.Designation == "Temp Half weapon attack speed"); + if (tempAttackSpeeds.Any()) + { + AttributeDefinition? classTempAttackSpeed = null; + if (attrCombos.FirstOrDefault(ac => tempAttackSpeeds.Contains(ac.TargetAttribute)) is { } attrCombo1) + { + attrCombos.Remove(attrCombo1); + classTempAttackSpeed = attrCombo1.TargetAttribute; + } + + if (attrCombos.FirstOrDefault(ac => tempAttackSpeeds.Contains(ac.InputAttribute)) is { } attrCombo2) + { + attrCombos.Remove(attrCombo2); + } + + if (classTempAttackSpeed is not null) + { + gameConfiguration.Attributes.Remove(classTempAttackSpeed); + } + } + + // Add new attribute combination + var areTwoWeaponsEquippedToAttackSpeedByWeapon = context.CreateNew( + attackSpeedByWeapon, + 0.5f, + areTwoWeaponsEquipped, + InputOperator.ExponentiateByAttribute, + default(AttributeDefinition?), + AggregateType.Multiplicate); + + charClass.AttributeCombinations.Add(areTwoWeaponsEquippedToAttackSpeedByWeapon); + + // Remove temp innovation defense decrement combos + var tempInnovationDefDecrements = gameConfiguration.Attributes.Where(a => a.Designation == "Temp Innovation defense decrement"); + if (tempInnovationDefDecrements.Any()) + { + AttributeDefinition? classTempInnovationDefDecrement = null; + if (attrCombos.FirstOrDefault(ac => tempInnovationDefDecrements.Contains(ac.InputAttribute)) is { } attrCombo) + { + attrCombos.Remove(attrCombo); + classTempInnovationDefDecrement = attrCombo.InputAttribute; + } + + if (attrCombos.FirstOrDefault(ac => ac.InputAttribute == innovationDefDecrement && ac.InputOperand == -1) is { } innovationDefDecrementToTempInnovDefDec) + { + innovationDefDecrementToTempInnovDefDec.TargetAttribute = defenseDecrement; + } + + if (classTempInnovationDefDecrement is not null) + { + gameConfiguration.Attributes.Remove(classTempInnovationDefDecrement); + } + } + + // Add new attribute combination + var isRestingToHealthRecoveryMultiplier = context.CreateNew( + healthRecoveryMultiplier, + 0.03f, + isResting, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var isRestingToManaRecoveryMultiplier = context.CreateNew( + manaRecoveryMultiplier, + 0.03f, + isResting, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var isInSafezoneToAbilityRecoveryAbsolute = context.CreateNew( + abilityRecoveryAbsolute, + 3f, + isInSafezone, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var isInSafezoneToIsShieldRecoveryActive = context.CreateNew( + isShieldRecoveryActive, + 1f, + isInSafezone, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var shieldRecoveryEverywhereToIsShieldRecoveryActive = context.CreateNew( + isShieldRecoveryActive, + 1f, + shieldRecoveryEverywhere, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var shieldRecoveryHiatusToShieldRecoveryRampFactor = context.CreateNew( + shieldRecoveryRampFactor, + 1f / 15f, + shieldRecoveryHiatus, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var shieldRecoveryRampFactorToShieldRecoveryMultiplier = context.CreateNew( + shieldRecoveryMultiplier, + 1f / 15f, + shieldRecoveryRampFactor, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.Multiplicate); + + charClass.AttributeCombinations.Add(isRestingToHealthRecoveryMultiplier); + charClass.AttributeCombinations.Add(isRestingToManaRecoveryMultiplier); + charClass.AttributeCombinations.Add(isInSafezoneToAbilityRecoveryAbsolute); + charClass.AttributeCombinations.Add(isInSafezoneToIsShieldRecoveryActive); + charClass.AttributeCombinations.Add(shieldRecoveryEverywhereToIsShieldRecoveryActive); + charClass.AttributeCombinations.Add(shieldRecoveryHiatusToShieldRecoveryRampFactor); + charClass.AttributeCombinations.Add(shieldRecoveryRampFactorToShieldRecoveryMultiplier); + + // Remove recovery multiplier combos + if (attrCombos.FirstOrDefault(ac => ac.InputAttribute == isInSafezone && ac.TargetAttribute == healthRecoveryMultiplier) is { } isInSafezoneToHealthRecoveryMultiplier) + { + attrCombos.Remove(isInSafezoneToHealthRecoveryMultiplier); + } + + if (attrCombos.FirstOrDefault(ac => ac.InputAttribute == isInSafezone && ac.TargetAttribute == shieldRecoveryMultiplier) is { } isInSafezoneToShieldRecoveryMultiplier) + { + attrCombos.Remove(isInSafezoneToShieldRecoveryMultiplier); + } + + // Change common base attribute values (ConstValueAttribute) + if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == manaRecoveryMultiplier) is { } baseManaRecoveryMultiplier) + { + charClass.BaseAttributeValues.Remove(baseManaRecoveryMultiplier); + } + + charClass.BaseAttributeValues.Add(context.CreateNew(0.037f, manaRecoveryMultiplier)); + charClass.BaseAttributeValues.Add(context.CreateNew(4f / 3f, shieldRecoveryRampFactor)); + + if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == shieldRecoveryMultiplier) is { } baseShieldRecoveryMultiplier) + { + charClass.BaseAttributeValues.Remove(baseShieldRecoveryMultiplier); + } + + charClass.BaseAttributeValues.Add(context.CreateNew(100, shieldRecoveryMultiplier)); + charClass.BaseAttributeValues.Add(context.CreateNew(1f / 75000, shieldRecoveryMultiplier, AggregateType.Multiplicate)); + + // Create new StatAttributDefinitions + charClass.StatAttributes.Add(context.CreateNew(isResting, 0, false)); + charClass.StatAttributes.Add(context.CreateNew(nearbyPartyMemberCount, 0, false)); + charClass.StatAttributes.Add(context.CreateNew(shieldRecoveryHiatus, 0, false)); + + // Change base ability recovery multiplier + if (charClass.Number != 4 && charClass.Number != 6 && charClass.Number != 7) // DK classes + { + if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == abilityRecoveryMultiplier) is { } baseAbilityRecoveryMultiplier) + { + charClass.BaseAttributeValues.Remove(baseAbilityRecoveryMultiplier); + } + + charClass.BaseAttributeValues.Add(context.CreateNew(0.03f, abilityRecoveryMultiplier)); + } + + // Add default movement speeds for tier 2 chars + if (charClass.Number == 16 || charClass.Number == 17 // DL classes + || charClass.Number == 12 || charClass.Number == 13 // MG classes + || charClass.Number == 24 || charClass.Number == 25) // RF classes + { + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeed, AggregateType.Maximum)); + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeedUnderwater, AggregateType.Maximum)); + } + }); + + // Update master skills + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SdRecoverySpeedInc)?.MasterDefinition is { } sdRecoverySpeedInc) + { + sdRecoverySpeedInc.ValueFormula = $"1 + {SkillsInitializer.FormulaRecoveryIncrease120}"; + sdRecoverySpeedInc.TargetAttribute = shieldRecoveryMultiplier; + sdRecoverySpeedInc.Aggregation = AggregateType.Multiplicate; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.IncreaseSdRecoveryRate)?.MasterDefinition is { } increaseSdRecoveryRate) + { + increaseSdRecoveryRate.ValueFormula = $"1 + {SkillsInitializer.FormulaRecoveryIncrease120}"; + increaseSdRecoveryRate.TargetAttribute = shieldRecoveryMultiplier; + increaseSdRecoveryRate.Aggregation = AggregateType.Multiplicate; + } + } +} diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 02022b24d4..000cc507a0 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -524,4 +524,9 @@ public enum UpdateVersion /// The version of the . /// FinishRageFighterMasterTree = 103, + + /// + /// The version of the . + /// + RegenerationsRefactor = 104, } From 9f6356cde3c6673b385309a4e94665bdf8e5c75a Mon Sep 17 00:00:00 2001 From: ze-dom Date: Tue, 25 Aug 2026 21:19:46 +0100 Subject: [PATCH 11/15] Fixed some update plugin bugs --- .../CharacterClassInitialization.cs | 2 +- .../Updates/RegenerationsRefactorPlugIn.cs | 22 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index add2b6cd2e..a73e9d8963 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -123,7 +123,7 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IncreaseBlockBonus, 1, Stats.DefenseRatePvm, aggregateType: AggregateType.Multiplicate)); // If two weapons are equipped (DK, MG, Sum, RF) we average the weapon attack speed - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AreTwoWeaponsEquipped, 1, Stats.EquippedWeaponCount, InputOperator.Maximum)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackSpeedByWeapon, 0.5f, Stats.AreTwoWeaponsEquipped, InputOperator.ExponentiateByAttribute, AggregateType.Multiplicate)); var tempDefense = this.Context.CreateNew(Guid.NewGuid(), "Temp Defense Bonus multiplier with Shield", string.Empty); diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs index a40f11a043..11598d20fa 100644 --- a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs @@ -24,7 +24,7 @@ public class RegenerationsRefactorPlugIn : UpdatePlugInBase /// /// The plug in name. /// - internal const string PlugInName = "Regenerations Refactor PlugIn"; + internal const string PlugInName = "Regenerations Refactor"; /// /// The plug in description. @@ -82,12 +82,17 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio gameConfiguration.CharacterClasses.ForEach(charClass => { - var attrCombos = charClass.AttributeCombinations.ToList(); + var attrCombos = charClass.AttributeCombinations; // Remove temp attack speed combos var tempAttackSpeeds = gameConfiguration.Attributes.Where(a => a.Designation == "Temp Half weapon attack speed"); if (tempAttackSpeeds.Any()) { + if (attrCombos.FirstOrDefault(ac => ac.TargetAttribute == areTwoWeaponsEquipped) is { } equippedWeaponCountToAreTwoWeaponsEquipped) + { + equippedWeaponCountToAreTwoWeaponsEquipped.AggregateType = AggregateType.Maximum; + } + AttributeDefinition? classTempAttackSpeed = null; if (attrCombos.FirstOrDefault(ac => tempAttackSpeeds.Contains(ac.TargetAttribute)) is { } attrCombo1) { @@ -106,7 +111,6 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio } } - // Add new attribute combination var areTwoWeaponsEquippedToAttackSpeedByWeapon = context.CreateNew( attackSpeedByWeapon, 0.5f, @@ -139,7 +143,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio } } - // Add new attribute combination + // Add new attribute combinations var isRestingToHealthRecoveryMultiplier = context.CreateNew( healthRecoveryMultiplier, 0.03f, @@ -190,7 +194,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio var shieldRecoveryRampFactorToShieldRecoveryMultiplier = context.CreateNew( shieldRecoveryMultiplier, - 1f / 15f, + 1f, shieldRecoveryRampFactor, InputOperator.Multiply, default(AttributeDefinition?), @@ -221,15 +225,15 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio charClass.BaseAttributeValues.Remove(baseManaRecoveryMultiplier); } - charClass.BaseAttributeValues.Add(context.CreateNew(0.037f, manaRecoveryMultiplier)); - charClass.BaseAttributeValues.Add(context.CreateNew(4f / 3f, shieldRecoveryRampFactor)); + charClass.BaseAttributeValues.Add(context.CreateNew(0.037f, manaRecoveryMultiplier, AggregateType.AddRaw)); + charClass.BaseAttributeValues.Add(context.CreateNew(4f / 3f, shieldRecoveryRampFactor, AggregateType.AddRaw)); if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == shieldRecoveryMultiplier) is { } baseShieldRecoveryMultiplier) { charClass.BaseAttributeValues.Remove(baseShieldRecoveryMultiplier); } - charClass.BaseAttributeValues.Add(context.CreateNew(100, shieldRecoveryMultiplier)); + charClass.BaseAttributeValues.Add(context.CreateNew(100, shieldRecoveryMultiplier, AggregateType.AddRaw)); charClass.BaseAttributeValues.Add(context.CreateNew(1f / 75000, shieldRecoveryMultiplier, AggregateType.Multiplicate)); // Create new StatAttributDefinitions @@ -245,7 +249,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio charClass.BaseAttributeValues.Remove(baseAbilityRecoveryMultiplier); } - charClass.BaseAttributeValues.Add(context.CreateNew(0.03f, abilityRecoveryMultiplier)); + charClass.BaseAttributeValues.Add(context.CreateNew(0.03f, abilityRecoveryMultiplier, AggregateType.AddRaw)); } // Add default movement speeds for tier 2 chars From aabd4f72bbaebc696cb856083e54bbf346d865cd Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 26 Aug 2026 13:29:54 +0100 Subject: [PATCH 12/15] Some fixes --- src/AttributeSystem/ComposableAttribute.cs | 11 +++++++++++ src/GameLogic/Player.cs | 7 ++++--- src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs | 12 ++++++------ .../CharacterClasses/ClassDarkKnight.cs | 1 - .../Initialization/CharacterClasses/ClassDarkLord.cs | 1 - .../CharacterClasses/ClassDarkWizard.cs | 1 - .../Initialization/CharacterClasses/ClassFairyElf.cs | 1 - .../CharacterClasses/ClassMagicGladiator.cs | 1 - .../CharacterClasses/ClassRageFighter.cs | 1 - .../Initialization/CharacterClasses/ClassSummoner.cs | 1 - .../Updates/RegenerationsRefactorPlugIn.cs | 3 +-- 11 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/AttributeSystem/ComposableAttribute.cs b/src/AttributeSystem/ComposableAttribute.cs index 1d5cd033fd..c0e2701298 100644 --- a/src/AttributeSystem/ComposableAttribute.cs +++ b/src/AttributeSystem/ComposableAttribute.cs @@ -54,6 +54,17 @@ public void RemoveElement(IElement element) } } + /// + /// Removes all elements from the composition. + /// + public void RemoveAllElements() + { + while (this._elementList.FirstOrDefault() is { } element) + { + this._elementList.Remove(element); + } + } + private float GetAndCacheValue() { if (this._elementList.Count == 0) diff --git a/src/GameLogic/Player.cs b/src/GameLogic/Player.cs index 8d42f1fe5b..10af5c4f58 100644 --- a/src/GameLogic/Player.cs +++ b/src/GameLogic/Player.cs @@ -1060,13 +1060,13 @@ public async Task RegenerateAsync() return; } - var now = DateTime.Now; - foreach (var r in Stats.IntervalRegenerationAttributes.Where(r => - attributes[r.RegenerationMultiplier] > 0 || attributes[r.AbsoluteAttribute] > 0)) + var now = DateTime.UtcNow; + foreach (var r in Stats.IntervalRegenerationAttributes) { if ((r.EnablerAttribute is { } enabler && attributes[enabler] < 1) || (r.HiatusAttribute is { } hiatus && attributes[hiatus] < r.HiatusThreshold)) { + this._lastRegeneration[r] = now; continue; } @@ -1855,6 +1855,7 @@ private async ValueTask OnPlayerEnteredWorldAsync() this.AddMissingStatAttributes(); this.Attributes = new ItemAwareAttributeSystem(this.Account!, selectedCharacter, this.GameContext.Configuration); + this.Attributes[Stats.IsResting] = 0; this.LogInvalidInventoryItems(); this._storages.CreateForCharacter(selectedCharacter); diff --git a/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs b/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs index 51484898d0..af4c4486ef 100644 --- a/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs +++ b/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs @@ -6,6 +6,7 @@ namespace MUnique.OpenMU.GameLogic.PlugIns; using System; using System.Runtime.InteropServices; +using MUnique.OpenMU.AttributeSystem; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.NPC; using MUnique.OpenMU.PlugIns; @@ -27,7 +28,7 @@ await gameContext.ForEachPlayerAsync(player => && !player.PlayerState.CurrentState.IsDisconnectedOrFinished() && player.Attributes is { } attributes) { - attributes[Stats.ShieldRecoveryHiatus] += 1; + attributes.AddElement(new SimpleElement(1, AggregateType.AddRaw), Stats.ShieldRecoveryHiatus); } return Task.CompletedTask; @@ -45,29 +46,28 @@ public void AttackableGotHit(IAttackable attackable, IAttacker attacker, HitInfo { var defender = attackable as Player ?? (attackable as Monster)?.SummonedBy; var attackerPlayer = attacker as Player ?? (attacker as Monster)?.SummonedBy; - if (defender is null || attackerPlayer is null || defender == attackerPlayer) + if (defender is null || attackerPlayer is null || defender == attackerPlayer || defender.Attributes is not { } attributes) { return; } if (hitInfo is { ShieldDamage: > 0 }) { - attackable.Attributes[Stats.ShieldRecoveryHiatus] = 0; + attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.RemoveAllElements(); } } /// public void AttackableMoved(IAttackable attackable) { - if (attackable is not Player) + if (attackable is not Player player || player.Attributes is not { } attributes) { return; } - var attributes = attackable.Attributes; if (attributes[Stats.IsShieldRecoveryActive] < 1 || attributes[Stats.CurrentShield] == attributes[Stats.MaximumShield]) { - attributes[Stats.ShieldRecoveryHiatus] = 0; + attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.RemoveAllElements(); } } } \ No newline at end of file diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs index 27e6cce807..e7cb931c5a 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkKnight.cs @@ -46,7 +46,6 @@ protected CharacterClass CreateDarkKnight(CharacterClassNumber number, string na result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs index 02b4bd6a89..c7a1b40d78 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkLord.cs @@ -44,7 +44,6 @@ private CharacterClass CreateDarkLord(CharacterClassNumber number, string name, result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs b/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs index e150109d98..d073b79c67 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassDarkWizard.cs @@ -46,7 +46,6 @@ protected CharacterClass CreateDarkWizard(CharacterClassNumber number, string na result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs b/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs index 9e1b93a41f..5c94445873 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassFairyElf.cs @@ -50,7 +50,6 @@ protected CharacterClass CreateFairyElf(CharacterClassNumber number, string name result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs b/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs index c5cd20d456..38f759978d 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassMagicGladiator.cs @@ -49,7 +49,6 @@ protected CharacterClass CreateMagicGladiator(CharacterClassNumber number, strin result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs b/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs index 46013d122b..1c87ea25d0 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassRageFighter.cs @@ -41,7 +41,6 @@ private CharacterClass CreateRageFighter(CharacterClassNumber number, string nam result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); this.AddDoubleWieldAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs index 9486a95481..2c0e3ec5c8 100644 --- a/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs +++ b/src/Persistence/Initialization/CharacterClasses/ClassSummoner.cs @@ -50,7 +50,6 @@ private CharacterClass CreateSummoner(CharacterClassNumber number, string name, result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.IsResting, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.Resets, 0, false)); result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.NearbyPartyMemberCount, 0, false)); - result.StatAttributes.Add(this.CreateStatAttributeDefinition(Stats.ShieldRecoveryHiatus, 0, false)); this.AddCommonAttributeRelationships(result.AttributeCombinations); diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs index 11598d20fa..04538ca581 100644 --- a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs @@ -47,7 +47,7 @@ public class RegenerationsRefactorPlugIn : UpdatePlugInBase public override bool IsMandatory => true; /// - public override DateTime CreatedAt => new(2026, 8, 25, 16, 0, 0, DateTimeKind.Utc); + public override DateTime CreatedAt => new(2026, 8, 26, 16, 0, 0, DateTimeKind.Utc); /// protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) @@ -239,7 +239,6 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio // Create new StatAttributDefinitions charClass.StatAttributes.Add(context.CreateNew(isResting, 0, false)); charClass.StatAttributes.Add(context.CreateNew(nearbyPartyMemberCount, 0, false)); - charClass.StatAttributes.Add(context.CreateNew(shieldRecoveryHiatus, 0, false)); // Change base ability recovery multiplier if (charClass.Number != 4 && charClass.Number != 6 && charClass.Number != 7) // DK classes From 92a4d1bfb32fb01276a6fe5363d82c40cc4478f4 Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 26 Aug 2026 16:14:41 +0100 Subject: [PATCH 13/15] plugin fixes --- .../Initialization/Updates/RegenerationsRefactorPlugIn.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs index 04538ca581..36bffa30ce 100644 --- a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs @@ -58,6 +58,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio var shieldRecoveryHiatus = context.CreateNew(Stats.ShieldRecoveryHiatus.Id, Stats.ShieldRecoveryHiatus.Designation, Stats.ShieldRecoveryHiatus.Description); gameConfiguration.Attributes.Add(shieldRecoveryHiatus); var shieldRecoveryRampFactor = context.CreateNew(Stats.ShieldRecoveryRampFactor.Id, Stats.ShieldRecoveryRampFactor.Designation, Stats.ShieldRecoveryRampFactor.Description); + shieldRecoveryRampFactor.MaximumValue = 3; gameConfiguration.Attributes.Add(shieldRecoveryRampFactor); var isResting = context.CreateNew(Stats.IsResting.Id, Stats.IsResting.Designation, Stats.IsResting.Description); gameConfiguration.Attributes.Add(isResting); @@ -90,7 +91,7 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio { if (attrCombos.FirstOrDefault(ac => ac.TargetAttribute == areTwoWeaponsEquipped) is { } equippedWeaponCountToAreTwoWeaponsEquipped) { - equippedWeaponCountToAreTwoWeaponsEquipped.AggregateType = AggregateType.Maximum; + equippedWeaponCountToAreTwoWeaponsEquipped.InputOperator = InputOperator.Maximum; } AttributeDefinition? classTempAttackSpeed = null; @@ -256,8 +257,8 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio || charClass.Number == 12 || charClass.Number == 13 // MG classes || charClass.Number == 24 || charClass.Number == 25) // RF classes { - charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeed, AggregateType.Maximum)); - charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, Stats.MovementSpeedUnderwater, AggregateType.Maximum)); + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeed, AggregateType.Maximum)); + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeedUnderwater, AggregateType.Maximum)); } }); From 8447be54b1b2b03891306c6c04f65143e3696d3a Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 26 Aug 2026 17:13:41 +0100 Subject: [PATCH 14/15] Added plugins for different versions --- .../CharacterClassInitialization.cs | 13 +- .../Updates/RegenerationsRefactorPlugIn075.cs | 30 +++++ .../RegenerationsRefactorPlugIn095d.cs | 55 ++++++++ ....cs => RegenerationsRefactorPlugInBase.cs} | 107 +-------------- .../RegenerationsRefactorPlugInSeason6.cs | 127 ++++++++++++++++++ .../Initialization/Updates/UpdateVersion.cs | 14 +- 6 files changed, 236 insertions(+), 110 deletions(-) create mode 100644 src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn075.cs create mode 100644 src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn095d.cs rename src/Persistence/Initialization/Updates/{RegenerationsRefactorPlugIn.cs => RegenerationsRefactorPlugInBase.cs} (56%) create mode 100644 src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInSeason6.cs diff --git a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs index a73e9d8963..7b85676e79 100644 --- a/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs +++ b/src/Persistence/Initialization/CharacterClasses/CharacterClassInitialization.cs @@ -140,16 +140,19 @@ private void AddCommonAttributeRelationships(ICollection attributeRelationships.Add(this.CreateAttributeRelationship(Stats.HealthRecoveryMultiplier, 0.03f, Stats.IsResting)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ManaRecoveryMultiplier, 0.03f, Stats.IsResting)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AbilityRecoveryAbsolute, 3, Stats.IsInSafezone)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsShieldRecoveryActive, 1, Stats.IsInSafezone)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsShieldRecoveryActive, 1, Stats.ShieldRecoveryEverywhere)); - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryRampFactor, 1f / 15f, Stats.ShieldRecoveryHiatus)); // (3 - 2) / (25 - 10) - attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 1, Stats.ShieldRecoveryRampFactor, aggregateType: AggregateType.Multiplicate)); if (this.UseClassicPvp) { attributeRelationships.Add(this.CreateAttributeRelationship(Stats.DefenseRatePvp, 1, Stats.DefenseRatePvm)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.AttackRatePvp, 1, Stats.AttackRatePvm)); } + else + { + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsShieldRecoveryActive, 1, Stats.IsInSafezone)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.IsShieldRecoveryActive, 1, Stats.ShieldRecoveryEverywhere)); + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryRampFactor, 1f / 15f, Stats.ShieldRecoveryHiatus)); // (3 - 2) / (25 - 10) + attributeRelationships.Add(this.CreateAttributeRelationship(Stats.ShieldRecoveryMultiplier, 1, Stats.ShieldRecoveryRampFactor, aggregateType: AggregateType.Multiplicate)); + } attributeRelationships.Add(this.CreateAttributeRelationship(Stats.MaximumGuildSize, 0.1f, Stats.Level)); attributeRelationships.Add(this.CreateAttributeRelationship(Stats.CanFly, 1, Stats.IsDinorantEquipped)); @@ -172,7 +175,6 @@ private void AddCommonBaseAttributeValues(ICollection baseA baseAttributeValues.Add(this.CreateConstValueAttribute(1, Stats.SwellLifeManaIncrease)); baseAttributeValues.Add(this.CreateConstValueAttribute(0.1f, Stats.DurabilityReductionFactor)); baseAttributeValues.Add(this.CreateConstValueAttribute(0, Stats.IncreaseBlockBonus)); // Nullify the Multiplicate values until DefSuccessRateIncPowUp master Skill is learned - baseAttributeValues.Add(this.CreateConstValueAttribute(4f / 3f, Stats.ShieldRecoveryRampFactor)); // y=mx+b: m=1/15, y=2, x=10 => b=4/3 if (isMaster) { @@ -184,6 +186,7 @@ private void AddCommonBaseAttributeValues(ICollection baseA { baseAttributeValues.Add(this.CreateConstValueAttribute(100, Stats.ShieldRecoveryMultiplier)); baseAttributeValues.Add(this.CreateConstValueAttribute(1f / 75000, Stats.ShieldRecoveryMultiplier, AggregateType.Multiplicate)); // 1 / (30 * 100 * 25) + baseAttributeValues.Add(this.CreateConstValueAttribute(4f / 3f, Stats.ShieldRecoveryRampFactor)); // y=mx+b: m=1/15, y=2, x=10 => b=4/3 } } diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn075.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn075.cs new file mode 100644 index 0000000000..0dba65d0b0 --- /dev/null +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn075.cs @@ -0,0 +1,30 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes and reworks some regeneration attributes (health, mana, ability). +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("F079402E-F557-423C-B376-80A1B87D842D")] +public class RegenerationsRefactorPlugIn075 : RegenerationsRefactorPlugInBase +{ + /// + public override UpdateVersion Version => UpdateVersion.RegenerationsRefactor075; + + /// + public override string DataInitializationKey => Version075.DataInitialization.Id; + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false); + } +} diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn095d.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn095d.cs new file mode 100644 index 0000000000..8bf8a15c6b --- /dev/null +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn095d.cs @@ -0,0 +1,55 @@ +// +// 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.Configuration; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.Persistence.Initialization; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes and reworks some regeneration attributes (health, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG). +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("A7C2E9F4-5B1D-4E8A-9C6F-3D2B7A1E5F90")] +public class RegenerationsRefactorPlugIn095D : RegenerationsRefactorPlugInBase +{ + /// + /// The plug in description. + /// + internal new const string PlugInDescription = "This update fixes and reworks some regeneration attributes (health, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG)."; + + /// + public override UpdateVersion Version => UpdateVersion.RegenerationsRefactorSeason6; + + /// + public override string DataInitializationKey => Version095d.DataInitialization.Id; + + /// + public override string Description => PlugInDescription; + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false); + + var movementSpeed = Stats.MovementSpeed.GetPersistent(gameConfiguration); + var movementSpeedUnderwater = Stats.MovementSpeedUnderwater.GetPersistent(gameConfiguration); + + gameConfiguration.CharacterClasses.ForEach(charClass => + { + // Add default movement speeds for tier 2 chars + if (charClass.Number == 12 || charClass.Number == 13) // MG classes + { + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeed, AggregateType.Maximum)); + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeedUnderwater, AggregateType.Maximum)); + } + }); + } +} diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInBase.cs similarity index 56% rename from src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs rename to src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInBase.cs index 36bffa30ce..6df7612862 100644 --- a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugIn.cs +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInBase.cs @@ -1,25 +1,18 @@ -// +// // 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.Configuration; using MUnique.OpenMU.GameLogic; using MUnique.OpenMU.GameLogic.Attributes; -using MUnique.OpenMU.Persistence.Initialization.Skills; -using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix; -using MUnique.OpenMU.PlugIns; /// -/// This update fixes and reworks some regeneration attributes (health, shield, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG, DL, RF). +/// This update fixes and reworks some regeneration attributes (health, mana, ability). /// -[PlugIn] -[Display(Name = PlugInName, Description = PlugInDescription)] -[Guid("E6A3B9F1-7C4D-48E2-A5B8-1F9D3C6E2A7B")] -public class RegenerationsRefactorPlugIn : UpdatePlugInBase +public abstract class RegenerationsRefactorPlugInBase : UpdatePlugInBase { /// /// The plug in name. @@ -29,13 +22,7 @@ public class RegenerationsRefactorPlugIn : UpdatePlugInBase /// /// The plug in description. /// - internal const string PlugInDescription = "This update fixes and reworks some regeneration attributes (health, shield, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG, DL, RF)."; - - /// - public override UpdateVersion Version => UpdateVersion.RegenerationsRefactor; - - /// - public override string DataInitializationKey => DataInitialization.Id; + internal const string PlugInDescription = "This update fixes and reworks some regeneration attributes (health, mana, ability)."; /// public override string Name => PlugInName; @@ -53,13 +40,6 @@ public class RegenerationsRefactorPlugIn : UpdatePlugInBase protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) { // Create new Stats - var isShieldRecoveryActive = context.CreateNew(Stats.IsShieldRecoveryActive.Id, Stats.IsShieldRecoveryActive.Designation, Stats.IsShieldRecoveryActive.Description); - gameConfiguration.Attributes.Add(isShieldRecoveryActive); - var shieldRecoveryHiatus = context.CreateNew(Stats.ShieldRecoveryHiatus.Id, Stats.ShieldRecoveryHiatus.Designation, Stats.ShieldRecoveryHiatus.Description); - gameConfiguration.Attributes.Add(shieldRecoveryHiatus); - var shieldRecoveryRampFactor = context.CreateNew(Stats.ShieldRecoveryRampFactor.Id, Stats.ShieldRecoveryRampFactor.Designation, Stats.ShieldRecoveryRampFactor.Description); - shieldRecoveryRampFactor.MaximumValue = 3; - gameConfiguration.Attributes.Add(shieldRecoveryRampFactor); var isResting = context.CreateNew(Stats.IsResting.Id, Stats.IsResting.Designation, Stats.IsResting.Description); gameConfiguration.Attributes.Add(isResting); @@ -71,16 +51,11 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio var healthRecoveryMultiplier = Stats.HealthRecoveryMultiplier.GetPersistent(gameConfiguration); var manaRecoveryMultiplier = Stats.ManaRecoveryMultiplier.GetPersistent(gameConfiguration); - var shieldRecoveryMultiplier = Stats.ShieldRecoveryMultiplier.GetPersistent(gameConfiguration); var abilityRecoveryMultiplier = Stats.AbilityRecoveryMultiplier.GetPersistent(gameConfiguration); var abilityRecoveryAbsolute = Stats.AbilityRecoveryAbsolute.GetPersistent(gameConfiguration); var isInSafezone = Stats.IsInSafezone.GetPersistent(gameConfiguration); - var shieldRecoveryEverywhere = Stats.ShieldRecoveryEverywhere.GetPersistent(gameConfiguration); var nearbyPartyMemberCount = Stats.NearbyPartyMemberCount.GetPersistent(gameConfiguration); - var movementSpeed = Stats.MovementSpeed.GetPersistent(gameConfiguration); - var movementSpeedUnderwater = Stats.MovementSpeedUnderwater.GetPersistent(gameConfiguration); - gameConfiguration.CharacterClasses.ForEach(charClass => { var attrCombos = charClass.AttributeCombinations; @@ -169,45 +144,9 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio default(AttributeDefinition?), AggregateType.AddRaw); - var isInSafezoneToIsShieldRecoveryActive = context.CreateNew( - isShieldRecoveryActive, - 1f, - isInSafezone, - InputOperator.Multiply, - default(AttributeDefinition?), - AggregateType.AddRaw); - - var shieldRecoveryEverywhereToIsShieldRecoveryActive = context.CreateNew( - isShieldRecoveryActive, - 1f, - shieldRecoveryEverywhere, - InputOperator.Multiply, - default(AttributeDefinition?), - AggregateType.AddRaw); - - var shieldRecoveryHiatusToShieldRecoveryRampFactor = context.CreateNew( - shieldRecoveryRampFactor, - 1f / 15f, - shieldRecoveryHiatus, - InputOperator.Multiply, - default(AttributeDefinition?), - AggregateType.AddRaw); - - var shieldRecoveryRampFactorToShieldRecoveryMultiplier = context.CreateNew( - shieldRecoveryMultiplier, - 1f, - shieldRecoveryRampFactor, - InputOperator.Multiply, - default(AttributeDefinition?), - AggregateType.Multiplicate); - charClass.AttributeCombinations.Add(isRestingToHealthRecoveryMultiplier); charClass.AttributeCombinations.Add(isRestingToManaRecoveryMultiplier); charClass.AttributeCombinations.Add(isInSafezoneToAbilityRecoveryAbsolute); - charClass.AttributeCombinations.Add(isInSafezoneToIsShieldRecoveryActive); - charClass.AttributeCombinations.Add(shieldRecoveryEverywhereToIsShieldRecoveryActive); - charClass.AttributeCombinations.Add(shieldRecoveryHiatusToShieldRecoveryRampFactor); - charClass.AttributeCombinations.Add(shieldRecoveryRampFactorToShieldRecoveryMultiplier); // Remove recovery multiplier combos if (attrCombos.FirstOrDefault(ac => ac.InputAttribute == isInSafezone && ac.TargetAttribute == healthRecoveryMultiplier) is { } isInSafezoneToHealthRecoveryMultiplier) @@ -215,11 +154,6 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio attrCombos.Remove(isInSafezoneToHealthRecoveryMultiplier); } - if (attrCombos.FirstOrDefault(ac => ac.InputAttribute == isInSafezone && ac.TargetAttribute == shieldRecoveryMultiplier) is { } isInSafezoneToShieldRecoveryMultiplier) - { - attrCombos.Remove(isInSafezoneToShieldRecoveryMultiplier); - } - // Change common base attribute values (ConstValueAttribute) if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == manaRecoveryMultiplier) is { } baseManaRecoveryMultiplier) { @@ -227,15 +161,6 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio } charClass.BaseAttributeValues.Add(context.CreateNew(0.037f, manaRecoveryMultiplier, AggregateType.AddRaw)); - charClass.BaseAttributeValues.Add(context.CreateNew(4f / 3f, shieldRecoveryRampFactor, AggregateType.AddRaw)); - - if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == shieldRecoveryMultiplier) is { } baseShieldRecoveryMultiplier) - { - charClass.BaseAttributeValues.Remove(baseShieldRecoveryMultiplier); - } - - charClass.BaseAttributeValues.Add(context.CreateNew(100, shieldRecoveryMultiplier, AggregateType.AddRaw)); - charClass.BaseAttributeValues.Add(context.CreateNew(1f / 75000, shieldRecoveryMultiplier, AggregateType.Multiplicate)); // Create new StatAttributDefinitions charClass.StatAttributes.Add(context.CreateNew(isResting, 0, false)); @@ -251,30 +176,6 @@ protected override async ValueTask ApplyAsync(IContext context, GameConfiguratio charClass.BaseAttributeValues.Add(context.CreateNew(0.03f, abilityRecoveryMultiplier, AggregateType.AddRaw)); } - - // Add default movement speeds for tier 2 chars - if (charClass.Number == 16 || charClass.Number == 17 // DL classes - || charClass.Number == 12 || charClass.Number == 13 // MG classes - || charClass.Number == 24 || charClass.Number == 25) // RF classes - { - charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeed, AggregateType.Maximum)); - charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeedUnderwater, AggregateType.Maximum)); - } }); - - // Update master skills - if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SdRecoverySpeedInc)?.MasterDefinition is { } sdRecoverySpeedInc) - { - sdRecoverySpeedInc.ValueFormula = $"1 + {SkillsInitializer.FormulaRecoveryIncrease120}"; - sdRecoverySpeedInc.TargetAttribute = shieldRecoveryMultiplier; - sdRecoverySpeedInc.Aggregation = AggregateType.Multiplicate; - } - - if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.IncreaseSdRecoveryRate)?.MasterDefinition is { } increaseSdRecoveryRate) - { - increaseSdRecoveryRate.ValueFormula = $"1 + {SkillsInitializer.FormulaRecoveryIncrease120}"; - increaseSdRecoveryRate.TargetAttribute = shieldRecoveryMultiplier; - increaseSdRecoveryRate.Aggregation = AggregateType.Multiplicate; - } } } diff --git a/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInSeason6.cs b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInSeason6.cs new file mode 100644 index 0000000000..9ee506d8bc --- /dev/null +++ b/src/Persistence/Initialization/Updates/RegenerationsRefactorPlugInSeason6.cs @@ -0,0 +1,127 @@ +// +// 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.Configuration; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.Persistence.Initialization.Skills; +using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix; +using MUnique.OpenMU.PlugIns; + +/// +/// This update fixes and reworks some regeneration attributes (health, shield, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG, DL, RF). +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("E6A3B9F1-7C4D-48E2-A5B8-1F9D3C6E2A7B")] +public class RegenerationsRefactorPlugInSeason6 : RegenerationsRefactorPlugInBase +{ + /// + /// The plug in description. + /// + internal new const string PlugInDescription = "This update fixes and reworks some regeneration attributes (health, shield, mana, ability). It also adds default running (and fast swimming) speed for tier 2 chars (MG, DL, RF)."; + + /// + public override UpdateVersion Version => UpdateVersion.RegenerationsRefactorSeason6; + + /// + public override string DataInitializationKey => DataInitialization.Id; + + /// + public override string Description => PlugInDescription; + + /// + protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + await base.ApplyAsync(context, gameConfiguration).ConfigureAwait(false); + + // Create new Stats + var isShieldRecoveryActive = context.CreateNew(Stats.IsShieldRecoveryActive.Id, Stats.IsShieldRecoveryActive.Designation, Stats.IsShieldRecoveryActive.Description); + gameConfiguration.Attributes.Add(isShieldRecoveryActive); + var shieldRecoveryHiatus = context.CreateNew(Stats.ShieldRecoveryHiatus.Id, Stats.ShieldRecoveryHiatus.Designation, Stats.ShieldRecoveryHiatus.Description); + gameConfiguration.Attributes.Add(shieldRecoveryHiatus); + var shieldRecoveryRampFactor = context.CreateNew(Stats.ShieldRecoveryRampFactor.Id, Stats.ShieldRecoveryRampFactor.Designation, Stats.ShieldRecoveryRampFactor.Description); + shieldRecoveryRampFactor.MaximumValue = 3; + gameConfiguration.Attributes.Add(shieldRecoveryRampFactor); + + var shieldRecoveryMultiplier = Stats.ShieldRecoveryMultiplier.GetPersistent(gameConfiguration); + var shieldRecoveryEverywhere = Stats.ShieldRecoveryEverywhere.GetPersistent(gameConfiguration); + + var movementSpeed = Stats.MovementSpeed.GetPersistent(gameConfiguration); + var movementSpeedUnderwater = Stats.MovementSpeedUnderwater.GetPersistent(gameConfiguration); + + gameConfiguration.CharacterClasses.ForEach(charClass => + { + var attrCombos = charClass.AttributeCombinations; + + // Add new attribute combinations + var shieldRecoveryEverywhereToIsShieldRecoveryActive = context.CreateNew( + isShieldRecoveryActive, + 1f, + shieldRecoveryEverywhere, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var shieldRecoveryHiatusToShieldRecoveryRampFactor = context.CreateNew( + shieldRecoveryRampFactor, + 1f / 15f, + shieldRecoveryHiatus, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.AddRaw); + + var shieldRecoveryRampFactorToShieldRecoveryMultiplier = context.CreateNew( + shieldRecoveryMultiplier, + 1f, + shieldRecoveryRampFactor, + InputOperator.Multiply, + default(AttributeDefinition?), + AggregateType.Multiplicate); + + charClass.AttributeCombinations.Add(shieldRecoveryEverywhereToIsShieldRecoveryActive); + charClass.AttributeCombinations.Add(shieldRecoveryHiatusToShieldRecoveryRampFactor); + charClass.AttributeCombinations.Add(shieldRecoveryRampFactorToShieldRecoveryMultiplier); + + // Change common base attribute values (ConstValueAttribute) + charClass.BaseAttributeValues.Add(context.CreateNew(4f / 3f, shieldRecoveryRampFactor, AggregateType.AddRaw)); + + if (charClass.BaseAttributeValues.FirstOrDefault(bav => bav.Definition == shieldRecoveryMultiplier) is { } baseShieldRecoveryMultiplier) + { + charClass.BaseAttributeValues.Remove(baseShieldRecoveryMultiplier); + } + + charClass.BaseAttributeValues.Add(context.CreateNew(100, shieldRecoveryMultiplier, AggregateType.AddRaw)); + charClass.BaseAttributeValues.Add(context.CreateNew(1f / 75000, shieldRecoveryMultiplier, AggregateType.Multiplicate)); + + // Add default movement speeds for tier 2 chars + if (charClass.Number == 16 || charClass.Number == 17 // DL classes + || charClass.Number == 12 || charClass.Number == 13 // MG classes + || charClass.Number == 24 || charClass.Number == 25) // RF classes + { + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeed, AggregateType.Maximum)); + charClass.BaseAttributeValues.Add(context.CreateNew(MovementSpeedConstants.RunningGearMovementSpeed, movementSpeedUnderwater, AggregateType.Maximum)); + } + }); + + // Update master skills + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.SdRecoverySpeedInc)?.MasterDefinition is { } sdRecoverySpeedInc) + { + sdRecoverySpeedInc.ValueFormula = $"1 + {SkillsInitializer.FormulaRecoveryIncrease120}"; + sdRecoverySpeedInc.TargetAttribute = shieldRecoveryMultiplier; + sdRecoverySpeedInc.Aggregation = AggregateType.Multiplicate; + } + + if (gameConfiguration.Skills.FirstOrDefault(s => s.Number == (short)SkillNumber.IncreaseSdRecoveryRate)?.MasterDefinition is { } increaseSdRecoveryRate) + { + increaseSdRecoveryRate.ValueFormula = $"1 + {SkillsInitializer.FormulaRecoveryIncrease120}"; + increaseSdRecoveryRate.TargetAttribute = shieldRecoveryMultiplier; + increaseSdRecoveryRate.Aggregation = AggregateType.Multiplicate; + } + } +} diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index 000cc507a0..ae4e369bd8 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -526,7 +526,17 @@ public enum UpdateVersion FinishRageFighterMasterTree = 103, /// - /// The version of the . + /// The version of the . /// - RegenerationsRefactor = 104, + RegenerationsRefactor075 = 104, + + /// + /// The version of the . + /// + RegenerationsRefactor095d = 105, + + /// + /// The version of the . + /// + RegenerationsRefactorSeason6 = 106, } From 0a80c16b5372c56713459884142e9dfd046679de Mon Sep 17 00:00:00 2001 From: ze-dom Date: Wed, 26 Aug 2026 17:28:05 +0100 Subject: [PATCH 15/15] Added conditions for versions without shield (classic PvP) --- .../{ => PeriodicTasks}/ShieldRecoveryHiatusPlugIn.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) rename src/GameLogic/PlugIns/{ => PeriodicTasks}/ShieldRecoveryHiatusPlugIn.cs (86%) diff --git a/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs b/src/GameLogic/PlugIns/PeriodicTasks/ShieldRecoveryHiatusPlugIn.cs similarity index 86% rename from src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs rename to src/GameLogic/PlugIns/PeriodicTasks/ShieldRecoveryHiatusPlugIn.cs index af4c4486ef..b5730be7ba 100644 --- a/src/GameLogic/PlugIns/ShieldRecoveryHiatusPlugIn.cs +++ b/src/GameLogic/PlugIns/PeriodicTasks/ShieldRecoveryHiatusPlugIn.cs @@ -26,7 +26,8 @@ await gameContext.ForEachPlayerAsync(player => { if (player.SelectedCharacter != null && !player.PlayerState.CurrentState.IsDisconnectedOrFinished() - && player.Attributes is { } attributes) + && player.Attributes is { } attributes + && attributes[Stats.MaximumShield] > 0) { attributes.AddElement(new SimpleElement(1, AggregateType.AddRaw), Stats.ShieldRecoveryHiatus); } @@ -51,7 +52,7 @@ public void AttackableGotHit(IAttackable attackable, IAttacker attacker, HitInfo return; } - if (hitInfo is { ShieldDamage: > 0 }) + if (attributes[Stats.MaximumShield] > 0 && hitInfo is { ShieldDamage: > 0 }) { attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.RemoveAllElements(); } @@ -65,7 +66,8 @@ public void AttackableMoved(IAttackable attackable) return; } - if (attributes[Stats.IsShieldRecoveryActive] < 1 || attributes[Stats.CurrentShield] == attributes[Stats.MaximumShield]) + if (attributes[Stats.MaximumShield] > 0 && + (attributes[Stats.IsShieldRecoveryActive] < 1 || attributes[Stats.CurrentShield] == attributes[Stats.MaximumShield])) { attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.RemoveAllElements(); }