diff --git a/documentation/manual-test-plan.md b/documentation/manual-test-plan.md
index f07b9b117..5f8a61cba 100644
--- a/documentation/manual-test-plan.md
+++ b/documentation/manual-test-plan.md
@@ -107,6 +107,14 @@ This one is easy to eyeball — before the fix everything came back a hundred ti
## Character
+### Specialist cooldown
+
+- [ ] Untransform, relog within 30 seconds: wearing the SP again is refused with the
+ side-effect message until the window ends
+- [ ] Wait out the 30 seconds in-game: the red "side effect gone" line and the cleared
+ cooldown arrive roughly on time
+- [ ] Server restart inside the window keeps the refusal on the remaining seconds
+
### Mounts
- [ ] Mount a vehicle: the sprite changes **and** you move faster
diff --git a/src/NosCore.Database/Entities/Character.cs b/src/NosCore.Database/Entities/Character.cs
index 4bd0795ad..c2ff28dc1 100644
--- a/src/NosCore.Database/Entities/Character.cs
+++ b/src/NosCore.Database/Entities/Character.cs
@@ -4,6 +4,7 @@
// |_|\__|\__/ |___/ \__/\__/|_|_\___|
//
+using NodaTime;
using NosCore.Data.Enumerations.Character;
using NosCore.Database.Entities.Base;
using NosCore.Packets.Enumerations;
@@ -135,6 +136,8 @@ public Character()
public short MapId { get; set; }
+ public Instant? LastSp { get; set; }
+
public short MapX { get; set; }
public short MapY { get; set; }
diff --git a/src/NosCore.Database/Migrations/20260830103418_AddCharacterLastSp.Designer.cs b/src/NosCore.Database/Migrations/20260830103418_AddCharacterLastSp.Designer.cs
new file mode 100644
index 000000000..cbcde28f3
--- /dev/null
+++ b/src/NosCore.Database/Migrations/20260830103418_AddCharacterLastSp.Designer.cs
@@ -0,0 +1,4196 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NodaTime;
+using NosCore.Database;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace NosCore.Database.Migrations
+{
+ [DbContext(typeof(NosCoreContext))]
+ [Migration("20260830103418_AddCharacterLastSp")]
+ partial class AddCharacterLastSp
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.11")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "audit_log_type", new[] { "account_creation", "character_creation", "email_update" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "authority_type", new[] { "user", "moderator", "game_master", "administrator", "root", "closed", "banned", "unconfirmed" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "character_class_type", new[] { "adventurer", "swordsman", "archer", "mage", "martial_artist" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "character_relation_type", new[] { "friend", "hidden_spouse", "spouse", "blocked" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "character_state", new[] { "active", "inactive" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "element_type", new[] { "neutral", "fire", "water", "light", "dark" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "equipment_type", new[] { "main_weapon", "armor", "hat", "gloves", "boots", "secondary_weapon", "necklace", "ring", "bracelet", "mask", "fairy", "amulet", "sp", "costume_suit", "costume_hat", "weapon_skin", "wing_skin" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "family_authority", new[] { "head", "assistant", "manager", "member" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "family_authority_type", new[] { "none", "put", "all" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "family_log_type", new[] { "daily_message", "raid_won", "rainbow_battle", "family_xp", "family_level_up", "level_up", "item_upgraded", "right_changed", "authority_changed", "family_managed", "user_managed", "ware_house_added", "ware_house_removed" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "family_member_rank", new[] { "nothing", "old_uncle", "old_aunt", "father", "mother", "uncle", "aunt", "brother", "sister", "spouse", "brother2", "sister2", "old_son", "old_daugter", "middle_son", "middle_daughter", "young_son", "young_daugter", "old_little_son", "old_little_daughter", "little_son", "little_daughter", "middle_little_son", "middle_little_daugter" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "gender_type", new[] { "male", "female" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "hair_color_type", new[] { "dark_purple", "yellow", "blue", "purple", "orange", "brown", "green", "dark_grey", "light_blue", "pink_red", "light_yellow", "light_pink", "light_green", "light_grey", "sky_blue", "black", "dark_orange", "dark_orange_variant2", "dark_orange_variant3", "dark_orange_variant4", "dark_orange_variant5", "dark_orange_variant6", "light_orange", "light_light_orange", "light_light_light_orange", "light_light_light_light_orange", "super_light_orange", "dark_yellow", "light_light_yellow", "kaki_yellow", "super_light_yellow", "super_light_yellow2", "super_light_yellow3", "little_dark_yellow", "yellow_variant", "yellow_variant1", "yellow_variant2", "yellow_variant3", "yellow_variant4", "yellow_variant5", "yellow_variant6", "yellow_variant7", "yellow_variant8", "yellow_variant9", "green_variant", "green_variant1", "dark_green_variant", "green_more_dark_variant", "green_variant2", "green_variant3", "green_variant4", "green_variant5", "green_variant6", "green_variant7", "green_variant8", "green_variant9", "green_variant10", "green_variant11", "green_variant12", "green_variant13", "green_variant14", "green_variant15", "green_variant16", "green_variant17", "green_variant18", "green_variant19", "green_variant20", "light_blue_variant1", "light_blue_variant2", "light_blue_variant3", "light_blue_variant4", "light_blue_variant5", "light_blue_variant6", "light_blue_variant7", "light_blue_variant8", "light_blue_variant9", "light_blue_variant10", "light_blue_variant11", "light_blue_variant12", "light_blue_variant13", "dark_black", "light_blue_variant14", "light_blue_variant15", "light_blue_variant16", "light_blue_variant17", "blue_variant", "blue_variant_dark", "blue_variant_dark_dark", "blue_variant_dark_dark2", "flash_blue", "flash_blue_dark", "flash_blue_dark2", "flash_blue_dark3", "flash_blue_dark4", "flash_blue_dark5", "flash_blue_dark6", "flash_blue_dark7", "flash_blue_dark8", "flash_blue_dark9", "white", "flash_blue_dark10", "flash_blue1", "flash_blue2", "flash_blue3", "flash_blue4", "flash_blue5", "flash_purple", "flash_light_purple", "flash_light_purple2", "flash_light_purple3", "flash_light_purple4", "flash_light_purple5", "light_purple", "purple_variant1", "purple_variant2", "purple_variant3", "purple_variant4", "purple_variant5", "purple_variant6", "purple_variant7", "purple_variant8", "purple_variant9", "purple_variant10", "purple_variant11", "purple_variant12", "purple_variant13", "purple_variant14", "purple_variant15" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "hair_style_type", new[] { "hair_style_a", "hair_style_b", "hair_style_c", "hair_style_d", "no_hair" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "item_effect_type", new[] { "no_effect", "teleport", "apply_hair_die", "apply_hair_style", "speaker", "marriage_proposal", "undefined", "sp_charger", "mate_capture", "dropped_sp_recharger", "premium_sp_recharger", "crafted_sp_recharger", "specialist_medal", "apply_skin_partner", "change_gender", "point_initialisation", "sealed_tarot_card", "tarot_card", "red_amulet", "blue_amulet", "reinforcement_amulet", "heroic", "random_heroic", "attack_amulet", "defense_amulet", "speed_booster", "box_effect", "vehicle", "gold_nos_merchant_upgrade", "silver_nos_merchant_upgrade", "inventory_upgrade", "pet_space_upgrade", "pet_basket_upgrade", "pet_backpack_upgrade", "inventory_ticket_upgrade", "buff_potions", "marriage_separation" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "item_type", new[] { "weapon", "armor", "fashion", "jewelery", "specialist", "box", "shell", "main", "upgrade", "production", "map", "special", "potion", "event", "title", "quest1", "sell", "food", "snack", "magical", "part", "teacher", "ammo", "quest2", "house", "garden", "minigame", "terrace", "miniland_theme" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "mate_type", new[] { "partner", "pet" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "miniland_state", new[] { "open", "private", "lock" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "monster_type", new[] { "unknown", "partner", "npc", "well", "portal", "boss", "elite", "peapod", "special", "gem_space_time" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "noscore_pocket_type", new[] { "equipment", "main", "etc", "miniland", "specialist", "costume", "wear", "mount", "raid" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "penalty_type", new[] { "muted", "banned", "block_exp", "block_f_exp", "block_rep", "warning" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "portal_type", new[] { "ts_normal", "closed", "open", "miniland", "ts_end", "ts_end_closed", "exit", "exit_closed", "raid", "effect", "blue_raid", "dark_raid", "time_space", "shop_teleport", "map_portal" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "quest_type", new[] { "hunt", "special_collect", "collect_in_raid", "brings", "capture_without_getting_the_monster", "capture", "times_space", "product", "number_of_kill", "target_reput", "ts_point", "dialog1", "collect_in_ts", "required", "wear", "needed", "collect", "transmit_gold", "go_to", "collect_map_entity", "use", "dialog2", "un_know", "inspect", "win_raid", "flower_quest" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "region_type", new[] { "en", "de", "fr", "it", "pl", "es", "ru", "cs", "tr" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "scripted_instance_type", new[] { "time_space", "raid", "raid_act4" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "static_bonus_type", new[] { "bazaar_medal_gold", "bazaar_medal_silver", "back_pack", "pet_basket", "pet_back_pack", "inventory_ticket_upgrade" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "teleporter_type", new[] { "teleporter", "teleporter_on_map" });
+ NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "warehouse_type", new[] { "warehouse", "family_ware_house", "pet_warehouse" });
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("NosCore.Database.Entities.Account", b =>
+ {
+ b.Property("AccountId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("AccountId"));
+
+ b.Property("Authority")
+ .HasColumnType("smallint");
+
+ b.Property("BankMoney")
+ .HasColumnType("bigint");
+
+ b.Property("Email")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ItemShopMoney")
+ .HasColumnType("bigint");
+
+ b.Property("Language")
+ .HasColumnType("integer");
+
+ b.Property("MfaSecret")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("NewAuthPassword")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("NewAuthSalt")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Password")
+ .HasMaxLength(255)
+ .IsUnicode(false)
+ .HasColumnType("character varying(255)");
+
+ b.Property("RegistrationIp")
+ .HasMaxLength(45)
+ .HasColumnType("character varying(45)");
+
+ b.Property("VerificationToken")
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.HasKey("AccountId");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Account");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Act", b =>
+ {
+ b.Property("ActId")
+ .HasColumnType("smallint");
+
+ b.Property("Scene")
+ .HasColumnType("smallint");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("ActId");
+
+ b.ToTable("Act");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.ActPart", b =>
+ {
+ b.Property("ActPartId")
+ .HasColumnType("smallint");
+
+ b.Property("ActId")
+ .HasColumnType("smallint");
+
+ b.Property("ActPartNumber")
+ .HasColumnType("smallint");
+
+ b.Property("MaxTs")
+ .HasColumnType("smallint");
+
+ b.HasKey("ActPartId");
+
+ b.HasIndex("ActId");
+
+ b.ToTable("ActPart");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.AuditLog", b =>
+ {
+ b.Property("AuditId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AuditLogType")
+ .HasColumnType("integer");
+
+ b.Property("TargetId")
+ .IsRequired()
+ .HasMaxLength(80)
+ .HasColumnType("character varying(80)");
+
+ b.Property("TargetType")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("character varying(32)");
+
+ b.Property("Time")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.HasKey("AuditId");
+
+ b.ToTable("AuditLog");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.BCard", b =>
+ {
+ b.Property("BCardId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BCardId"));
+
+ b.Property("CardId")
+ .HasColumnType("smallint");
+
+ b.Property("CastType")
+ .HasColumnType("smallint");
+
+ b.Property("FirstData")
+ .HasColumnType("integer");
+
+ b.Property("IsLevelDivided")
+ .HasColumnType("boolean");
+
+ b.Property("IsLevelScaled")
+ .HasColumnType("boolean");
+
+ b.Property("ItemVNum")
+ .HasColumnType("smallint");
+
+ b.Property("NpcMonsterVNum")
+ .HasColumnType("smallint");
+
+ b.Property("SecondData")
+ .HasColumnType("integer");
+
+ b.Property("SkillVNum")
+ .HasColumnType("smallint");
+
+ b.Property("Slot")
+ .HasColumnType("smallint");
+
+ b.Property("SubType")
+ .HasColumnType("smallint");
+
+ b.Property("ThirdData")
+ .HasColumnType("integer");
+
+ b.Property("Type")
+ .HasColumnType("smallint");
+
+ b.HasKey("BCardId");
+
+ b.HasIndex("CardId");
+
+ b.HasIndex("ItemVNum");
+
+ b.HasIndex("NpcMonsterVNum");
+
+ b.HasIndex("SkillVNum");
+
+ b.ToTable("BCard");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.BazaarItem", b =>
+ {
+ b.Property("BazaarItemId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BazaarItemId"));
+
+ b.Property("Amount")
+ .HasColumnType("smallint");
+
+ b.Property("DateStart")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Duration")
+ .HasColumnType("smallint");
+
+ b.Property("IsPackage")
+ .HasColumnType("boolean");
+
+ b.Property("ItemInstanceId")
+ .HasColumnType("uuid");
+
+ b.Property("MedalUsed")
+ .HasColumnType("boolean");
+
+ b.Property("Price")
+ .HasColumnType("bigint");
+
+ b.Property("SellerId")
+ .HasColumnType("bigint");
+
+ b.HasKey("BazaarItemId");
+
+ b.HasIndex("ItemInstanceId");
+
+ b.HasIndex("SellerId");
+
+ b.ToTable("BazaarItem");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Card", b =>
+ {
+ b.Property("CardId")
+ .HasColumnType("smallint");
+
+ b.Property("BuffType")
+ .HasColumnType("smallint");
+
+ b.Property("Delay")
+ .HasColumnType("integer");
+
+ b.Property("Duration")
+ .HasColumnType("integer");
+
+ b.Property("EffectId")
+ .HasColumnType("integer");
+
+ b.Property("Level")
+ .HasColumnType("smallint");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Propability")
+ .HasColumnType("smallint");
+
+ b.Property("TimeoutBuff")
+ .HasColumnType("smallint");
+
+ b.Property("TimeoutBuffChance")
+ .HasColumnType("smallint");
+
+ b.HasKey("CardId");
+
+ b.ToTable("Card");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Character", b =>
+ {
+ b.Property("CharacterId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CharacterId"));
+
+ b.Property("AccountId")
+ .HasColumnType("bigint");
+
+ b.Property("Act4Dead")
+ .HasColumnType("integer");
+
+ b.Property("Act4Kill")
+ .HasColumnType("integer");
+
+ b.Property("Act4Points")
+ .HasColumnType("integer");
+
+ b.Property("ArenaWinner")
+ .HasColumnType("integer");
+
+ b.Property("Biography")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("BuffBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("Class")
+ .HasColumnType("smallint");
+
+ b.Property("Compliment")
+ .HasColumnType("smallint");
+
+ b.Property("CurrentScriptId")
+ .HasColumnType("uuid");
+
+ b.Property("Dignity")
+ .HasColumnType("smallint");
+
+ b.Property("Elo")
+ .HasColumnType("integer");
+
+ b.Property("EmoticonsBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("ExchangeBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("Faction")
+ .HasColumnType("smallint");
+
+ b.Property("FamilyRequestBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("FriendRequestBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("Gender")
+ .HasColumnType("smallint");
+
+ b.Property("Gold")
+ .HasColumnType("bigint");
+
+ b.Property("GroupRequestBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("HairColor")
+ .HasColumnType("smallint");
+
+ b.Property("HairStyle")
+ .HasColumnType("smallint");
+
+ b.Property("HeroChatBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("HeroLevel")
+ .HasColumnType("smallint");
+
+ b.Property("HeroXp")
+ .HasColumnType("bigint");
+
+ b.Property("Hp")
+ .HasColumnType("integer");
+
+ b.Property("HpBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("JobLevel")
+ .HasColumnType("smallint");
+
+ b.Property("JobLevelXp")
+ .HasColumnType("bigint");
+
+ b.Property("LastSp")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Level")
+ .HasColumnType("smallint");
+
+ b.Property("LevelXp")
+ .HasColumnType("bigint");
+
+ b.Property("MapId")
+ .HasColumnType("smallint");
+
+ b.Property("MapX")
+ .HasColumnType("smallint");
+
+ b.Property("MapY")
+ .HasColumnType("smallint");
+
+ b.Property("MasterPoints")
+ .HasColumnType("integer");
+
+ b.Property("MasterTicket")
+ .HasColumnType("integer");
+
+ b.Property("MaxMateCount")
+ .HasColumnType("smallint");
+
+ b.Property("MinilandInviteBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("MouseAimLock")
+ .HasColumnType("boolean");
+
+ b.Property("Mp")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .IsUnicode(false)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Prefix")
+ .HasMaxLength(25)
+ .HasColumnType("character varying(25)");
+
+ b.Property("QuickGetUp")
+ .HasColumnType("boolean");
+
+ b.Property("RagePoint")
+ .HasColumnType("bigint");
+
+ b.Property("Reput")
+ .HasColumnType("bigint");
+
+ b.Property("ServerId")
+ .HasColumnType("smallint");
+
+ b.Property("ShouldRename")
+ .HasColumnType("boolean");
+
+ b.Property("Slot")
+ .HasColumnType("smallint");
+
+ b.Property("SpAdditionPoint")
+ .HasColumnType("integer");
+
+ b.Property("SpPoint")
+ .HasColumnType("integer");
+
+ b.Property("State")
+ .HasColumnType("smallint");
+
+ b.Property("TalentLose")
+ .HasColumnType("integer");
+
+ b.Property("TalentSurrender")
+ .HasColumnType("integer");
+
+ b.Property("TalentWin")
+ .HasColumnType("integer");
+
+ b.Property("WhisperBlocked")
+ .HasColumnType("boolean");
+
+ b.HasKey("CharacterId");
+
+ b.HasIndex("AccountId");
+
+ b.HasIndex("CurrentScriptId");
+
+ b.HasIndex("MapId");
+
+ b.ToTable("Character");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.CharacterActPart", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("ActPartId")
+ .HasColumnType("smallint");
+
+ b.Property("CharacterId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ActPartId");
+
+ b.HasIndex("CharacterId");
+
+ b.ToTable("CharacterActPart");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.CharacterQuest", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CharacterId")
+ .HasColumnType("bigint");
+
+ b.Property("CompletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("QuestId")
+ .HasColumnType("smallint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CharacterId");
+
+ b.HasIndex("QuestId");
+
+ b.ToTable("CharacterQuest");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.CharacterQuestObjective", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CharacterQuestId")
+ .HasColumnType("uuid");
+
+ b.Property("Count")
+ .HasColumnType("integer");
+
+ b.Property("QuestObjectiveId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("QuestObjectiveId");
+
+ b.HasIndex("CharacterQuestId", "QuestObjectiveId")
+ .IsUnique();
+
+ b.ToTable("CharacterQuestObjective");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.CharacterRelation", b =>
+ {
+ b.Property("CharacterRelationId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CharacterId")
+ .HasColumnType("bigint");
+
+ b.Property("RelatedCharacterId")
+ .HasColumnType("bigint");
+
+ b.Property("RelationType")
+ .HasColumnType("smallint");
+
+ b.HasKey("CharacterRelationId");
+
+ b.HasIndex("CharacterId");
+
+ b.HasIndex("RelatedCharacterId");
+
+ b.ToTable("CharacterRelation");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.CharacterSkill", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CharacterId")
+ .HasColumnType("bigint");
+
+ b.Property("SkillVNum")
+ .HasColumnType("smallint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CharacterId");
+
+ b.HasIndex("SkillVNum");
+
+ b.ToTable("CharacterSkill");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Combo", b =>
+ {
+ b.Property("ComboId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ComboId"));
+
+ b.Property("Animation")
+ .HasColumnType("smallint");
+
+ b.Property("Effect")
+ .HasColumnType("smallint");
+
+ b.Property("Hit")
+ .HasColumnType("smallint");
+
+ b.Property("SkillVNum")
+ .HasColumnType("smallint");
+
+ b.HasKey("ComboId");
+
+ b.HasIndex("SkillVNum");
+
+ b.ToTable("Combo");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.DignityLevel", b =>
+ {
+ b.Property("DignityLevelId")
+ .HasColumnType("smallint");
+
+ b.Property("MaxDignity")
+ .HasColumnType("smallint");
+
+ b.HasKey("DignityLevelId");
+
+ b.ToTable("DignityLevel");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Drop", b =>
+ {
+ b.Property("DropId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("smallint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("DropId"));
+
+ b.Property("Amount")
+ .HasColumnType("integer");
+
+ b.Property("DropChance")
+ .HasColumnType("integer");
+
+ b.Property("MapTypeId")
+ .HasColumnType("smallint");
+
+ b.Property("MonsterVNum")
+ .HasColumnType("smallint");
+
+ b.Property("VNum")
+ .HasColumnType("smallint");
+
+ b.HasKey("DropId");
+
+ b.HasIndex("MapTypeId");
+
+ b.HasIndex("MonsterVNum");
+
+ b.HasIndex("VNum");
+
+ b.ToTable("Drop");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.EquipmentOption", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("Level")
+ .HasColumnType("smallint");
+
+ b.Property("Type")
+ .HasColumnType("smallint");
+
+ b.Property("Value")
+ .HasColumnType("integer");
+
+ b.Property("WearableInstanceId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.HasIndex("WearableInstanceId");
+
+ b.ToTable("EquipmentOption");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Family", b =>
+ {
+ b.Property("FamilyId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("FamilyId"));
+
+ b.Property("FamilyExperience")
+ .HasColumnType("integer");
+
+ b.Property("FamilyFaction")
+ .HasColumnType("smallint");
+
+ b.Property("FamilyHeadGender")
+ .HasColumnType("smallint");
+
+ b.Property("FamilyLevel")
+ .HasColumnType("smallint");
+
+ b.Property("FamilyMessage")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("ManagerAuthorityType")
+ .HasColumnType("integer");
+
+ b.Property("ManagerCanGetHistory")
+ .HasColumnType("boolean");
+
+ b.Property("ManagerCanInvite")
+ .HasColumnType("boolean");
+
+ b.Property("ManagerCanNotice")
+ .HasColumnType("boolean");
+
+ b.Property("ManagerCanShout")
+ .HasColumnType("boolean");
+
+ b.Property("MaxSize")
+ .HasColumnType("smallint");
+
+ b.Property("MemberAuthorityType")
+ .HasColumnType("integer");
+
+ b.Property("MemberCanGetHistory")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("WarehouseSize")
+ .HasColumnType("smallint");
+
+ b.HasKey("FamilyId");
+
+ b.ToTable("Family");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.FamilyCharacter", b =>
+ {
+ b.Property("FamilyCharacterId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("FamilyCharacterId"));
+
+ b.Property("Authority")
+ .HasColumnType("smallint");
+
+ b.Property("CharacterId")
+ .HasColumnType("bigint");
+
+ b.Property("DailyMessage")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("Experience")
+ .HasColumnType("integer");
+
+ b.Property("FamilyId")
+ .HasColumnType("bigint");
+
+ b.Property("Rank")
+ .HasColumnType("smallint");
+
+ b.HasKey("FamilyCharacterId");
+
+ b.HasIndex("CharacterId");
+
+ b.HasIndex("FamilyId");
+
+ b.ToTable("FamilyCharacter");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.FamilyLog", b =>
+ {
+ b.Property("FamilyLogId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("FamilyLogId"));
+
+ b.Property("FamilyId")
+ .HasColumnType("bigint");
+
+ b.Property("FamilyLogData")
+ .HasMaxLength(255)
+ .HasColumnType("character varying(255)");
+
+ b.Property("FamilyLogType")
+ .HasColumnType("smallint");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("FamilyLogId");
+
+ b.HasIndex("FamilyId");
+
+ b.ToTable("FamilyLog");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NActDesc", b =>
+ {
+ b.Property("I18NActDescId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NActDescId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NActDescId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NActDesc");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NBCard", b =>
+ {
+ b.Property("I18NbCardId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NbCardId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NbCardId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NBCard");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NCard", b =>
+ {
+ b.Property("I18NCardId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NCardId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NCardId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NCard");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NItem", b =>
+ {
+ b.Property("I18NItemId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NItemId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NItemId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NItem");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NMapIdData", b =>
+ {
+ b.Property("I18NMapIdDataId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NMapIdDataId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NMapIdDataId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NMapIdData");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NMapPointData", b =>
+ {
+ b.Property("I18NMapPointDataId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NMapPointDataId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NMapPointDataId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NMapPointData");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NNpcMonster", b =>
+ {
+ b.Property("I18NNpcMonsterId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NNpcMonsterId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NNpcMonsterId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NNpcMonster");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NNpcMonsterTalk", b =>
+ {
+ b.Property("I18NNpcMonsterTalkId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NNpcMonsterTalkId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NNpcMonsterTalkId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NNpcMonsterTalk");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NQuest", b =>
+ {
+ b.Property("I18NQuestId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NQuestId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NQuestId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NQuest");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.I18NSkill", b =>
+ {
+ b.Property("I18NSkillId")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("I18NSkillId"));
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RegionType")
+ .HasColumnType("integer");
+
+ b.Property("Text")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("I18NSkillId");
+
+ b.HasIndex("Key", "RegionType")
+ .IsUnique();
+
+ b.ToTable("I18NSkill");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.InventoryItemInstance", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("uuid");
+
+ b.Property("CharacterId")
+ .HasColumnType("bigint");
+
+ b.Property("ItemInstanceId")
+ .HasColumnType("uuid");
+
+ b.Property("Slot")
+ .HasColumnType("smallint");
+
+ b.Property("Type")
+ .HasColumnType("smallint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ItemInstanceId");
+
+ b.HasIndex("CharacterId", "Slot", "Type")
+ .IsUnique();
+
+ b.ToTable("InventoryItemInstance");
+ });
+
+ modelBuilder.Entity("NosCore.Database.Entities.Item", b =>
+ {
+ b.Property("VNum")
+ .HasColumnType("smallint");
+
+ b.Property("BasicUpgrade")
+ .HasColumnType("smallint");
+
+ b.Property("CellonLvl")
+ .HasColumnType("smallint");
+
+ b.Property("Class")
+ .HasColumnType("smallint");
+
+ b.Property("CloseDefence")
+ .HasColumnType("smallint");
+
+ b.Property("Color")
+ .HasColumnType("smallint");
+
+ b.Property("Concentrate")
+ .HasColumnType("smallint");
+
+ b.Property("CriticalLuckRate")
+ .HasColumnType("smallint");
+
+ b.Property("CriticalRate")
+ .HasColumnType("smallint");
+
+ b.Property("DamageMaximum")
+ .HasColumnType("smallint");
+
+ b.Property