diff --git a/src/AttributeSystem/ComposableAttribute.cs b/src/AttributeSystem/ComposableAttribute.cs
index 0f27f78f37..bafd2661cb 100644
--- a/src/AttributeSystem/ComposableAttribute.cs
+++ b/src/AttributeSystem/ComposableAttribute.cs
@@ -105,6 +105,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()
{
// Aggregate a copy-on-write snapshot, so a concurrent AddElement/RemoveElement (e.g. an expiring
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..5dabae4096 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).
@@ -1437,7 +1461,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 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 shield and additional ability.");
+
+ ///
+ /// 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.");
///
/// Gets the attribute which defines if the character is located on an underwater game map.
@@ -1588,13 +1617,43 @@ 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.
+ ///
+ ///
+ /// 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),
+ IntervalResting = TimeSpan.FromSeconds(1),
+ EnablerAttribute = IsShieldRecoveryActive,
+ HiatusAttribute = ShieldRecoveryHiatus,
+ };
private static Regeneration ManaRegenerationAfterMonsterKill { get; } = new(ManaAfterMonsterKillMultiplier, MaximumMana, CurrentMana, ManaAfterMonsterKillAbsolute);
@@ -1613,8 +1672,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.
@@ -1647,5 +1706,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/Player.cs b/src/GameLogic/Player.cs
index 9c4d32c357..3e7f51a51e 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;
@@ -77,6 +76,14 @@ public class Player : AsyncDisposable, IBucketMapObserver, IAttackable, IAttacke
private DateTime _lastRegenerate = 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;
private IDisposable? _accountLoggingScope;
@@ -240,8 +247,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;
}
@@ -876,28 +882,42 @@ public async Task RegenerateAsync()
{
try
{
- var attributes = this.Attributes;
- if (attributes is null)
+ if (this.Attributes is not { } attributes)
{
return;
}
- 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.CurrentAttribute == Stats.CurrentShield && !this.IsAtSafezone() &&
- attributes[Stats.ShieldRecoveryEverywhere] < 1)
+ if ((r.EnablerAttribute is { } enabler && attributes[enabler] < 1)
+ || (r.HiatusAttribute is { } hiatus && attributes[hiatus] < r.HiatusThreshold))
{
- // 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.
+ this._lastRegeneration[r] = now;
continue;
}
+ var factor = 0f;
+ var interval = r.Interval;
+ if (attributes[Stats.IsResting] > 0)
+ {
+ interval = r.IntervalResting;
+
+ if (r.CurrentAttribute == Stats.CurrentMana)
+ {
+ // Mana recovery while resting is on top of regular recovery
+ factor += (float)((now - this._lastRegeneration[r]) / r.Interval);
+ }
+ }
+
+ factor += (float)((now - this._lastRegeneration[r]) / interval);
+
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]) * factor),
attributes[r.MaximumAttribute]);
+
+ this._lastRegeneration[r] = now;
}
await this.RegenerateHeroStateAsync().ConfigureAwait(false);
@@ -1202,6 +1222,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];
+ }
+ }
+
///
/// Sets the current map without raising the enter/leave map events, when the player is
/// removed from the game.
@@ -1594,7 +1625,7 @@ private async ValueTask OnPlayerEnteredWorldAsync()
this.AddMissingStatAttributes();
this.Attributes = new ItemAwareAttributeSystem(this.Account!, selectedCharacter, this.GameContext.Configuration);
- this.Attributes[Stats.NearbyPartyMemberCount] = 0;
+ this.Attributes[Stats.IsResting] = 0;
this.LogInvalidInventoryItems();
this._storages.CreateForCharacter(selectedCharacter);
@@ -1669,17 +1700,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/PeriodicTasks/ShieldRecoveryHiatusPlugIn.cs b/src/GameLogic/PlugIns/PeriodicTasks/ShieldRecoveryHiatusPlugIn.cs
new file mode 100644
index 0000000000..b5730be7ba
--- /dev/null
+++ b/src/GameLogic/PlugIns/PeriodicTasks/ShieldRecoveryHiatusPlugIn.cs
@@ -0,0 +1,75 @@
+//
+// 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.AttributeSystem;
+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.MaximumShield] > 0)
+ {
+ attributes.AddElement(new SimpleElement(1, AggregateType.AddRaw), Stats.ShieldRecoveryHiatus);
+ }
+
+ 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 || defender.Attributes is not { } attributes)
+ {
+ return;
+ }
+
+ if (attributes[Stats.MaximumShield] > 0 && hitInfo is { ShieldDamage: > 0 })
+ {
+ attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.RemoveAllElements();
+ }
+ }
+
+ ///
+ public void AttackableMoved(IAttackable attackable)
+ {
+ if (attackable is not Player player || player.Attributes is not { } attributes)
+ {
+ return;
+ }
+
+ if (attributes[Stats.MaximumShield] > 0 &&
+ (attributes[Stats.IsShieldRecoveryActive] < 1 || attributes[Stats.CurrentShield] == attributes[Stats.MaximumShield]))
+ {
+ attributes.GetComposableAttribute(Stats.ShieldRecoveryHiatus)?.RemoveAllElements();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs b/src/GameLogic/PlugIns/UpdateIsInSafezoneAfterPlayerMoved.cs
index ca09af0346..c8958bf84f 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 or increase shield, ability, health and mana recoveries.
///
[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/GameLogic/Properties/PlugInResources.Designer.cs b/src/GameLogic/Properties/PlugInResources.Designer.cs
index dc41811fa6..4f8c0eb696 100644
--- a/src/GameLogic/Properties/PlugInResources.Designer.cs
+++ b/src/GameLogic/Properties/PlugInResources.Designer.cs
@@ -2501,6 +2501,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 26953515c4..105249bd0b 100644
--- a/src/GameLogic/Properties/PlugInResources.resx
+++ b/src/GameLogic/Properties/PlugInResources.resx
@@ -177,6 +177,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/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 73c227137a..dd05e4ef71 100644
--- a/src/Persistence/EntityFramework/EntityDataContext.cs
+++ b/src/Persistence/EntityFramework/EntityDataContext.cs
@@ -74,7 +74,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