diff --git a/documentation/dat/Skill.dat.md b/documentation/dat/Skill.dat.md index f2543992b..bd8baeded 100644 --- a/documentation/dat/Skill.dat.md +++ b/documentation/dat/Skill.dat.md @@ -13,6 +13,7 @@ | CastEffect | Int16 | | | | CastId | Int16 | | | | CastTime | Int16 | | | +| CellPattern | String | | | | Class | Byte | | | | Combo | ICollection`1 | | | | Cooldown | Int16 | | | diff --git a/src/NosCore.Database/Entities/Skill.cs b/src/NosCore.Database/Entities/Skill.cs index 6e7c9837d..05a1c39be 100644 --- a/src/NosCore.Database/Entities/Skill.cs +++ b/src/NosCore.Database/Entities/Skill.cs @@ -40,6 +40,10 @@ public Skill() public short Cooldown { get; set; } + /// Cells hit, "dx,dy,..." from the caster facing north. Null when there is none. + [MaxLength(512)] + public string? CellPattern { get; set; } + public byte CpCost { get; set; } public short Duration { get; set; } diff --git a/src/NosCore.Database/Migrations/20260826123844_AddSkillCellPattern.Designer.cs b/src/NosCore.Database/Migrations/20260826123844_AddSkillCellPattern.Designer.cs new file mode 100644 index 000000000..e6a37ec0e --- /dev/null +++ b/src/NosCore.Database/Migrations/20260826123844_AddSkillCellPattern.Designer.cs @@ -0,0 +1,4155 @@ +// +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("20260826123844_AddSkillCellPattern")] + partial class AddSkillCellPattern + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.6") + .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("smallint"); + + 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("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.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.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("Label") + .HasColumnType("text"); + + 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/20260826123844_AddSkillCellPattern.cs b/src/NosCore.Database/Migrations/20260826123844_AddSkillCellPattern.cs new file mode 100644 index 000000000..85f2598b6 --- /dev/null +++ b/src/NosCore.Database/Migrations/20260826123844_AddSkillCellPattern.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace NosCore.Database.Migrations +{ + /// + public partial class AddSkillCellPattern : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CellPattern", + table: "Skill", + type: "character varying(512)", + maxLength: 512, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CellPattern", + table: "Skill"); + } + } +} diff --git a/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs b/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs index cdd935c0a..995304d87 100644 --- a/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs +++ b/src/NosCore.Database/Migrations/NosCoreContextModelSnapshot.cs @@ -2714,6 +2714,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CastTime") .HasColumnType("smallint"); + b.Property("CellPattern") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + b.Property("Class") .HasColumnType("smallint"); diff --git a/src/NosCore.GameObject/Services/BattleService/Model/SkillInfo.cs b/src/NosCore.GameObject/Services/BattleService/Model/SkillInfo.cs index dfb5efe54..de2a76d16 100644 --- a/src/NosCore.GameObject/Services/BattleService/Model/SkillInfo.cs +++ b/src/NosCore.GameObject/Services/BattleService/Model/SkillInfo.cs @@ -27,7 +27,8 @@ public sealed record SkillInfo( byte Element, short Duration, short MpCost, - IReadOnlyList BCards) + IReadOnlyList BCards, + string? CellPattern = null) { public bool IsAoe => HitType is TargetHitType.SingleAoeTargetHit or TargetHitType.AoeTargetHit diff --git a/src/NosCore.GameObject/Services/BattleService/SkillCells.cs b/src/NosCore.GameObject/Services/BattleService/SkillCells.cs new file mode 100644 index 000000000..1ee2823c3 --- /dev/null +++ b/src/NosCore.GameObject/Services/BattleService/SkillCells.cs @@ -0,0 +1,81 @@ +// __ _ __ __ ___ __ ___ ___ +// | \| |/__\ /' _/ / _//__\| _ \ __| +// | | ' | \/ |`._`.| \_| \/ | v / _| +// |_|\__|\__/ |___/ \__/\__/|_|_\___| +// + +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace NosCore.GameObject.Services.BattleService; + +public static class SkillCells +{ + public static sbyte[]? Parse(string? pattern) + { + if (string.IsNullOrWhiteSpace(pattern)) + { + return null; + } + + var parts = pattern.Split(','); + + // Pairs, so an odd count is a broken row, not a pattern with a spare coordinate. + if (parts.Length == 0 || parts.Length % 2 != 0) + { + return null; + } + + var cells = new sbyte[parts.Length]; + for (var i = 0; i < parts.Length; i++) + { + if (!sbyte.TryParse(parts[i], NumberStyles.Integer, CultureInfo.InvariantCulture, + out cells[i])) + { + return null; + } + } + + return cells; + } + + public static HashSet<(short X, short Y)> Resolve(sbyte[] pattern, short casterX, + short casterY, short targetX, short targetY) + { + var cells = new HashSet<(short, short)>(pattern.Length / 2); + + double dx = targetX - casterX; + double dy = targetY - casterY; + var len = Math.Sqrt((dx * dx) + (dy * dy)); + + double ux = 0, uy = -1; + if (len > 0.0001) + { + ux = dx / len; + uy = dy / len; + } + + foreach (var (cx, cy) in Pairs(pattern)) + { + double right = cx; + double forward = -cy; + + var x = (right * -uy) + (forward * ux); + var y = (right * ux) + (forward * uy); + + cells.Add(((short)(casterX + Math.Round(x, MidpointRounding.AwayFromZero)), + (short)(casterY + Math.Round(y, MidpointRounding.AwayFromZero)))); + } + + return cells; + } + + private static IEnumerable<(sbyte X, sbyte Y)> Pairs(sbyte[] pattern) + { + for (var i = 0; i + 1 < pattern.Length; i += 2) + { + yield return (pattern[i], pattern[i + 1]); + } + } +} diff --git a/src/NosCore.GameObject/Services/BattleService/SkillResolver.cs b/src/NosCore.GameObject/Services/BattleService/SkillResolver.cs index 00ebe6de1..ba08c482b 100644 --- a/src/NosCore.GameObject/Services/BattleService/SkillResolver.cs +++ b/src/NosCore.GameObject/Services/BattleService/SkillResolver.cs @@ -135,6 +135,7 @@ private SkillInfo BuildInfo(SkillDto main, SkillDto? upgrade, long castId) Element: main.Element, Duration: main.Duration, MpCost: main.MpCost, - BCards: _catalog.GetSkillBCards(main.SkillVNum)); + BCards: _catalog.GetSkillBCards(main.SkillVNum), + CellPattern: main.CellPattern); } } diff --git a/src/NosCore.GameObject/Services/BattleService/TargetResolver.cs b/src/NosCore.GameObject/Services/BattleService/TargetResolver.cs index efee3a5cf..c9edfef18 100644 --- a/src/NosCore.GameObject/Services/BattleService/TargetResolver.cs +++ b/src/NosCore.GameObject/Services/BattleService/TargetResolver.cs @@ -35,6 +35,12 @@ public IReadOnlyList Resolve(IAliveEntity attacker, IAliveEntity p return results; } + var pattern = SkillCells.Parse(skill.CellPattern); + var cells = pattern == null + ? null + : SkillCells.Resolve(pattern, attacker.PositionX, attacker.PositionY, + primaryTarget.PositionX, primaryTarget.PositionY); + var range = skill.TargetRange; var cx = primaryTarget.PositionX; var cy = primaryTarget.PositionY; @@ -44,7 +50,7 @@ public IReadOnlyList Resolve(IAliveEntity attacker, IAliveEntity p if (monster.VisualId == primaryTarget.VisualId && monster.VisualType == primaryTarget.VisualType) continue; if (!monster.IsAlive) continue; if (!IsEnemy(attacker, monster)) continue; - if (WithinRange(cx, cy, monster.PositionX, monster.PositionY, range)) + if (IsHit(cells, cx, cy, monster.PositionX, monster.PositionY, range)) { results.Add(monster); } @@ -61,7 +67,7 @@ public IReadOnlyList Resolve(IAliveEntity attacker, IAliveEntity p if (player.VisualId == attacker.VisualId && attacker.VisualType == VisualType.Player) continue; if (!player.IsAlive) continue; if (!IsEnemy(attacker, player)) continue; - if (WithinRange(cx, cy, player.PositionX, player.PositionY, range)) + if (IsHit(cells, cx, cy, player.PositionX, player.PositionY, range)) { results.Add(player); } @@ -86,6 +92,12 @@ private static bool IsEnemy(IAliveEntity attacker, IAliveEntity candidate) }; } + private static bool IsHit(HashSet<(short X, short Y)>? cells, short cx, short cy, short x, + short y, int range) + { + return cells != null ? cells.Contains((x, y)) : WithinRange(cx, cy, x, y, range); + } + private static bool WithinRange(short cx, short cy, short x, short y, int range) { return Math.Abs(cx - x) <= range && Math.Abs(cy - y) <= range; diff --git a/src/NosCore.Parser/Parsers/SkillParser.cs b/src/NosCore.Parser/Parsers/SkillParser.cs index d712aa73f..1cb1f1a70 100644 --- a/src/NosCore.Parser/Parsers/SkillParser.cs +++ b/src/NosCore.Parser/Parsers/SkillParser.cs @@ -54,6 +54,7 @@ public FluentParserBuilder BuildParser(string folder) .Field(x => x.Type, chunk => Convert.ToByte(chunk["TYPE"][0][5])) .Field(x => x.Element, chunk => Convert.ToByte(chunk["TYPE"][0][7])) .Field(x => x.Combo, chunk => AddCombos(chunk)) + .Field(x => x.CellPattern, chunk => ReadCellPattern(chunk)) .Field(x => x.CpCost, chunk => chunk["COST"][0][2] == "-1" ? (byte)0 : byte.Parse(chunk["COST"][0][2])) .Field(x => x.Price, chunk => Convert.ToInt32(chunk["COST"][0][3])) .Field(x => x.CastEffect, chunk => Convert.ToInt16(chunk["EFFECT"][0][3])) @@ -144,6 +145,48 @@ private List AddBCards(Dictionary chunks) return list; } + // CELL holds thirty cells at most; a longer pattern continues in the unused tail of + // COST. A continues flag on the last available triple means the row ran out, not that + // there is more, so the tail is read and may well be empty. + private static string? ReadCellPattern(Dictionary chunks) + { + if (!chunks.TryGetValue("CELL", out var cell) || cell.Length == 0) + { + return null; + } + + var cells = new List(); + var ranOut = ReadTriples(cell[0], 4, cells); + + if (ranOut && chunks.TryGetValue("COST", out var cost) && cost.Length > 0) + { + ReadTriples(cost[0], 5, cells); + } + + return cells.Count == 0 ? null : string.Join(",", cells); + } + + // Field [0] is empty and [1] is the section name, so CELL's triples start at [4] and + // COST's tail at [5]. + private static bool ReadTriples(string[] fields, int start, List into) + { + for (var i = start; i + 2 < fields.Length; i += 3) + { + if (!int.TryParse(fields[i], out var dx) + || !int.TryParse(fields[i + 1], out var dy) + || !int.TryParse(fields[i + 2], out var continues) + || continues == 0) + { + return false; + } + + into.Add(dx); + into.Add(dy); + } + + return true; + } + // FCOMBO's first field is a switch (has a chain / has not), not a step. The row starts // at index 2, so triplet j starts at 3 + j*3; counting from the switch shifted every // step by one field and the chain never fired. diff --git a/test/NosCore.GameObject.Tests/Services/BattleService/SkillCellsTests.cs b/test/NosCore.GameObject.Tests/Services/BattleService/SkillCellsTests.cs new file mode 100644 index 000000000..322444892 --- /dev/null +++ b/test/NosCore.GameObject.Tests/Services/BattleService/SkillCellsTests.cs @@ -0,0 +1,130 @@ +// __ _ __ __ ___ __ ___ ___ +// | \| |/__\ /' _/ / _//__\| _ \ __| +// | | ' | \/ |`._`.| \_| \/ | v / _| +// |_|\__|\__/ |___/ \__/\__/|_|_\___| +// + +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NosCore.GameObject.Services.BattleService; + +namespace NosCore.GameObject.Tests.Services.BattleService +{ + [TestClass] + public class SkillCellsTests + { + private const string PiercingShot = "0,-8,0,-7,0,-6,0,-5,0,-4,0,-3,0,-2,0,-1"; + + private static List<(sbyte dx, sbyte dy)> Pairs(sbyte[] flat) + { + var cells = new List<(sbyte, sbyte)>(); + for (var i = 0; i < flat.Length; i += 2) + { + cells.Add((flat[i], flat[i + 1])); + } + + return cells; + } + + [TestMethod] + public void AimingNorthLeavesThePatternAsWritten() + { + var pattern = SkillCells.Parse(PiercingShot)!; + var cells = SkillCells.Resolve(pattern, 50, 50, 50, 40); + + Assert.AreEqual(8, cells.Count); + for (short dy = 1; dy <= 8; dy++) + { + Assert.IsTrue(cells.Contains((50, (short)(50 - dy))), $"missing (50,{50 - dy})"); + } + } + + [TestMethod] + public void AimingSouthTurnsThePatternAround() + { + var pattern = SkillCells.Parse(PiercingShot)!; + var cells = SkillCells.Resolve(pattern, 50, 50, 50, 60); + + Assert.AreEqual(8, cells.Count); + for (short dy = 1; dy <= 8; dy++) + { + Assert.IsTrue(cells.Contains((50, (short)(50 + dy))), $"missing (50,{50 + dy})"); + } + } + + [TestMethod] + public void AimingEastPutsTheLineOnTheXAxis() + { + var pattern = SkillCells.Parse(PiercingShot)!; + var cells = SkillCells.Resolve(pattern, 50, 50, 60, 50); + + Assert.AreEqual(8, cells.Count); + for (short dx = 1; dx <= 8; dx++) + { + Assert.IsTrue(cells.Contains(((short)(50 + dx), 50)), $"missing ({50 + dx},50)"); + } + } + + // Diagonals round, so demand the quadrant and the reach rather than exact cells. + [TestMethod] + public void AimingDiagonallyPointsTheLineAtTheTarget() + { + var pattern = SkillCells.Parse(PiercingShot)!; + var cells = SkillCells.Resolve(pattern, 50, 50, 60, 40); + + Assert.IsTrue(cells.All(c => c.X >= 50 && c.Y <= 50), + "a cell landed outside the target's quadrant"); + + var furthest = cells.Max(c => System.Math.Max(System.Math.Abs(c.X - 50), + System.Math.Abs(c.Y - 50))); + Assert.AreEqual(6, furthest, + "eight diagonal steps reach six cells on each axis, not eight"); + } + + // Without the zero-distance guard the normalisation divides by zero and every cell + // collapses onto the caster, silently. + [TestMethod] + public void CastingOnYourOwnCellKeepsTheWrittenOrientation() + { + var pattern = SkillCells.Parse(PiercingShot)!; + var cells = SkillCells.Resolve(pattern, 50, 50, 50, 50); + + Assert.AreEqual(8, cells.Count); + Assert.IsTrue(cells.Contains((50, 42))); + } + + // Most skills have no drawing: 1890 of the 1958. The column is null for them. + [TestMethod] + public void ASkillWithoutADrawingHasNoPattern() + { + Assert.IsNull(SkillCells.Parse(null)); + Assert.IsNull(SkillCells.Parse("")); + Assert.IsNull(SkillCells.Parse(" ")); + } + + // A broken row must not stop a fight: the pattern decides who a skill hits, and throwing + // here would take down the cast instead of degrading it to a single target. + [TestMethod] + public void AMalformedColumnIsNoPatternRatherThanAnException() + { + Assert.IsNull(SkillCells.Parse("0,-1,0"), "an odd count is a broken row, not a cell"); + Assert.IsNull(SkillCells.Parse("0,-1,x,2")); + Assert.IsNull(SkillCells.Parse("0,-1,,2")); + Assert.IsNull(SkillCells.Parse("0,-1,200,2"), "200 does not fit an sbyte"); + } + + [TestMethod] + public void AWellFormedColumnComesBackAsPairs() + { + var pattern = SkillCells.Parse(PiercingShot)!; + Assert.AreEqual(16, pattern.Length); + + var cells = Pairs(pattern); + Assert.AreEqual(8, cells.Count); + Assert.IsTrue(cells.All(c => c.dx == 0), "the line is one cell wide"); + Assert.AreEqual(-8, cells.Min(c => c.dy)); + Assert.AreEqual(-1, cells.Max(c => c.dy)); + } + } +} diff --git a/test/NosCore.Parser.Tests/SkillParserTests.cs b/test/NosCore.Parser.Tests/SkillParserTests.cs index e96301b68..36f0704c8 100644 --- a/test/NosCore.Parser.Tests/SkillParserTests.cs +++ b/test/NosCore.Parser.Tests/SkillParserTests.cs @@ -101,12 +101,14 @@ private static string CreateSkillData( short mpCost = 0, short itemVNum = 0, byte levelMinimum = 0, - string fcombo = "0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0") + string fcombo = "0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0", + (int dx, int dy)[]? cells = null, + (int dx, int dy)[]? tailCells = null) { return $"\tVNUM\t{skillVNum}\r\n" + $"\tNAME\t{name}\r\n" + $"\tTYPE\t{skillType}\t{castId}\t{classType}\t{type}\t0\t{element}\t0\r\n" + - $"\tCOST\t{cpCost}\t{price}\t0\r\n" + + $"\tCOST\t{cpCost}\t{price}\t0{Triples(tailCells, 30)}\r\n" + $"\tLEVEL\t{levelMinimum}\t-1\t-1\t-1\t-1\r\n" + $"\tEFFECT\t0\t{castEffect}\t{castAnimation}\t{effect}\t{attackAnimation}\t0\t0\t0\t0\r\n" + $"\tTARGET\t{targetType}\t{hitType}\t{range}\t{targetRange}\t0\r\n" + @@ -117,11 +119,96 @@ private static string CreateSkillData( "\tBASIC\t3\t0\t0\t0\t0\t0\r\n" + "\tBASIC\t4\t0\t0\t0\t0\t0\r\n" + $"\tFCOMBO\t{fcombo}\r\n" + - "\tCELL\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\t0\r\n" + + $"\tCELL\t8\t8{Triples(cells, 30)}\r\n" + "\tZ_DESC\t0\r\n" + "#========================================================="; } + private static string Triples((int dx, int dy)[]? cells, int slots) + { + var sb = new System.Text.StringBuilder(); + for (var i = 0; i < slots; i++) + { + if ((cells != null) && (i < cells.Length)) + { + sb.Append($"\t{cells[i].dx}\t{cells[i].dy}\t1"); + } + else + { + sb.Append("\t0\t0\t0"); + } + } + + return sb.ToString(); + } + + // A line of `length` cells straight in front of the caster: dy negative is forward. + private static (int dx, int dy)[] Line(int length, int dx = 0) + { + var cells = new (int, int)[length]; + for (var i = 0; i < length; i++) + { + cells[i] = (dx, -length + i); + } + + return cells; + } + + private async Task ParseOneAsync(string content) + { + CreateTestFile(content); + var parser = new SkillParser(_bCardDaoMock.Object, _comboDaoMock.Object, + _skillDaoMock.Object, NullLoggerFactory.Instance, _logLanguageMock.Object); + await parser.InsertSkillsAsync(_tempFolder); + Assert.AreEqual(1, _savedSkills.Count); + return _savedSkills[0]; + } + + // An empty CELL is the overwhelming majority - 1890 of the 1958 skills - and has to come + // out as nothing rather than as a pattern of zero-zero cells. + [TestMethod] + public async Task ASkillWithNoPatternHasNoCellPattern() + { + var skill = await ParseOneAsync(CreateSkillData(skillVNum: 1)); + Assert.IsNull(skill.CellPattern); + } + + // Skill 244, the archer's piercing shot: the row of eight squares in front of the caster. + [TestMethod] + public async Task AStraightLineComesOutAsEightPairs() + { + var skill = await ParseOneAsync(CreateSkillData(skillVNum: 244, cells: Line(8))); + Assert.AreEqual("0,-8,0,-7,0,-6,0,-5,0,-4,0,-3,0,-2,0,-1", skill.CellPattern); + } + + // The list ends at the first triple whose third field is zero, not at the end of the row. + [TestMethod] + public async Task ThePatternStopsAtTheFirstZeroContinuesFlag() + { + var skill = await ParseOneAsync(CreateSkillData(skillVNum: 2, cells: Line(3))); + Assert.AreEqual("0,-3,0,-2,0,-1", skill.CellPattern); + } + + // CELL holds thirty cells and no more: 2 + 30*3 + 1 = 93 fields. Ten skills need more and + // continue in the tail of COST, which is zeros for the other 1948. + [TestMethod] + public async Task APatternLongerThanThirtyContinuesInTheTailOfCost() + { + var skill = await ParseOneAsync(CreateSkillData(skillVNum: 1857, + cells: Line(30), tailCells: Line(4, dx: 1))); + + Assert.AreEqual(34, skill.CellPattern!.Split(',').Length / 2); + Assert.IsTrue(skill.CellPattern!.EndsWith("1,-4,1,-3,1,-2,1,-1"), + "the tail is appended after the thirty CELL holds"); + } + + [TestMethod] + public async Task AFullCellWithAnEmptyTailStopsAtThirty() + { + var skill = await ParseOneAsync(CreateSkillData(skillVNum: 1175, cells: Line(30))); + Assert.AreEqual(30, skill.CellPattern!.Split(',').Length / 2); + } + [TestMethod] public async Task SkillParser_ReadsComboTripletsAfterTheSwitch() {