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("DamageMinimum") + .HasColumnType("smallint"); + + b.Property("DarkElement") + .HasColumnType("smallint"); + + b.Property("DarkResistance") + .HasColumnType("smallint"); + + b.Property("DefenceDodge") + .HasColumnType("smallint"); + + b.Property("DistanceDefence") + .HasColumnType("smallint"); + + b.Property("DistanceDefenceDodge") + .HasColumnType("smallint"); + + b.Property("Effect") + .HasColumnType("integer"); + + b.Property("EffectValue") + .HasColumnType("integer"); + + b.Property("Element") + .HasColumnType("integer"); + + b.Property("ElementRate") + .HasColumnType("smallint"); + + b.Property("EquipmentSlot") + .HasColumnType("smallint"); + + b.Property("FireElement") + .HasColumnType("smallint"); + + b.Property("FireResistance") + .HasColumnType("smallint"); + + b.Property("Flag1") + .HasColumnType("boolean"); + + b.Property("Flag2") + .HasColumnType("boolean"); + + b.Property("Flag3") + .HasColumnType("boolean"); + + b.Property("Flag4") + .HasColumnType("boolean"); + + b.Property("Flag6") + .HasColumnType("boolean"); + + b.Property("Flag7") + .HasColumnType("boolean"); + + b.Property("Flag8") + .HasColumnType("boolean"); + + b.Property("Flag9") + .HasColumnType("boolean"); + + b.Property("Height") + .HasColumnType("smallint"); + + b.Property("HitRate") + .HasColumnType("smallint"); + + b.Property("Hp") + .HasColumnType("smallint"); + + b.Property("HpRegeneration") + .HasColumnType("smallint"); + + b.Property("IsColored") + .HasColumnType("boolean"); + + b.Property("IsConsumable") + .HasColumnType("boolean"); + + b.Property("IsDroppable") + .HasColumnType("boolean"); + + b.Property("IsHeroic") + .HasColumnType("boolean"); + + b.Property("IsMinilandActionable") + .HasColumnType("boolean"); + + b.Property("IsSoldable") + .HasColumnType("boolean"); + + b.Property("IsTradable") + .HasColumnType("boolean"); + + b.Property("IsWarehouse") + .HasColumnType("boolean"); + + b.Property("ItemSubType") + .HasColumnType("smallint"); + + b.Property("ItemType") + .HasColumnType("smallint"); + + b.Property("ItemValidTime") + .HasColumnType("bigint"); + + b.Property("LevelJobMinimum") + .HasColumnType("smallint"); + + b.Property("LevelMinimum") + .HasColumnType("smallint"); + + b.Property("LightElement") + .HasColumnType("smallint"); + + b.Property("LightResistance") + .HasColumnType("smallint"); + + b.Property("MagicDefence") + .HasColumnType("smallint"); + + b.Property("MaxCellon") + .HasColumnType("smallint"); + + b.Property("MaxCellonLvl") + .HasColumnType("smallint"); + + b.Property("MaxElementRate") + .HasColumnType("smallint"); + + b.Property("MaximumAmmo") + .HasColumnType("smallint"); + + b.Property("MinilandObjectPoint") + .HasColumnType("integer"); + + b.Property("MoreHp") + .HasColumnType("smallint"); + + b.Property("MoreMp") + .HasColumnType("smallint"); + + b.Property("Morph") + .HasColumnType("smallint"); + + b.Property("Mp") + .HasColumnType("smallint"); + + b.Property("MpRegeneration") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.Property("PvpDefence") + .HasColumnType("smallint"); + + b.Property("PvpStrength") + .HasColumnType("smallint"); + + b.Property("ReduceOposantResistance") + .HasColumnType("smallint"); + + b.Property("ReputPrice") + .HasColumnType("bigint"); + + b.Property("ReputationMinimum") + .HasColumnType("smallint"); + + b.Property("RequireBinding") + .HasColumnType("boolean"); + + b.Property("SecondMorph") + .HasColumnType("smallint"); + + b.Property("SecondaryElement") + .HasColumnType("integer"); + + b.Property("Sex") + .HasColumnType("smallint"); + + b.Property("SpType") + .HasColumnType("smallint"); + + b.Property("Speed") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("WaitDelay") + .HasColumnType("smallint"); + + b.Property("WaterElement") + .HasColumnType("smallint"); + + b.Property("WaterResistance") + .HasColumnType("smallint"); + + b.Property("Width") + .HasColumnType("smallint"); + + b.HasKey("VNum"); + + b.ToTable("Item"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ItemInstance", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("smallint"); + + b.Property("BoundCharacterId") + .HasColumnType("bigint"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Design") + .HasColumnType("smallint"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(21) + .HasColumnType("character varying(21)"); + + b.Property("DurabilityPoint") + .HasColumnType("integer"); + + b.Property("ItemDeleteTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ItemVNum") + .HasColumnType("smallint"); + + b.Property("Rare") + .HasColumnType("smallint"); + + b.Property("Upgrade") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("BoundCharacterId"); + + b.HasIndex("ItemVNum"); + + b.ToTable("ItemInstance"); + + b.HasDiscriminator("Discriminator").HasValue("ItemInstance"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Mail", b => + { + b.Property("MailId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("MailId")); + + b.Property("Armor") + .HasColumnType("smallint"); + + b.Property("CostumeHat") + .HasColumnType("smallint"); + + b.Property("CostumeSuit") + .HasColumnType("smallint"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Fairy") + .HasColumnType("smallint"); + + b.Property("Hat") + .HasColumnType("smallint"); + + b.Property("IsOpened") + .HasColumnType("boolean"); + + b.Property("IsSenderCopy") + .HasColumnType("boolean"); + + b.Property("ItemInstanceId") + .HasColumnType("uuid"); + + b.Property("MainWeapon") + .HasColumnType("smallint"); + + b.Property("Mask") + .HasColumnType("smallint"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ReceiverId") + .HasColumnType("bigint"); + + b.Property("SecondaryWeapon") + .HasColumnType("smallint"); + + b.Property("SenderCharacterClass") + .HasColumnType("smallint"); + + b.Property("SenderGender") + .HasColumnType("smallint"); + + b.Property("SenderHairColor") + .HasColumnType("smallint"); + + b.Property("SenderHairStyle") + .HasColumnType("smallint"); + + b.Property("SenderId") + .HasColumnType("bigint"); + + b.Property("SenderMorphId") + .HasColumnType("smallint"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("WeaponSkin") + .HasColumnType("smallint"); + + b.Property("WingSkin") + .HasColumnType("smallint"); + + b.HasKey("MailId"); + + b.HasIndex("ItemInstanceId"); + + b.HasIndex("ReceiverId"); + + b.HasIndex("SenderId"); + + b.ToTable("Mail"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Map", b => + { + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Music") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ShopAllowed") + .HasColumnType("boolean"); + + b.HasKey("MapId"); + + b.ToTable("Map"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapMonster", b => + { + b.Property("MapMonsterId") + .HasColumnType("integer"); + + b.Property("Direction") + .HasColumnType("smallint"); + + b.Property("IsDisabled") + .HasColumnType("boolean"); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("MapX") + .HasColumnType("smallint"); + + b.Property("MapY") + .HasColumnType("smallint"); + + b.Property("VNum") + .HasColumnType("smallint"); + + b.HasKey("MapMonsterId"); + + b.HasIndex("MapId"); + + b.HasIndex("VNum"); + + b.ToTable("MapMonster"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapNpc", b => + { + b.Property("MapNpcId") + .HasColumnType("integer"); + + b.Property("Dialog") + .HasColumnType("smallint"); + + b.Property("Direction") + .HasColumnType("smallint"); + + b.Property("Effect") + .HasColumnType("smallint"); + + b.Property("EffectDelay") + .HasColumnType("smallint"); + + b.Property("IsDisabled") + .HasColumnType("boolean"); + + b.Property("IsMoving") + .HasColumnType("boolean"); + + b.Property("IsSitting") + .HasColumnType("boolean"); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("MapX") + .HasColumnType("smallint"); + + b.Property("MapY") + .HasColumnType("smallint"); + + b.Property("VNum") + .HasColumnType("smallint"); + + b.HasKey("MapNpcId"); + + b.HasIndex("Dialog"); + + b.HasIndex("MapId"); + + b.HasIndex("VNum"); + + b.ToTable("MapNpc"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapType", b => + { + b.Property("MapTypeId") + .HasColumnType("smallint"); + + b.Property("MapTypeName") + .IsRequired() + .HasColumnType("text"); + + b.Property("PotionDelay") + .HasColumnType("smallint"); + + b.Property("RespawnMapTypeId") + .HasColumnType("bigint"); + + b.Property("ReturnMapTypeId") + .HasColumnType("bigint"); + + b.HasKey("MapTypeId"); + + b.HasIndex("RespawnMapTypeId"); + + b.HasIndex("ReturnMapTypeId"); + + b.ToTable("MapType"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapTypeMap", b => + { + b.Property("MapTypeMapId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("MapTypeMapId")); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("MapTypeId") + .HasColumnType("smallint"); + + b.HasKey("MapTypeMapId"); + + b.HasIndex("MapTypeId"); + + b.HasIndex("MapId", "MapTypeId") + .IsUnique(); + + b.ToTable("MapTypeMap"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Mate", b => + { + b.Property("MateId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("MateId")); + + b.Property("Attack") + .HasColumnType("smallint"); + + b.Property("CanPickUp") + .HasColumnType("boolean"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("Defence") + .HasColumnType("smallint"); + + b.Property("Direction") + .HasColumnType("smallint"); + + b.Property("Experience") + .HasColumnType("bigint"); + + b.Property("Hp") + .HasColumnType("integer"); + + b.Property("IsSummonable") + .HasColumnType("boolean"); + + b.Property("IsTeamMember") + .HasColumnType("boolean"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("Loyalty") + .HasColumnType("smallint"); + + b.Property("MapX") + .HasColumnType("smallint"); + + b.Property("MapY") + .HasColumnType("smallint"); + + b.Property("MateType") + .HasColumnType("smallint"); + + b.Property("Mp") + .HasColumnType("integer"); + + b.Property("Name") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Skin") + .HasColumnType("smallint"); + + b.Property("VNum") + .HasColumnType("smallint"); + + b.HasKey("MateId"); + + b.HasIndex("CharacterId"); + + b.HasIndex("VNum"); + + b.ToTable("Mate"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Miniland", b => + { + b.Property("MinilandId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DailyVisitCount") + .HasColumnType("integer"); + + b.Property("MinilandMessage") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("MinilandPoint") + .HasColumnType("bigint"); + + b.Property("OwnerId") + .HasColumnType("bigint"); + + b.Property("State") + .HasColumnType("smallint"); + + b.Property("VisitCount") + .HasColumnType("integer"); + + b.Property("WelcomeMusicInfo") + .HasColumnType("smallint"); + + b.HasKey("MinilandId"); + + b.HasIndex("OwnerId"); + + b.ToTable("Miniland"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MinilandObject", b => + { + b.Property("MinilandObjectId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("InventoryItemInstanceId") + .HasColumnType("uuid"); + + b.Property("Level1BoxAmount") + .HasColumnType("smallint"); + + b.Property("Level2BoxAmount") + .HasColumnType("smallint"); + + b.Property("Level3BoxAmount") + .HasColumnType("smallint"); + + b.Property("Level4BoxAmount") + .HasColumnType("smallint"); + + b.Property("Level5BoxAmount") + .HasColumnType("smallint"); + + b.Property("MapX") + .HasColumnType("smallint"); + + b.Property("MapY") + .HasColumnType("smallint"); + + b.HasKey("MinilandObjectId"); + + b.HasIndex("InventoryItemInstanceId"); + + b.ToTable("MinilandObject"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.NpcMonster", b => + { + b.Property("NpcMonsterVNum") + .HasColumnType("smallint"); + + b.Property("AlwaysActive") + .HasColumnType("boolean"); + + b.Property("AmountRequired") + .HasColumnType("smallint"); + + b.Property("AttackClass") + .HasColumnType("smallint"); + + b.Property("AttackUpgrade") + .HasColumnType("smallint"); + + b.Property("BasicArea") + .HasColumnType("smallint"); + + b.Property("BasicCooldown") + .HasColumnType("smallint"); + + b.Property("BasicHitChance") + .HasColumnType("smallint"); + + b.Property("BasicRange") + .HasColumnType("smallint"); + + b.Property("BasicSkill") + .HasColumnType("smallint"); + + b.Property("CModeVNum") + .HasColumnType("smallint"); + + b.Property("CanCatch") + .HasColumnType("boolean"); + + b.Property("CanCollect") + .HasColumnType("boolean"); + + b.Property("CanOnlyBeDmgedByJajamaruLastSkill") + .HasColumnType("boolean"); + + b.Property("CanWalk") + .HasColumnType("boolean"); + + b.Property("CantDebuff") + .HasColumnType("boolean"); + + b.Property("CantTargetInfo") + .HasColumnType("boolean"); + + b.Property("CantVoke") + .HasColumnType("boolean"); + + b.Property("CellMinRange") + .HasColumnType("smallint"); + + b.Property("CellSize") + .HasColumnType("smallint"); + + b.Property("CloseDefence") + .HasColumnType("smallint"); + + b.Property("Concentrate") + .HasColumnType("smallint"); + + b.Property("CriticalChance") + .HasColumnType("smallint"); + + b.Property("CriticalRate") + .HasColumnType("smallint"); + + b.Property("DamageMaximum") + .HasColumnType("smallint"); + + b.Property("DamageMinimum") + .HasColumnType("smallint"); + + b.Property("DarkResistance") + .HasColumnType("smallint"); + + b.Property("DashSpeed") + .HasColumnType("smallint"); + + b.Property("DefenceDodge") + .HasColumnType("smallint"); + + b.Property("DefenceUpgrade") + .HasColumnType("smallint"); + + b.Property("DisappearAfterHitting") + .HasColumnType("boolean"); + + b.Property("DisappearAfterSeconds") + .HasColumnType("boolean"); + + b.Property("DisappearAfterSecondsMana") + .HasColumnType("boolean"); + + b.Property("DistanceDefence") + .HasColumnType("smallint"); + + b.Property("DistanceDefenceDodge") + .HasColumnType("smallint"); + + b.Property("DontDrainHpAfterSeconds") + .HasColumnType("boolean"); + + b.Property("EffectIdConstantly") + .HasColumnType("smallint"); + + b.Property("EffectIdOnDeath") + .HasColumnType("smallint"); + + b.Property("Element") + .HasColumnType("smallint"); + + b.Property("ElementRate") + .HasColumnType("smallint"); + + b.Property("FireResistance") + .HasColumnType("smallint"); + + b.Property("GiveDamagePercentage") + .HasColumnType("integer"); + + b.Property("GroupAttack") + .HasColumnType("smallint"); + + b.Property("HasDash") + .HasColumnType("boolean"); + + b.Property("HasMode") + .HasColumnType("boolean"); + + b.Property("HeroLevel") + .HasColumnType("smallint"); + + b.Property("HeroXp") + .HasColumnType("integer"); + + b.Property("HpThreshold") + .HasColumnType("smallint"); + + b.Property("IconId") + .HasColumnType("smallint"); + + b.Property("IsHostile") + .HasColumnType("boolean"); + + b.Property("IsPercent") + .HasColumnType("boolean"); + + b.Property("IsPercentileDmg") + .HasColumnType("boolean"); + + b.Property("IsValhallaPartner") + .HasColumnType("boolean"); + + b.Property("JobXp") + .HasColumnType("integer"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("LightResistance") + .HasColumnType("smallint"); + + b.Property("Limiter") + .HasColumnType("smallint"); + + b.Property("MagicDefence") + .HasColumnType("smallint"); + + b.Property("MaxHp") + .HasColumnType("integer"); + + b.Property("MaxMp") + .HasColumnType("integer"); + + b.Property("Midgard") + .HasColumnType("integer"); + + b.Property("MonsterType") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("NoAggresiveIcon") + .HasColumnType("boolean"); + + b.Property("NoticeRange") + .HasColumnType("smallint"); + + b.Property("OnDefenseOnlyOnce") + .HasColumnType("boolean"); + + b.Property("PetInfoVal1") + .HasColumnType("integer"); + + b.Property("PetInfoVal2") + .HasColumnType("integer"); + + b.Property("PetInfoVal3") + .HasColumnType("integer"); + + b.Property("PetInfoVal4") + .HasColumnType("integer"); + + b.Property("Race") + .HasColumnType("smallint"); + + b.Property("RaceType") + .HasColumnType("smallint"); + + b.Property("RangeThreshold") + .HasColumnType("smallint"); + + b.Property("RegenerateHpOverTime") + .HasColumnType("boolean"); + + b.Property("RespawnTime") + .HasColumnType("integer"); + + b.Property("SpawnMobOrColor") + .HasColumnType("integer"); + + b.Property("Speed") + .HasColumnType("smallint"); + + b.Property("SpriteSize") + .HasColumnType("smallint"); + + b.Property("TakeDamages") + .HasColumnType("integer"); + + b.Property("VNumRequired") + .HasColumnType("smallint"); + + b.Property("VisibleOnMinimapAsGreenDot") + .HasColumnType("boolean"); + + b.Property("WaterResistance") + .HasColumnType("smallint"); + + b.Property("Xp") + .HasColumnType("integer"); + + b.HasKey("NpcMonsterVNum"); + + b.ToTable("NpcMonster"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.NpcMonsterSkill", b => + { + b.Property("NpcMonsterSkillId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("NpcMonsterSkillId")); + + b.Property("Force") + .HasColumnType("smallint"); + + b.Property("NpcMonsterVNum") + .HasColumnType("smallint"); + + b.Property("Rate") + .HasColumnType("smallint"); + + b.Property("SkillVNum") + .HasColumnType("smallint"); + + b.HasKey("NpcMonsterSkillId"); + + b.HasIndex("NpcMonsterVNum"); + + b.HasIndex("SkillVNum"); + + b.ToTable("NpcMonsterSkill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.NpcTalk", b => + { + b.Property("DialogId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("DialogId")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("DialogId"); + + b.ToTable("NpcTalk"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.PenaltyLog", b => + { + b.Property("PenaltyLogId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PenaltyLogId")); + + b.Property("AccountId") + .HasColumnType("bigint"); + + b.Property("AdminName") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("DateStart") + .HasColumnType("timestamp with time zone"); + + b.Property("Penalty") + .HasColumnType("smallint"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("PenaltyLogId"); + + b.HasIndex("AccountId"); + + b.ToTable("PenaltyLog"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Portal", b => + { + b.Property("PortalId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PortalId")); + + b.Property("DestinationMapId") + .HasColumnType("smallint"); + + b.Property("DestinationX") + .HasColumnType("smallint"); + + b.Property("DestinationY") + .HasColumnType("smallint"); + + b.Property("IsDisabled") + .HasColumnType("boolean"); + + b.Property("SourceMapId") + .HasColumnType("smallint"); + + b.Property("SourceX") + .HasColumnType("smallint"); + + b.Property("SourceY") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("PortalId"); + + b.HasIndex("DestinationMapId"); + + b.HasIndex("SourceMapId"); + + b.ToTable("Portal"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Quest", b => + { + b.Property("QuestId") + .HasColumnType("smallint"); + + b.Property("AutoFinish") + .HasColumnType("boolean"); + + b.Property("Desc") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("EndDialogId") + .HasColumnType("integer"); + + b.Property("IsDaily") + .HasColumnType("boolean"); + + b.Property("IsSecondary") + .HasColumnType("boolean"); + + b.Property("LevelMax") + .HasColumnType("smallint"); + + b.Property("LevelMin") + .HasColumnType("smallint"); + + b.Property("NextQuestId") + .HasColumnType("smallint"); + + b.Property("QuestType") + .HasColumnType("smallint"); + + b.Property("RequiredQuestId") + .HasColumnType("smallint"); + + b.Property("SpecialData") + .HasColumnType("integer"); + + b.Property("StartDialogId") + .HasColumnType("integer"); + + b.Property("TargetMap") + .HasColumnType("smallint"); + + b.Property("TargetX") + .HasColumnType("smallint"); + + b.Property("TargetY") + .HasColumnType("smallint"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("QuestId"); + + b.ToTable("Quest"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestObjective", b => + { + b.Property("QuestObjectiveId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FirstData") + .HasColumnType("integer"); + + b.Property("FourthData") + .HasColumnType("integer"); + + b.Property("QuestId") + .HasColumnType("smallint"); + + b.Property("SecondData") + .HasColumnType("integer"); + + b.Property("ThirdData") + .HasColumnType("integer"); + + b.HasKey("QuestObjectiveId"); + + b.HasIndex("QuestId"); + + b.ToTable("QuestObjective"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestQuestReward", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("QuestId") + .HasColumnType("smallint"); + + b.Property("QuestRewardId") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("QuestId"); + + b.HasIndex("QuestRewardId"); + + b.ToTable("QuestQuestReward"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestReward", b => + { + b.Property("QuestRewardId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("QuestRewardId")); + + b.Property("Amount") + .HasColumnType("integer"); + + b.Property("Data") + .HasColumnType("integer"); + + b.Property("Design") + .HasColumnType("smallint"); + + b.Property("Rarity") + .HasColumnType("smallint"); + + b.Property("RewardType") + .HasColumnType("smallint"); + + b.Property("Upgrade") + .HasColumnType("smallint"); + + b.HasKey("QuestRewardId"); + + b.ToTable("QuestReward"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuicklistEntry", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("IconType") + .HasColumnType("smallint"); + + b.Property("IconVNum") + .HasColumnType("smallint"); + + b.Property("Morph") + .HasColumnType("smallint"); + + b.Property("QuickListIndex") + .HasColumnType("smallint"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.ToTable("QuicklistEntry"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Recipe", b => + { + b.Property("RecipeId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RecipeId")); + + b.Property("Amount") + .HasColumnType("smallint"); + + b.Property("ItemVNum") + .HasColumnType("smallint"); + + b.Property("MapNpcId") + .HasColumnType("integer"); + + b.HasKey("RecipeId"); + + b.HasIndex("ItemVNum"); + + b.HasIndex("MapNpcId"); + + b.ToTable("Recipe"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RecipeItem", b => + { + b.Property("RecipeItemId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RecipeItemId")); + + b.Property("Amount") + .HasColumnType("smallint"); + + b.Property("ItemVNum") + .HasColumnType("smallint"); + + b.Property("RecipeId") + .HasColumnType("smallint"); + + b.HasKey("RecipeItemId"); + + b.HasIndex("ItemVNum"); + + b.HasIndex("RecipeId"); + + b.ToTable("RecipeItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ReputationLevel", b => + { + b.Property("ReputationLevelId") + .HasColumnType("smallint"); + + b.Property("MaxReputation") + .HasColumnType("bigint"); + + b.Property("MinReputation") + .HasColumnType("bigint"); + + b.HasKey("ReputationLevelId"); + + b.ToTable("ReputationLevel"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Respawn", b => + { + b.Property("RespawnId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RespawnId")); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("RespawnMapTypeId") + .HasColumnType("bigint"); + + b.Property("X") + .HasColumnType("smallint"); + + b.Property("Y") + .HasColumnType("smallint"); + + b.HasKey("RespawnId"); + + b.HasIndex("CharacterId"); + + b.HasIndex("MapId"); + + b.HasIndex("RespawnMapTypeId"); + + b.ToTable("Respawn"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RespawnMapType", b => + { + b.Property("RespawnMapTypeId") + .HasColumnType("bigint"); + + b.Property("DefaultX") + .HasColumnType("smallint"); + + b.Property("DefaultY") + .HasColumnType("smallint"); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("Name") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("RespawnMapTypeId"); + + b.HasIndex("MapId"); + + b.ToTable("RespawnMapType"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RollGeneratedItem", b => + { + b.Property("RollGeneratedItemId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("RollGeneratedItemId")); + + b.Property("IsRareRandom") + .HasColumnType("boolean"); + + b.Property("IsSuperReward") + .HasColumnType("boolean"); + + b.Property("ItemGeneratedAmount") + .HasColumnType("smallint"); + + b.Property("ItemGeneratedUpgrade") + .HasColumnType("smallint"); + + b.Property("ItemGeneratedVNum") + .HasColumnType("smallint"); + + b.Property("MaximumOriginalItemRare") + .HasColumnType("smallint"); + + b.Property("MinimumOriginalItemRare") + .HasColumnType("smallint"); + + b.Property("OriginalItemDesign") + .HasColumnType("smallint"); + + b.Property("OriginalItemVNum") + .HasColumnType("smallint"); + + b.Property("Probability") + .HasColumnType("smallint"); + + b.HasKey("RollGeneratedItemId"); + + b.HasIndex("ItemGeneratedVNum"); + + b.HasIndex("OriginalItemVNum"); + + b.ToTable("RollGeneratedItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Script", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Argument1") + .HasColumnType("smallint"); + + b.Property("Argument2") + .HasColumnType("smallint"); + + b.Property("Argument3") + .HasColumnType("smallint"); + + b.Property("ScriptId") + .HasColumnType("smallint"); + + b.Property("ScriptStepId") + .HasColumnType("smallint"); + + b.Property("StepType") + .IsRequired() + .HasColumnType("text"); + + b.Property("StringArgument") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ScriptId", "ScriptStepId") + .IsUnique(); + + b.ToTable("Script"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ScriptedInstance", b => + { + b.Property("ScriptedInstanceId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ScriptedInstanceId")); + + b.Property("IsHeroic") + .HasColumnType("boolean"); + + b.Property("Label") + .HasColumnType("text"); + + b.Property("LevelMaximum") + .HasColumnType("smallint"); + + b.Property("LevelMinimum") + .HasColumnType("smallint"); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("PositionX") + .HasColumnType("smallint"); + + b.Property("PositionY") + .HasColumnType("smallint"); + + b.Property("Script") + .HasMaxLength(2147483647) + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("ScriptedInstanceId"); + + b.HasIndex("MapId"); + + b.ToTable("ScriptedInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Shop", b => + { + b.Property("ShopId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ShopId")); + + b.Property("MapNpcId") + .HasColumnType("integer"); + + b.Property("MenuType") + .HasColumnType("smallint"); + + b.Property("ShopType") + .HasColumnType("smallint"); + + b.HasKey("ShopId"); + + b.HasIndex("MapNpcId"); + + b.ToTable("Shop"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ShopItem", b => + { + b.Property("ShopItemId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ShopItemId")); + + b.Property("Color") + .HasColumnType("smallint"); + + b.Property("ItemVNum") + .HasColumnType("smallint"); + + b.Property("Rare") + .HasColumnType("smallint"); + + b.Property("ShopId") + .HasColumnType("integer"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("Upgrade") + .HasColumnType("smallint"); + + b.HasKey("ShopItemId"); + + b.HasIndex("ItemVNum"); + + b.HasIndex("ShopId"); + + b.ToTable("ShopItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ShopSkill", b => + { + b.Property("ShopSkillId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ShopSkillId")); + + b.Property("ShopId") + .HasColumnType("integer"); + + b.Property("SkillVNum") + .HasColumnType("smallint"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("ShopSkillId"); + + b.HasIndex("ShopId"); + + b.HasIndex("SkillVNum"); + + b.ToTable("ShopSkill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Skill", b => + { + b.Property("SkillVNum") + .HasColumnType("smallint"); + + b.Property("AttackAnimation") + .HasColumnType("smallint"); + + b.Property("CastAnimation") + .HasColumnType("smallint"); + + b.Property("CastEffect") + .HasColumnType("smallint"); + + b.Property("CastId") + .HasColumnType("smallint"); + + b.Property("CastTime") + .HasColumnType("smallint"); + + b.Property("CellPattern") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Class") + .HasColumnType("smallint"); + + b.Property("Cooldown") + .HasColumnType("smallint"); + + b.Property("CpCost") + .HasColumnType("smallint"); + + b.Property("Duration") + .HasColumnType("smallint"); + + b.Property("Effect") + .HasColumnType("smallint"); + + b.Property("Element") + .HasColumnType("smallint"); + + b.Property("HitType") + .HasColumnType("smallint"); + + b.Property("ItemVNum") + .HasColumnType("smallint"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("LevelMinimum") + .HasColumnType("smallint"); + + b.Property("MinimumAdventurerLevel") + .HasColumnType("smallint"); + + b.Property("MinimumArcherLevel") + .HasColumnType("smallint"); + + b.Property("MinimumMagicianLevel") + .HasColumnType("smallint"); + + b.Property("MinimumSwordmanLevel") + .HasColumnType("smallint"); + + b.Property("MpCost") + .HasColumnType("smallint"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Price") + .HasColumnType("integer"); + + b.Property("Range") + .HasColumnType("smallint"); + + b.Property("SkillType") + .HasColumnType("smallint"); + + b.Property("TargetRange") + .HasColumnType("smallint"); + + b.Property("TargetType") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UpgradeSkill") + .HasColumnType("smallint"); + + b.Property("UpgradeType") + .HasColumnType("smallint"); + + b.HasKey("SkillVNum"); + + b.ToTable("Skill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.StaticBonus", b => + { + b.Property("StaticBonusId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("StaticBonusId")); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("DateEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("StaticBonusType") + .HasColumnType("smallint"); + + b.HasKey("StaticBonusId"); + + b.HasIndex("CharacterId"); + + b.ToTable("StaticBonus"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.StaticBuff", b => + { + b.Property("StaticBuffId") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("StaticBuffId")); + + b.Property("CardId") + .HasColumnType("smallint"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("RemainingTime") + .HasColumnType("integer"); + + b.HasKey("StaticBuffId"); + + b.HasIndex("CardId"); + + b.HasIndex("CharacterId"); + + b.ToTable("StaticBuff"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Teleporter", b => + { + b.Property("TeleporterId") + .ValueGeneratedOnAdd() + .HasColumnType("smallint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TeleporterId")); + + b.Property("Index") + .HasColumnType("smallint"); + + b.Property("MapId") + .HasColumnType("smallint"); + + b.Property("MapNpcId") + .HasColumnType("integer"); + + b.Property("MapX") + .HasColumnType("smallint"); + + b.Property("MapY") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("TeleporterId"); + + b.HasIndex("MapId"); + + b.HasIndex("MapNpcId"); + + b.ToTable("Teleporter"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Title", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("TitleType") + .HasColumnType("smallint"); + + b.Property("Visible") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId", "TitleType") + .IsUnique(); + + b.ToTable("Title"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Warehouse", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("bigint"); + + b.Property("FamilyId") + .HasColumnType("bigint"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("FamilyId"); + + b.ToTable("Warehouse"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.WarehouseItem", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ItemInstanceId") + .HasColumnType("uuid"); + + b.Property("Slot") + .HasColumnType("smallint"); + + b.Property("WarehouseId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ItemInstanceId"); + + b.HasIndex("WarehouseId"); + + b.ToTable("WarehouseItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.UsableInstance", b => + { + b.HasBaseType("NosCore.Database.Entities.ItemInstance"); + + b.Property("Hp") + .HasColumnType("smallint"); + + b.Property("Mp") + .HasColumnType("smallint"); + + b.ToTable("ItemInstance", t => + { + t.Property("Hp") + .HasColumnName("UsableInstance_Hp"); + + t.Property("Mp") + .HasColumnName("UsableInstance_Mp"); + }); + + b.HasDiscriminator().HasValue("UsableInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.WearableInstance", b => + { + b.HasBaseType("NosCore.Database.Entities.ItemInstance"); + + b.Property("Ammo") + .HasColumnType("smallint"); + + b.Property("Cellon") + .HasColumnType("smallint"); + + b.Property("CloseDefence") + .HasColumnType("smallint"); + + b.Property("Concentrate") + .HasColumnType("smallint"); + + b.Property("CriticalDodge") + .HasColumnType("smallint"); + + b.Property("CriticalLuckRate") + .HasColumnType("smallint"); + + b.Property("CriticalRate") + .HasColumnType("smallint"); + + b.Property("DamageMaximum") + .HasColumnType("smallint"); + + b.Property("DamageMinimum") + .HasColumnType("smallint"); + + b.Property("DarkElement") + .HasColumnType("smallint"); + + b.Property("DarkResistance") + .HasColumnType("smallint"); + + b.Property("DefenceDodge") + .HasColumnType("smallint"); + + b.Property("DistanceDefence") + .HasColumnType("smallint"); + + b.Property("DistanceDefenceDodge") + .HasColumnType("smallint"); + + b.Property("ElementRate") + .HasColumnType("smallint"); + + b.Property("FireElement") + .HasColumnType("smallint"); + + b.Property("FireResistance") + .HasColumnType("smallint"); + + b.Property("HitRate") + .HasColumnType("smallint"); + + b.Property("Hp") + .HasColumnType("smallint"); + + b.Property("IsEmpty") + .HasColumnType("boolean"); + + b.Property("IsFixed") + .HasColumnType("boolean"); + + b.Property("LightElement") + .HasColumnType("smallint"); + + b.Property("LightResistance") + .HasColumnType("smallint"); + + b.Property("MagicDefence") + .HasColumnType("smallint"); + + b.Property("MaxElementRate") + .HasColumnType("smallint"); + + b.Property("Mp") + .HasColumnType("smallint"); + + b.Property("ShellRarity") + .HasColumnType("smallint"); + + b.Property("WaterElement") + .HasColumnType("smallint"); + + b.Property("WaterResistance") + .HasColumnType("smallint"); + + b.Property("Xp") + .HasColumnType("bigint"); + + b.HasDiscriminator().HasValue("WearableInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.SpecialistInstance", b => + { + b.HasBaseType("NosCore.Database.Entities.WearableInstance"); + + b.Property("SlDamage") + .HasColumnType("smallint"); + + b.Property("SlDefence") + .HasColumnType("smallint"); + + b.Property("SlElement") + .HasColumnType("smallint"); + + b.Property("SlHp") + .HasColumnType("smallint"); + + b.Property("SpDamage") + .HasColumnType("smallint"); + + b.Property("SpDark") + .HasColumnType("smallint"); + + b.Property("SpDefence") + .HasColumnType("smallint"); + + b.Property("SpElement") + .HasColumnType("smallint"); + + b.Property("SpFire") + .HasColumnType("smallint"); + + b.Property("SpHp") + .HasColumnType("smallint"); + + b.Property("SpLevel") + .HasColumnType("smallint"); + + b.Property("SpLight") + .HasColumnType("smallint"); + + b.Property("SpStoneUpgrade") + .HasColumnType("smallint"); + + b.Property("SpWater") + .HasColumnType("smallint"); + + b.HasDiscriminator().HasValue("SpecialistInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.BoxInstance", b => + { + b.HasBaseType("NosCore.Database.Entities.SpecialistInstance"); + + b.Property("HoldingVNum") + .HasColumnType("smallint"); + + b.HasDiscriminator().HasValue("BoxInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ActPart", b => + { + b.HasOne("NosCore.Database.Entities.Act", "Act") + .WithMany("ActParts") + .HasForeignKey("ActId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Act"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.BCard", b => + { + b.HasOne("NosCore.Database.Entities.Card", "Card") + .WithMany("BCards") + .HasForeignKey("CardId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Item", "Item") + .WithMany("BCards") + .HasForeignKey("ItemVNum") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.NpcMonster", "NpcMonster") + .WithMany("BCards") + .HasForeignKey("NpcMonsterVNum") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Skill", "Skill") + .WithMany("BCards") + .HasForeignKey("SkillVNum") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Card"); + + b.Navigation("Item"); + + b.Navigation("NpcMonster"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.BazaarItem", b => + { + b.HasOne("NosCore.Database.Entities.ItemInstance", "ItemInstance") + .WithMany("BazaarItem") + .HasForeignKey("ItemInstanceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Character", "Seller") + .WithMany("BazaarItem") + .HasForeignKey("SellerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ItemInstance"); + + b.Navigation("Seller"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Character", b => + { + b.HasOne("NosCore.Database.Entities.Account", "Account") + .WithMany("Character") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Script", "Script") + .WithMany("Characters") + .HasForeignKey("CurrentScriptId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("Character") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("Map"); + + b.Navigation("Script"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.CharacterActPart", b => + { + b.HasOne("NosCore.Database.Entities.ActPart", "ActPart") + .WithMany("CharacterActParts") + .HasForeignKey("ActPartId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("CharacterActParts") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ActPart"); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.CharacterQuest", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("CharacterQuest") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Quest", "Quest") + .WithMany("CharacterQuest") + .HasForeignKey("QuestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("Quest"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.CharacterQuestObjective", b => + { + b.HasOne("NosCore.Database.Entities.CharacterQuest", "CharacterQuest") + .WithMany("CharacterQuestObjective") + .HasForeignKey("CharacterQuestId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.QuestObjective", "QuestObjective") + .WithMany("CharacterQuestObjective") + .HasForeignKey("QuestObjectiveId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CharacterQuest"); + + b.Navigation("QuestObjective"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.CharacterRelation", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character1") + .WithMany("CharacterRelation1") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Character", "Character2") + .WithMany("CharacterRelation2") + .HasForeignKey("RelatedCharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character1"); + + b.Navigation("Character2"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.CharacterSkill", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("CharacterSkill") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Skill", "Skill") + .WithMany("CharacterSkill") + .HasForeignKey("SkillVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Combo", b => + { + b.HasOne("NosCore.Database.Entities.Skill", "Skill") + .WithMany("Combo") + .HasForeignKey("SkillVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Drop", b => + { + b.HasOne("NosCore.Database.Entities.MapType", "MapType") + .WithMany("Drops") + .HasForeignKey("MapTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.NpcMonster", "NpcMonster") + .WithMany("Drop") + .HasForeignKey("MonsterVNum") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Item", "Item") + .WithMany("Drop") + .HasForeignKey("VNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("MapType"); + + b.Navigation("NpcMonster"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.EquipmentOption", b => + { + b.HasOne("NosCore.Database.Entities.WearableInstance", "WearableInstance") + .WithMany() + .HasForeignKey("WearableInstanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("WearableInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.FamilyCharacter", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("FamilyCharacter") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Family", "Family") + .WithMany("FamilyCharacters") + .HasForeignKey("FamilyId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("Family"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.FamilyLog", b => + { + b.HasOne("NosCore.Database.Entities.Family", "Family") + .WithMany("FamilyLogs") + .HasForeignKey("FamilyId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Family"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.InventoryItemInstance", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("Inventory") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.ItemInstance", "ItemInstance") + .WithMany("InventoryItemInstance") + .HasForeignKey("ItemInstanceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("ItemInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ItemInstance", b => + { + b.HasOne("NosCore.Database.Entities.Character", "BoundCharacter") + .WithMany() + .HasForeignKey("BoundCharacterId"); + + b.HasOne("NosCore.Database.Entities.Item", "Item") + .WithMany("ItemInstances") + .HasForeignKey("ItemVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BoundCharacter"); + + b.Navigation("Item"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Mail", b => + { + b.HasOne("NosCore.Database.Entities.ItemInstance", "ItemInstance") + .WithMany("Mail") + .HasForeignKey("ItemInstanceId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Character", "Receiver") + .WithMany("Mail1") + .HasForeignKey("ReceiverId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Character", "Sender") + .WithMany("Mail") + .HasForeignKey("SenderId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("ItemInstance"); + + b.Navigation("Receiver"); + + b.Navigation("Sender"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapMonster", b => + { + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("MapMonster") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.NpcMonster", "NpcMonster") + .WithMany("MapMonster") + .HasForeignKey("VNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + + b.Navigation("NpcMonster"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapNpc", b => + { + b.HasOne("NosCore.Database.Entities.NpcTalk", "NpcTalk") + .WithMany("MapNpc") + .HasForeignKey("Dialog") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("MapNpc") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.NpcMonster", "NpcMonster") + .WithMany("MapNpc") + .HasForeignKey("VNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + + b.Navigation("NpcMonster"); + + b.Navigation("NpcTalk"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapType", b => + { + b.HasOne("NosCore.Database.Entities.RespawnMapType", "RespawnMapType") + .WithMany("MapTypes") + .HasForeignKey("RespawnMapTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.RespawnMapType", "ReturnMapType") + .WithMany("MapTypes1") + .HasForeignKey("ReturnMapTypeId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RespawnMapType"); + + b.Navigation("ReturnMapType"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapTypeMap", b => + { + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("MapTypeMap") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.MapType", "MapType") + .WithMany("MapTypeMap") + .HasForeignKey("MapTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + + b.Navigation("MapType"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Mate", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("Mate") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.NpcMonster", "NpcMonster") + .WithMany("Mate") + .HasForeignKey("VNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("NpcMonster"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Miniland", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Owner") + .WithMany("Miniland") + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MinilandObject", b => + { + b.HasOne("NosCore.Database.Entities.InventoryItemInstance", "InventoryItemInstance") + .WithMany("MinilandObject") + .HasForeignKey("InventoryItemInstanceId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("InventoryItemInstance"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.NpcMonsterSkill", b => + { + b.HasOne("NosCore.Database.Entities.NpcMonster", "NpcMonster") + .WithMany("NpcMonsterSkill") + .HasForeignKey("NpcMonsterVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Skill", "Skill") + .WithMany("NpcMonsterSkill") + .HasForeignKey("SkillVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("NpcMonster"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.PenaltyLog", b => + { + b.HasOne("NosCore.Database.Entities.Account", "Account") + .WithMany("PenaltyLog") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Account"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Portal", b => + { + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("Portal") + .HasForeignKey("DestinationMapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Map", "Map1") + .WithMany("Portal1") + .HasForeignKey("SourceMapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + + b.Navigation("Map1"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestObjective", b => + { + b.HasOne("NosCore.Database.Entities.Quest", "Quest") + .WithMany("QuestObjective") + .HasForeignKey("QuestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Quest"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestQuestReward", b => + { + b.HasOne("NosCore.Database.Entities.Quest", "Quest") + .WithMany("QuestQuestReward") + .HasForeignKey("QuestId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.QuestReward", "QuestReward") + .WithMany("QuestQuestReward") + .HasForeignKey("QuestRewardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Quest"); + + b.Navigation("QuestReward"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuicklistEntry", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("QuicklistEntry") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Recipe", b => + { + b.HasOne("NosCore.Database.Entities.Item", "Item") + .WithMany("Recipe") + .HasForeignKey("ItemVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.MapNpc", "MapNpc") + .WithMany("Recipe") + .HasForeignKey("MapNpcId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("MapNpc"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RecipeItem", b => + { + b.HasOne("NosCore.Database.Entities.Item", "Item") + .WithMany("RecipeItem") + .HasForeignKey("ItemVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Recipe", "Recipe") + .WithMany("RecipeItem") + .HasForeignKey("RecipeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Recipe"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Respawn", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("Respawn") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("Respawn") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.RespawnMapType", "RespawnMapType") + .WithMany("Respawn") + .HasForeignKey("RespawnMapTypeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("Map"); + + b.Navigation("RespawnMapType"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RespawnMapType", b => + { + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("RespawnMapType") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RollGeneratedItem", b => + { + b.HasOne("NosCore.Database.Entities.Item", "ItemGenerated") + .WithMany("RollGeneratedItem2") + .HasForeignKey("ItemGeneratedVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Item", "OriginalItem") + .WithMany("RollGeneratedItem") + .HasForeignKey("OriginalItemVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ItemGenerated"); + + b.Navigation("OriginalItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ScriptedInstance", b => + { + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("ScriptedInstance") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Shop", b => + { + b.HasOne("NosCore.Database.Entities.MapNpc", "MapNpc") + .WithMany("Shop") + .HasForeignKey("MapNpcId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("MapNpc"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ShopItem", b => + { + b.HasOne("NosCore.Database.Entities.Item", "Item") + .WithMany("ShopItem") + .HasForeignKey("ItemVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Shop", "Shop") + .WithMany("ShopItem") + .HasForeignKey("ShopId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("Shop"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ShopSkill", b => + { + b.HasOne("NosCore.Database.Entities.Shop", "Shop") + .WithMany("ShopSkill") + .HasForeignKey("ShopId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Skill", "Skill") + .WithMany("ShopSkill") + .HasForeignKey("SkillVNum") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Shop"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.StaticBonus", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("StaticBonus") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.StaticBuff", b => + { + b.HasOne("NosCore.Database.Entities.Card", "Card") + .WithMany("StaticBuff") + .HasForeignKey("CardId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("StaticBuff") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Card"); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Teleporter", b => + { + b.HasOne("NosCore.Database.Entities.Map", "Map") + .WithMany("Teleporter") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.MapNpc", "MapNpc") + .WithMany("Teleporter") + .HasForeignKey("MapNpcId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Map"); + + b.Navigation("MapNpc"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Title", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("Title") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Warehouse", b => + { + b.HasOne("NosCore.Database.Entities.Character", "Character") + .WithMany("Warehouses") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("NosCore.Database.Entities.Family", "Family") + .WithMany("Warehouses") + .HasForeignKey("FamilyId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Character"); + + b.Navigation("Family"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.WarehouseItem", b => + { + b.HasOne("NosCore.Database.Entities.ItemInstance", "ItemInstance") + .WithMany("WarehouseItems") + .HasForeignKey("ItemInstanceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("NosCore.Database.Entities.Warehouse", "Warehouse") + .WithMany("WarehouseItems") + .HasForeignKey("WarehouseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ItemInstance"); + + b.Navigation("Warehouse"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Account", b => + { + b.Navigation("Character"); + + b.Navigation("PenaltyLog"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Act", b => + { + b.Navigation("ActParts"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ActPart", b => + { + b.Navigation("CharacterActParts"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Card", b => + { + b.Navigation("BCards"); + + b.Navigation("StaticBuff"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Character", b => + { + b.Navigation("BazaarItem"); + + b.Navigation("CharacterActParts"); + + b.Navigation("CharacterQuest"); + + b.Navigation("CharacterRelation1"); + + b.Navigation("CharacterRelation2"); + + b.Navigation("CharacterSkill"); + + b.Navigation("FamilyCharacter"); + + b.Navigation("Inventory"); + + b.Navigation("Mail"); + + b.Navigation("Mail1"); + + b.Navigation("Mate"); + + b.Navigation("Miniland"); + + b.Navigation("QuicklistEntry"); + + b.Navigation("Respawn"); + + b.Navigation("StaticBonus"); + + b.Navigation("StaticBuff"); + + b.Navigation("Title"); + + b.Navigation("Warehouses"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.CharacterQuest", b => + { + b.Navigation("CharacterQuestObjective"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Family", b => + { + b.Navigation("FamilyCharacters"); + + b.Navigation("FamilyLogs"); + + b.Navigation("Warehouses"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.InventoryItemInstance", b => + { + b.Navigation("MinilandObject"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Item", b => + { + b.Navigation("BCards"); + + b.Navigation("Drop"); + + b.Navigation("ItemInstances"); + + b.Navigation("Recipe"); + + b.Navigation("RecipeItem"); + + b.Navigation("RollGeneratedItem"); + + b.Navigation("RollGeneratedItem2"); + + b.Navigation("ShopItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.ItemInstance", b => + { + b.Navigation("BazaarItem"); + + b.Navigation("InventoryItemInstance"); + + b.Navigation("Mail"); + + b.Navigation("WarehouseItems"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Map", b => + { + b.Navigation("Character"); + + b.Navigation("MapMonster"); + + b.Navigation("MapNpc"); + + b.Navigation("MapTypeMap"); + + b.Navigation("Portal"); + + b.Navigation("Portal1"); + + b.Navigation("Respawn"); + + b.Navigation("RespawnMapType"); + + b.Navigation("ScriptedInstance"); + + b.Navigation("Teleporter"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapNpc", b => + { + b.Navigation("Recipe"); + + b.Navigation("Shop"); + + b.Navigation("Teleporter"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.MapType", b => + { + b.Navigation("Drops"); + + b.Navigation("MapTypeMap"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.NpcMonster", b => + { + b.Navigation("BCards"); + + b.Navigation("Drop"); + + b.Navigation("MapMonster"); + + b.Navigation("MapNpc"); + + b.Navigation("Mate"); + + b.Navigation("NpcMonsterSkill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.NpcTalk", b => + { + b.Navigation("MapNpc"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Quest", b => + { + b.Navigation("CharacterQuest"); + + b.Navigation("QuestObjective"); + + b.Navigation("QuestQuestReward"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestObjective", b => + { + b.Navigation("CharacterQuestObjective"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.QuestReward", b => + { + b.Navigation("QuestQuestReward"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Recipe", b => + { + b.Navigation("RecipeItem"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.RespawnMapType", b => + { + b.Navigation("MapTypes"); + + b.Navigation("MapTypes1"); + + b.Navigation("Respawn"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Script", b => + { + b.Navigation("Characters"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Shop", b => + { + b.Navigation("ShopItem"); + + b.Navigation("ShopSkill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Skill", b => + { + b.Navigation("BCards"); + + b.Navigation("CharacterSkill"); + + b.Navigation("Combo"); + + b.Navigation("NpcMonsterSkill"); + + b.Navigation("ShopSkill"); + }); + + modelBuilder.Entity("NosCore.Database.Entities.Warehouse", b => + { + b.Navigation("WarehouseItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/NosCore.Database/Migrations/20260830103418_AddCharacterLastSp.cs b/src/NosCore.Database/Migrations/20260830103418_AddCharacterLastSp.cs new file mode 100644 index 000000000..5994164d2 --- /dev/null +++ b/src/NosCore.Database/Migrations/20260830103418_AddCharacterLastSp.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace NosCore.Database.Migrations +{ + /// + public partial class AddCharacterLastSp : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "LastSp", + table: "Character", + type: "timestamp with time zone", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "LastSp", + table: "Character"); + } + } +} diff --git a/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs b/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs index f0cd3b308..f705756df 100644 --- a/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs +++ b/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -420,6 +420,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("JobLevelXp") .HasColumnType("bigint"); + b.Property("LastSp") + .HasColumnType("timestamp with time zone"); + b.Property("Level") .HasColumnType("smallint"); diff --git a/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstance.cs b/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstance.cs index d3b01f179..f582be947 100644 --- a/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstance.cs +++ b/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstance.cs @@ -65,6 +65,7 @@ public class MapInstance : IBroadcastable, IDisposable private readonly IRegenerationService? _regenerationService; private readonly IBattleService? _battleService; private readonly IVitalityService? _vitalityService; + private readonly TransformationService.ISpCooldownNotificationService? _spCooldownNotificationService; private readonly ConcurrentDictionary _pendingRespawns = new(); public MapWorld EcsWorld { get; } @@ -73,7 +74,8 @@ public MapInstance(Map.Map map, Guid guid, bool shopAllowed, MapInstanceType typ IMapItemGenerationService mapItemGenerationService, ILogger logger, IClock clock, IMapChangeService mapChangeService, ISessionGroupFactory sessionGroupFactory, ISessionRegistry sessionRegistry, IHeuristic distanceCalculator, IMonsterAi? monsterAi = null, IBuffService? buffService = null, IRegenerationService? regenerationService = null, - IBattleService? battleService = null, IVitalityService? vitalityService = null) + IBattleService? battleService = null, IVitalityService? vitalityService = null, + TransformationService.ISpCooldownNotificationService? spCooldownNotificationService = null) { LastPackets = new ConcurrentQueue(); XpRate = 1; @@ -100,6 +102,7 @@ public MapInstance(Map.Map map, Guid guid, bool shopAllowed, MapInstanceType typ _regenerationService = regenerationService; _battleService = battleService; _vitalityService = vitalityService; + _spCooldownNotificationService = spCooldownNotificationService; EcsWorld = new MapWorld(); } @@ -445,6 +448,11 @@ await _vitalityService.RefreshAndNotifyAsync(session.Character) await _battleService.TickCooldownResetsAsync(this).ConfigureAwait(false); } + if (_spCooldownNotificationService != null) + { + await _spCooldownNotificationService.TickAsync(this); + } + await SweepPendingRespawnsAsync().ConfigureAwait(false); } catch (Exception e) diff --git a/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstanceGenerationService.cs b/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstanceGenerationService.cs index 7804c53b3..76781a19e 100644 --- a/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstanceGenerationService.cs +++ b/src/NosCore.GameObject/Services/MapInstanceGenerationService/MapInstanceGenerationService.cs @@ -42,7 +42,8 @@ public class MapInstanceGeneratorService(List maps, List NosCore.GameObject.Services.BattleService.IBuffService buffService, NosCore.GameObject.Services.BattleService.IRegenerationService regenerationService, NosCore.GameObject.Services.BattleService.IBattleService battleService, - NosCore.GameObject.Services.BattleService.IVitalityService vitalityService) + NosCore.GameObject.Services.BattleService.IVitalityService vitalityService, + TransformationService.ISpCooldownNotificationService spCooldownNotificationService) : IMapInstanceGeneratorService { public Task AddMapInstanceAsync(MapInstance mapInstance) @@ -130,7 +131,7 @@ public MapInstance CreateMapInstance(Map.Map map, Guid guid, bool shopAllowed, M { return new MapInstance(map, guid, shopAllowed, normalInstance, mapItemGenerationService, loggerFactory.CreateLogger(), clock, - mapChangeService, sessionGroupFactory, sessionRegistry, distanceCalculator, monsterAi, buffService, regenerationService, battleService, vitalityService); + mapChangeService, sessionGroupFactory, sessionRegistry, distanceCalculator, monsterAi, buffService, regenerationService, battleService, vitalityService, spCooldownNotificationService); } private async Task LoadPortalsAsync(MapInstance mapInstance, List portals) diff --git a/src/NosCore.GameObject/Services/SaveService/SaveService.cs b/src/NosCore.GameObject/Services/SaveService/SaveService.cs index e7d03bb8b..8bd4c324e 100644 --- a/src/NosCore.GameObject/Services/SaveService/SaveService.cs +++ b/src/NosCore.GameObject/Services/SaveService/SaveService.cs @@ -63,6 +63,7 @@ public async Task SaveAsync(ClientSession session) characterDto.MapX = character.PositionX; characterDto.MapY = character.PositionY; } + characterDto.LastSp = character.LastSp; characterDto.SpPoint = character.SpPoint; characterDto.SpAdditionPoint = character.SpAdditionPoint; characterDto.CurrentScriptId = character.CurrentScriptId; diff --git a/src/NosCore.GameObject/Services/TransformationService/SpCooldownNotificationService.cs b/src/NosCore.GameObject/Services/TransformationService/SpCooldownNotificationService.cs new file mode 100644 index 000000000..9fcb3bd4d --- /dev/null +++ b/src/NosCore.GameObject/Services/TransformationService/SpCooldownNotificationService.cs @@ -0,0 +1,82 @@ +// __ _ __ __ ___ __ ___ ___ +// | \| |/__\ /' _/ / _//__\| _ \ __| +// | | ' | \/ |`._`.| \_| \/ | v / _| +// |_|\__|\__/ |___/ \__/\__/|_|_\___| +// + +using NodaTime; +using NosCore.GameObject.Infastructure; +using NosCore.GameObject.Networking.ClientSession; +using NosCore.GameObject.Services.MapInstanceGenerationService; +using NosCore.Packets.Enumerations; +using NosCore.Packets.ServerPackets.Chats; +using NosCore.Packets.ServerPackets.Specialists; +using NosCore.Shared.Enumerations; +using System.Collections.Concurrent; +using System.Threading.Tasks; + +namespace NosCore.GameObject.Services.TransformationService +{ + public interface ISpCooldownNotificationService + { + void Schedule(ClientSession session, Instant end); + + Task TickAsync(MapInstance mapInstance); + } + + // The cooldown itself is gated by LastSp arithmetic; this only delivers the + // "side effect gone" packets when the window elapses. Swept by the owning map's + // life loop instead of a detached timer so a disconnected session is dropped + // rather than raced. + public sealed class SpCooldownNotificationService(IClock clock) : ISpCooldownNotificationService, ISingletonService + { + private readonly ConcurrentDictionary _pending = new(); + + public void Schedule(ClientSession session, Instant end) + { + _pending[session.Character.CharacterId] = (session, end); + } + + public async Task TickAsync(MapInstance mapInstance) + { + if (_pending.IsEmpty) + { + return; + } + + var now = clock.GetCurrentInstant(); + foreach (var (characterId, entry) in _pending) + { + if (!entry.Session.HasPlayerEntity) + { + _pending.TryRemove(characterId, out _); + continue; + } + + if (entry.Session.Character.MapInstance.MapInstanceId != mapInstance.MapInstanceId) + { + continue; + } + + if (entry.End > now) + { + continue; + } + + if (!_pending.TryRemove(characterId, out _)) + { + continue; + } + + await entry.Session.SendPacketAsync(new SayiPacket + { + VisualType = VisualType.Player, + VisualId = characterId, + Type = SayColorType.Red, + Message = Game18NConstString.TransformationSideEffectGone + }); + await entry.Session.SendPacketAsync(new SdPacket { Cooldown = 0 }); + } + } + } +} diff --git a/src/NosCore.GameObject/Services/TransformationService/TransformationService.cs b/src/NosCore.GameObject/Services/TransformationService/TransformationService.cs index ab2ba801d..466fedbe7 100644 --- a/src/NosCore.GameObject/Services/TransformationService/TransformationService.cs +++ b/src/NosCore.GameObject/Services/TransformationService/TransformationService.cs @@ -33,9 +33,12 @@ public class TransformationService(IClock clock, IExperienceService experienceSe IJobExperienceService jobExperienceService, IHeroExperienceService heroExperienceService, ILogger logger, ILogLanguageLocalizer logLanguage, IOptions worldConfiguration, SpeedCalculationService.ISpeedCalculationService speedCalculationService, - SkillService.ISkillService skillService) + SkillService.ISkillService skillService, + ISpCooldownNotificationService spCooldownNotificationService) : ITransformationService { + public const short SpCooldownSeconds = 30; + public async Task RemoveSpAsync(ClientSession session) { var character = session.Character; @@ -45,7 +48,7 @@ public async Task RemoveSpAsync(ClientSession session) character.MorphDesign = 0; await skillService.UnloadSpecialistSkillsAsync(character); - character.SpCooldown = 30; + character.SpCooldown = SpCooldownSeconds; var characterId = character.CharacterId; var spCooldown = character.SpCooldown; @@ -76,19 +79,7 @@ await mapInstance.SendPacketAsync(new GuriPacket }); await session.SendPacketAsync(statPacket); - async Task CoolDown() - { - await session.SendPacketAsync(new SayiPacket - { - VisualType = VisualType.Player, - VisualId = characterId, - Type = SayColorType.Red, - Message = Game18NConstString.TransformationSideEffectGone - }); - await session.SendPacketAsync(new SdPacket { Cooldown = 0 }); - } - - Observable.Timer(TimeSpan.FromMilliseconds(spCooldown * 1000)).Select(_ => CoolDown()).Subscribe(); + spCooldownNotificationService.Schedule(session, clock.GetCurrentInstant().Plus(Duration.FromSeconds(spCooldown))); } public async Task ChangeSpAsync(ClientSession session) diff --git a/src/NosCore.PacketHandlers/CharacterScreen/SelectPacketHandler.cs b/src/NosCore.PacketHandlers/CharacterScreen/SelectPacketHandler.cs index d01b6e1c4..c1c262bb9 100644 --- a/src/NosCore.PacketHandlers/CharacterScreen/SelectPacketHandler.cs +++ b/src/NosCore.PacketHandlers/CharacterScreen/SelectPacketHandler.cs @@ -1,4 +1,4 @@ -// __ _ __ __ ___ __ ___ ___ +// __ _ __ __ ___ __ ___ ___ // | \| |/__\ /' _/ / _//__\| _ \ __| // | | ' | \/ |`._`.| \_| \/ | v / _| // |_|\__|\__/ |___/ \__/\__/|_|_\___| @@ -177,7 +177,7 @@ await pubSubHub.SubscribeAsync(new Subscriber false, true, now, - now, + characterDto.LastSp ?? now, 0 ); @@ -207,6 +207,12 @@ await pubSubHub.SubscribeAsync(new Subscriber var character = clientSession.Character; group.JoinGroup(character); + if (characterDto.LastSp is { } lastSp + && now < lastSp.Plus(Duration.FromSeconds(GameObject.Services.TransformationService.TransformationService.SpCooldownSeconds))) + { + character.SpCooldown = GameObject.Services.TransformationService.TransformationService.SpCooldownSeconds; + } + #pragma warning disable CS0618 await clientSession.SendPacketsAsync(character.GenerateInv(logger, logLanguage)); #pragma warning restore CS0618 diff --git a/test/NosCore.GameObject.Tests/Services/TransformationService/TransformationServiceTests.cs b/test/NosCore.GameObject.Tests/Services/TransformationService/TransformationServiceTests.cs index b0cc65630..33fa37ce3 100644 --- a/test/NosCore.GameObject.Tests/Services/TransformationService/TransformationServiceTests.cs +++ b/test/NosCore.GameObject.Tests/Services/TransformationService/TransformationServiceTests.cs @@ -43,7 +43,8 @@ public async Task SetupAsync() TestHelpers.Instance.LogLanguageLocalizer, TestHelpers.Instance.WorldConfiguration, new GameObject.Services.SpeedCalculationService.SpeedCalculationService(new SpeedService()), - new Mock().Object); + new Mock().Object, + new GameObject.Services.TransformationService.SpCooldownNotificationService(TestHelpers.Instance.Clock)); } [TestMethod] diff --git a/test/NosCore.PacketHandlers.Tests/Inventory/SpTransformPacketHandlerTests.cs b/test/NosCore.PacketHandlers.Tests/Inventory/SpTransformPacketHandlerTests.cs index 2505d87cb..f45de1b46 100644 --- a/test/NosCore.PacketHandlers.Tests/Inventory/SpTransformPacketHandlerTests.cs +++ b/test/NosCore.PacketHandlers.Tests/Inventory/SpTransformPacketHandlerTests.cs @@ -50,7 +50,8 @@ public async Task SetupAsync() new Mock().Object, new Mock().Object, new Mock>().Object, TestHelpers.Instance.LogLanguageLocalizer, TestHelpers.Instance.WorldConfiguration, new NosCore.GameObject.Services.SpeedCalculationService.SpeedCalculationService(new SpeedService()), - new Mock().Object), + new Mock().Object, + new SpCooldownNotificationService(TestHelpers.Instance.Clock)), TestHelpers.Instance.GameLanguageLocalizer); } diff --git a/test/NosCore.Tests.Shared/TestHelpers.cs b/test/NosCore.Tests.Shared/TestHelpers.cs index 6d3be481b..bf4999c8b 100644 --- a/test/NosCore.Tests.Shared/TestHelpers.cs +++ b/test/NosCore.Tests.Shared/TestHelpers.cs @@ -258,7 +258,8 @@ private async Task GenerateMapInstanceProviderAsync() new Mock().Object, new Mock().Object, new Mock().Object, - new Mock().Object); + new Mock().Object, + new Mock().Object); await instanceGeneratorService.InitializeAsync(); await instanceGeneratorService.AddMapInstanceAsync(new MapInstance(miniland, MinilandId, false, MapInstanceType.NormalInstance, MapItemProvider, NullLogger.Instance, Clock, mapChangeService, SessionGroupFactory, SessionRegistry, Instance.DistanceCalculator));