From 5693fa8ce8483765cba4bdfd8e086172cfbef961 Mon Sep 17 00:00:00 2001 From: Ilya Kozhemyakin Date: Tue, 6 May 2025 01:23:34 +0400 Subject: [PATCH] =?UTF-8?q?=D0=91=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20=D0=BD=D0=B0=D0=B4=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=80=D1=81=D0=B8=D0=BD=D0=B3=D0=BE=D0=BC=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BA=D0=BB=D0=B8=D0=BD=D0=B0=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Aonprd/DataBuilder/AonprdDataBuilder.cs | 2 + .../Subclasses/ApparitionProcessor.cs | 2 +- .../Processors/Types/SpellProcessor.cs | 77 ++++++++- Pf2eDataScrapper/Importers/ProcessorInput.cs | 26 +++ ...signer.cs => 20250505211157_1.Designer.cs} | 149 +++++++----------- ...0250505114131_1.cs => 20250505211157_1.cs} | 42 ++--- .../Migrations/Pf2eDbContextModelSnapshot.cs | 147 +++++++---------- Pf2eModel/Model/Enum/SpellTradition.cs | 3 + Pf2eModel/Model/Structure/Spell.cs | 26 +-- .../Structure/SpellRelation/SpellArea.cs | 8 +- Pf2eModel/Pf2eDbContext.cs | 2 + Pf2eModel/Pf2eDbContextModel.cs | 30 ++-- Pf2eViewer/EntityView.cs | 4 + 13 files changed, 279 insertions(+), 239 deletions(-) rename Pf2eModel/Migrations/{20250505114131_1.Designer.cs => 20250505211157_1.Designer.cs} (96%) rename Pf2eModel/Migrations/{20250505114131_1.cs => 20250505211157_1.cs} (97%) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs index 70699af..031ebd8 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs @@ -90,6 +90,8 @@ internal class AonprdDataBuilder(AonprdLoader loader) continue; } + SubProcessProgressBar.Refresh(0, ""); + SubProcessProgressBar.Max = Compendium.Count([item.Attribute.Type]); foreach (AonprdLoader.LoaderEntry entry in Loader.Entries(item.Attribute.Type)) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs index b094c8b..7b15874 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs @@ -9,7 +9,7 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Subclasses; -[AonprdProcessor("apparition", ["class", "spell", "trainings"])] +[AonprdProcessor("apparition", ["class", "spell", "skill"])] internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclassProcessor(input) { public override Subclass Object { get; } = new(); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs index 59158f2..617f861 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SpellProcessor.cs @@ -1,4 +1,5 @@ -using Pf2eModel.Model.Common.I18n; +using Newtonsoft.Json.Linq; +using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.SpellRelation; @@ -21,6 +22,15 @@ internal class SpellProcessor(AonprdProcessorInput input) : AbstractEntityProces Object.Level = Input.GetRequired("level"); Object.Actions = Input.EnumOptional("actions", ActionEnum.Unknown); Object.IsFocus = Input.GetRequired("spell_type") == "Focus"; + Object.Duration = Input.GetOptional("duration", -1); + Object.Range = Input.GetOptional("range", -1); + + Object.Trigger = Input.GetOptionalLocale("trigger_markdown", LocaleType.English); + Object.Target = Input.GetOptionalLocale("target_markdown", LocaleType.English); + + Object.Traditions = (Input.GetOptional("tradition") ?? []) + .Select(token => Input.Enum(token.ToString())) + .ToArray(); switch (Input.GetRequired("spell_type")) { @@ -32,25 +42,78 @@ internal class SpellProcessor(AonprdProcessorInput input) : AbstractEntityProces break; } - foreach (string areaType in Input.GetOptional("area_type") ?? []) + ProcessArea(); + } + + private void ProcessArea() + { + List areaTypes = (Input.GetOptional("area_type") ?? []).ToList(); + + switch (areaTypes.Count) { - Object.SpellAreas.Add( + case 0: + return; + + case 1: + A( + new() + { + Spell = Object, + Type = Input.Enum(areaTypes[0]), + Details = Input.GetOptionalLocale("area_raw", LocaleType.English), + } + ); + + return; + } + + List areaDetails = (Input.GetOptional("area_raw") ?? "") + .Replace(", or", ",") + .Replace(" or ", ", ") + .Split(", ") + .ToList(); + + if (areaTypes.Remove("square")) + { + A(new() { Spell = Object, Type = SpellAreaType.Square }); + } + + if (areaTypes.Count != areaDetails.Count) + { + throw new( + $"Area details count does not match area types count for spell {Entity.Name[LocaleType.English]}" + ); + } + + for (int i = 0; i < areaTypes.Count; i++) + { + A( new() { Spell = Object, - Type = Input.EnumRequired(areaType), - Value = 0, + Type = Input.Enum(areaTypes[i]), + Details = new() { [LocaleType.English] = areaDetails[i] }, } ); } + + return; + + void A(SpellArea spellArea) + { + Object.SpellAreas.Add(spellArea); + Context.SpellAreas.Add(spellArea); + } } protected override string GetDescription() => base.GetDescription() + AddedDescription( [ - "area_raw", + "range", + "range_raw", "bloodline_markdown", + "cost_markdown", "deity_markdown", "domain_markdown", "lesson_markdown", @@ -59,9 +122,7 @@ internal class SpellProcessor(AonprdProcessorInput input) : AbstractEntityProces "summary_markdown", "tradition_markdown", "patron_theme_markdown", - "trigger_markdown", "saving_throw_markdown", - "target_markdown", ] ); } diff --git a/Pf2eDataScrapper/Importers/ProcessorInput.cs b/Pf2eDataScrapper/Importers/ProcessorInput.cs index 827797e..50a04a3 100644 --- a/Pf2eDataScrapper/Importers/ProcessorInput.cs +++ b/Pf2eDataScrapper/Importers/ProcessorInput.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json.Linq; using NpgsqlTypes; +using Pf2eModel.Model.Common.I18n; namespace Pf2eDataScrapper.Importers; @@ -24,6 +25,16 @@ internal class ProcessorInput(JObject input) : token.ToObject() ?? default; } + public virtual T GetOptional(string path, T defaultValue) + { + JToken? token = Input.SelectToken(path) ?? null; + if (token is null || token.Type == JTokenType.Null) + { + return defaultValue; + } + return token.ToObject() ?? defaultValue; + } + public virtual T EnumRequired(string path) where T : Enum { @@ -54,6 +65,21 @@ internal class ProcessorInput(JObject input) } } + public virtual LocaleString GetRequiredLocale(string path, LocaleType type) + { + return new() { [type] = GetRequired(path) }; + } + + public virtual LocaleString? GetOptionalLocale(string path, LocaleType type) + { + string? value = GetOptional(path); + if (value is null) + { + return null; + } + return new() { [type] = value }; + } + public virtual T Enum(string value) where T : Enum { diff --git a/Pf2eModel/Migrations/20250505114131_1.Designer.cs b/Pf2eModel/Migrations/20250505211157_1.Designer.cs similarity index 96% rename from Pf2eModel/Migrations/20250505114131_1.Designer.cs rename to Pf2eModel/Migrations/20250505211157_1.Designer.cs index 9ac18ea..ee58128 100644 --- a/Pf2eModel/Migrations/20250505114131_1.Designer.cs +++ b/Pf2eModel/Migrations/20250505211157_1.Designer.cs @@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Migrations { [DbContext(typeof(Pf2eDbContext))] - [Migration("20250505114131_1")] + [Migration("20250505211157_1")] partial class _1 { /// @@ -25,6 +25,7 @@ namespace Pf2eModel.Migrations .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_category", new[] { "unknown", "archetype", "class-feature", "class-feat", "offensive", "defensive", "interaction", "familiar", "army_tactic", "army_war_action", "kingdom_activity", "kingdom_feat", "kingdom_feature" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_enum", new[] { "unknown", "1", "2", "3", "reaction", "free", "passive", "other", "1-3", "1-2", "2-3", "2/2 rounds", "reaction/1", "reaction/2", "reaction/3", "free/1", "1 minute", "5 minutes", "10 minutes", "30 minutes", "1 hour" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ancestry_vision", new[] { "normal", "low-light-vision", "dark-vision" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "attribute_values", new[] { "free", "str", "dex", "con", "int", "wis", "cha" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "class_progression_type", new[] { "entity", "ancestry_feat", "class_feat", "general_feat", "skill_feat", "skill_increase" }); @@ -39,9 +40,10 @@ namespace Pf2eModel.Migrations NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "size", new[] { "tiny", "small", "medium", "large" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "source_category", new[] { "rulebook", "adventure-path", "lost-omens", "adventures", "comics", "blog-posts", "society" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder", "varies", "cuboid", "circle", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_damage_category", new[] { "persistent", "splash" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_damage_kinds", new[] { "damage", "healing", "any" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_tradition", new[] { "none", "divine", "occult", "arcane", "primal", "elemental", "all" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "training_stage", new[] { "untrained", "trained", "expert", "master", "legendary" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "trait_type", new[] { "regular", "class", "tradition" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -305,19 +307,19 @@ namespace Pf2eModel.Migrations b.Property("EntityId") .HasColumnType("integer") .HasColumnName("entity_id") - .HasColumnOrder(0); + .HasColumnOrder(1); b.Property("TargetId") .HasColumnType("integer") .HasColumnName("target_id") - .HasColumnOrder(1); + .HasColumnOrder(2); b.Property("Md5") .IsRequired() .HasMaxLength(32) .HasColumnType("character varying(32)") .HasColumnName("md5") - .HasColumnOrder(3) + .HasColumnOrder(5) .HasComment("Контроль версии объекта"); b.Property("Suffix") @@ -325,7 +327,7 @@ namespace Pf2eModel.Migrations .HasMaxLength(255) .HasColumnType("character varying(255)") .HasColumnName("suffix") - .HasColumnOrder(2) + .HasColumnOrder(4) .HasComment("Часть URL до объекта. Для источников Foundry - полный UUID объекта"); b.HasKey("EntityId", "TargetId") @@ -439,35 +441,15 @@ namespace Pf2eModel.Migrations .HasColumnName("id") .HasColumnOrder(1); - b.Property("Actions") - .HasColumnType("integer") + b.Property("Actions") + .HasColumnType("action_enum") .HasColumnName("actions") - .HasColumnOrder(3); + .HasColumnOrder(2); b.Property("Extra") .HasColumnType("text") .HasColumnName("extra") - .HasColumnOrder(6); - - b.Property("IsFreeAction") - .HasColumnType("boolean") - .HasColumnName("is_free_action") - .HasColumnOrder(6); - - b.Property("IsPassive") - .HasColumnType("boolean") - .HasColumnName("is_passive") - .HasColumnOrder(6); - - b.Property("IsReaction") - .HasColumnType("boolean") - .HasColumnName("is_reaction") - .HasColumnOrder(5); - - b.Property("MaxActions") - .HasColumnType("integer") - .HasColumnName("max_actions") - .HasColumnOrder(4); + .HasColumnOrder(3); b.HasKey("Id") .HasName("pk_actions"); @@ -775,8 +757,8 @@ namespace Pf2eModel.Migrations .HasColumnName("spellcasting") .HasColumnOrder(5); - b.Property("Tradition") - .HasColumnType("integer") + b.Property("Tradition") + .HasColumnType("spell_tradition") .HasColumnName("tradition") .HasColumnOrder(6); @@ -1263,65 +1245,47 @@ namespace Pf2eModel.Migrations .HasColumnName("id") .HasColumnOrder(1); - b.Property("CanHeightening") - .HasColumnType("boolean") - .HasColumnName("can_heightening") - .HasColumnOrder(11); - - b.Property("Cost") - .HasColumnType("integer") - .HasColumnName("cost") + b.Property("Actions") + .HasColumnType("action_enum") + .HasColumnName("actions") .HasColumnOrder(3); - b.Property("Counteraction") - .HasColumnType("boolean") - .HasColumnName("counteraction") + b.Property("Duration") + .HasMaxLength(255) + .HasColumnType("integer") + .HasColumnName("duration") .HasColumnOrder(4); - b.Property("Duration") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("duration") - .HasColumnOrder(6); + b.Property("IsFocus") + .HasColumnType("boolean") + .HasColumnName("is_focus") + .HasColumnOrder(5); b.Property("Level") .HasColumnType("integer") .HasColumnName("level") .HasColumnOrder(2); - b.Property("Range") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") + b.Property("Range") + .HasColumnType("integer") .HasColumnName("range") - .HasColumnOrder(7); - - b.Property("Requirements") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("requirements") - .HasColumnOrder(8); - - b.Property("Sustained") - .HasColumnType("boolean") - .HasColumnName("sustained") - .HasColumnOrder(5); + .HasColumnOrder(6); b.Property("Target") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") + .HasColumnType("jsonb") .HasColumnName("target") - .HasColumnOrder(9); + .HasColumnOrder(8); - b.Property("Time") + b.PrimitiveCollection("Traditions") .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("time") - .HasColumnOrder(10); + .HasColumnType("spell_tradition[]") + .HasColumnName("traditions") + .HasColumnOrder(6); + + b.Property("Trigger") + .HasColumnType("jsonb") + .HasColumnName("trigger") + .HasColumnOrder(7); b.HasKey("Id") .HasName("pk_spells"); @@ -1331,30 +1295,36 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.SpellRelation.SpellArea", b => { - b.Property("SpellId") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasColumnName("spell_id") + .HasColumnName("id") .HasColumnOrder(1); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Details") .HasMaxLength(255) - .HasColumnType("character varying(255)") + .HasColumnType("jsonb") .HasColumnName("details") + .HasColumnOrder(3); + + b.Property("SpellId") + .HasColumnType("integer") + .HasColumnName("spell_id") .HasColumnOrder(2); b.Property("Type") .HasColumnType("spell_area_type") .HasColumnName("type") - .HasColumnOrder(3); - - b.Property("Value") - .HasColumnType("integer") - .HasColumnName("value") .HasColumnOrder(4); - b.HasKey("SpellId") + b.HasKey("Id") .HasName("pk_spell_areas"); + b.HasIndex("SpellId") + .HasDatabaseName("ix_spell_areas_spell_id"); + b.ToTable("spell_areas", (string)null); }); @@ -2109,9 +2079,10 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.SpellRelation.SpellArea", b => { b.HasOne("Pf2eModel.Model.Structure.Spell", "Spell") - .WithOne("SpellArea") - .HasForeignKey("Pf2eModel.Model.Structure.SpellRelation.SpellArea", "SpellId") + .WithMany("SpellAreas") + .HasForeignKey("SpellId") .OnDelete(DeleteBehavior.Cascade) + .IsRequired() .HasConstraintName("fk_spell_areas_spells_spell_id"); b.Navigation("Spell"); @@ -2120,7 +2091,7 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.SpellRelation.SpellDamage", b => { b.HasOne("Pf2eModel.Model.Structure.Spell", "Spell") - .WithMany("SpellDamages") + .WithMany() .HasForeignKey("SpellId") .OnDelete(DeleteBehavior.Cascade) .IsRequired() @@ -2276,9 +2247,7 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b => { - b.Navigation("SpellArea"); - - b.Navigation("SpellDamages"); + b.Navigation("SpellAreas"); b.Navigation("SpellDefenses"); diff --git a/Pf2eModel/Migrations/20250505114131_1.cs b/Pf2eModel/Migrations/20250505211157_1.cs similarity index 97% rename from Pf2eModel/Migrations/20250505114131_1.cs rename to Pf2eModel/Migrations/20250505211157_1.cs index 7ac8590..5a380f6 100644 --- a/Pf2eModel/Migrations/20250505114131_1.cs +++ b/Pf2eModel/Migrations/20250505211157_1.cs @@ -15,6 +15,7 @@ namespace Pf2eModel.Migrations { migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:action_category", "unknown,archetype,class-feature,class-feat,offensive,defensive,interaction,familiar,army_tactic,army_war_action,kingdom_activity,kingdom_feat,kingdom_feature") + .Annotation("Npgsql:Enum:action_enum", "unknown,1,2,3,reaction,free,passive,other,1-3,1-2,2-3,2/2 rounds,reaction/1,reaction/2,reaction/3,free/1,1 minute,5 minutes,10 minutes,30 minutes,1 hour") .Annotation("Npgsql:Enum:ancestry_vision", "normal,low-light-vision,dark-vision") .Annotation("Npgsql:Enum:attribute_values", "free,str,dex,con,int,wis,cha") .Annotation("Npgsql:Enum:class_progression_type", "entity,ancestry_feat,class_feat,general_feat,skill_feat,skill_increase") @@ -29,9 +30,10 @@ namespace Pf2eModel.Migrations .Annotation("Npgsql:Enum:locale_type", "english,legacy_english,russian,legacy,hobby,other") .Annotation("Npgsql:Enum:size", "tiny,small,medium,large") .Annotation("Npgsql:Enum:source_category", "rulebook,adventure-path,lost-omens,adventures,comics,blog-posts,society") - .Annotation("Npgsql:Enum:spell_area_type", "burst,emanation,cone,line,square,cube,cylinder") + .Annotation("Npgsql:Enum:spell_area_type", "burst,emanation,cone,line,square,cube,cylinder,varies,cuboid,circle,other") .Annotation("Npgsql:Enum:spell_damage_category", "persistent,splash") .Annotation("Npgsql:Enum:spell_damage_kinds", "damage,healing,any") + .Annotation("Npgsql:Enum:spell_tradition", "none,divine,occult,arcane,primal,elemental,all") .Annotation("Npgsql:Enum:training_stage", "untrained,trained,expert,master,legendary") .Annotation("Npgsql:Enum:trait_type", "regular,class,tradition"); @@ -133,11 +135,7 @@ namespace Pf2eModel.Migrations columns: table => new { id = table.Column(type: "integer", nullable: false), - actions = table.Column(type: "integer", nullable: true), - max_actions = table.Column(type: "integer", nullable: true), - is_reaction = table.Column(type: "boolean", nullable: false), - is_free_action = table.Column(type: "boolean", nullable: false), - is_passive = table.Column(type: "boolean", nullable: false), + actions = table.Column(type: "action_enum", nullable: false), extra = table.Column(type: "text", nullable: true) }, constraints: table => @@ -197,7 +195,7 @@ namespace Pf2eModel.Migrations hp = table.Column(type: "integer", nullable: false), perception = table.Column(type: "integer", nullable: false), spellcasting = table.Column(type: "boolean", nullable: false), - tradition = table.Column(type: "integer", nullable: false), + tradition = table.Column(type: "spell_tradition", nullable: false), additional_trained_skills = table.Column(type: "integer", nullable: false) }, constraints: table => @@ -301,15 +299,13 @@ namespace Pf2eModel.Migrations { id = table.Column(type: "integer", nullable: false), level = table.Column(type: "integer", nullable: false), - cost = table.Column(type: "integer", nullable: false), - counteraction = table.Column(type: "boolean", nullable: false), - sustained = table.Column(type: "boolean", nullable: false), - duration = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), - range = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), - requirements = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), - target = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), - time = table.Column(type: "character varying(255)", maxLength: 255, nullable: false), - can_heightening = table.Column(type: "boolean", nullable: false) + actions = table.Column(type: "action_enum", nullable: false), + duration = table.Column(type: "integer", maxLength: 255, nullable: true), + is_focus = table.Column(type: "boolean", nullable: false), + traditions = table.Column(type: "spell_tradition[]", nullable: false), + range = table.Column(type: "integer", nullable: false), + trigger = table.Column(type: "jsonb", nullable: true), + target = table.Column(type: "jsonb", nullable: true) }, constraints: table => { @@ -861,14 +857,15 @@ namespace Pf2eModel.Migrations name: "spell_areas", columns: table => new { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), spell_id = table.Column(type: "integer", nullable: false), - details = table.Column(type: "character varying(255)", maxLength: 255, nullable: true), - type = table.Column(type: "spell_area_type", nullable: false), - value = table.Column(type: "integer", nullable: false) + details = table.Column(type: "jsonb", maxLength: 255, nullable: true), + type = table.Column(type: "spell_area_type", nullable: false) }, constraints: table => { - table.PrimaryKey("pk_spell_areas", x => x.spell_id); + table.PrimaryKey("pk_spell_areas", x => x.id); table.ForeignKey( name: "fk_spell_areas_spells_spell_id", column: x => x.spell_id, @@ -1206,6 +1203,11 @@ namespace Pf2eModel.Migrations table: "languages", column: "entity_id"); + migrationBuilder.CreateIndex( + name: "ix_spell_areas_spell_id", + table: "spell_areas", + column: "spell_id"); + migrationBuilder.CreateIndex( name: "ix_spell_damages_spell_id", table: "spell_damages", diff --git a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs index 67456e4..a0dd2fc 100644 --- a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs +++ b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs @@ -22,6 +22,7 @@ namespace Pf2eModel.Migrations .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_category", new[] { "unknown", "archetype", "class-feature", "class-feat", "offensive", "defensive", "interaction", "familiar", "army_tactic", "army_war_action", "kingdom_activity", "kingdom_feat", "kingdom_feature" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_enum", new[] { "unknown", "1", "2", "3", "reaction", "free", "passive", "other", "1-3", "1-2", "2-3", "2/2 rounds", "reaction/1", "reaction/2", "reaction/3", "free/1", "1 minute", "5 minutes", "10 minutes", "30 minutes", "1 hour" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ancestry_vision", new[] { "normal", "low-light-vision", "dark-vision" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "attribute_values", new[] { "free", "str", "dex", "con", "int", "wis", "cha" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "class_progression_type", new[] { "entity", "ancestry_feat", "class_feat", "general_feat", "skill_feat", "skill_increase" }); @@ -36,9 +37,10 @@ namespace Pf2eModel.Migrations NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "size", new[] { "tiny", "small", "medium", "large" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "source_category", new[] { "rulebook", "adventure-path", "lost-omens", "adventures", "comics", "blog-posts", "society" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder", "varies", "cuboid", "circle", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_damage_category", new[] { "persistent", "splash" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_damage_kinds", new[] { "damage", "healing", "any" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_tradition", new[] { "none", "divine", "occult", "arcane", "primal", "elemental", "all" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "training_stage", new[] { "untrained", "trained", "expert", "master", "legendary" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "trait_type", new[] { "regular", "class", "tradition" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -302,19 +304,19 @@ namespace Pf2eModel.Migrations b.Property("EntityId") .HasColumnType("integer") .HasColumnName("entity_id") - .HasColumnOrder(0); + .HasColumnOrder(1); b.Property("TargetId") .HasColumnType("integer") .HasColumnName("target_id") - .HasColumnOrder(1); + .HasColumnOrder(2); b.Property("Md5") .IsRequired() .HasMaxLength(32) .HasColumnType("character varying(32)") .HasColumnName("md5") - .HasColumnOrder(3) + .HasColumnOrder(5) .HasComment("Контроль версии объекта"); b.Property("Suffix") @@ -322,7 +324,7 @@ namespace Pf2eModel.Migrations .HasMaxLength(255) .HasColumnType("character varying(255)") .HasColumnName("suffix") - .HasColumnOrder(2) + .HasColumnOrder(4) .HasComment("Часть URL до объекта. Для источников Foundry - полный UUID объекта"); b.HasKey("EntityId", "TargetId") @@ -436,35 +438,15 @@ namespace Pf2eModel.Migrations .HasColumnName("id") .HasColumnOrder(1); - b.Property("Actions") - .HasColumnType("integer") + b.Property("Actions") + .HasColumnType("action_enum") .HasColumnName("actions") - .HasColumnOrder(3); + .HasColumnOrder(2); b.Property("Extra") .HasColumnType("text") .HasColumnName("extra") - .HasColumnOrder(6); - - b.Property("IsFreeAction") - .HasColumnType("boolean") - .HasColumnName("is_free_action") - .HasColumnOrder(6); - - b.Property("IsPassive") - .HasColumnType("boolean") - .HasColumnName("is_passive") - .HasColumnOrder(6); - - b.Property("IsReaction") - .HasColumnType("boolean") - .HasColumnName("is_reaction") - .HasColumnOrder(5); - - b.Property("MaxActions") - .HasColumnType("integer") - .HasColumnName("max_actions") - .HasColumnOrder(4); + .HasColumnOrder(3); b.HasKey("Id") .HasName("pk_actions"); @@ -772,8 +754,8 @@ namespace Pf2eModel.Migrations .HasColumnName("spellcasting") .HasColumnOrder(5); - b.Property("Tradition") - .HasColumnType("integer") + b.Property("Tradition") + .HasColumnType("spell_tradition") .HasColumnName("tradition") .HasColumnOrder(6); @@ -1260,65 +1242,47 @@ namespace Pf2eModel.Migrations .HasColumnName("id") .HasColumnOrder(1); - b.Property("CanHeightening") - .HasColumnType("boolean") - .HasColumnName("can_heightening") - .HasColumnOrder(11); - - b.Property("Cost") - .HasColumnType("integer") - .HasColumnName("cost") + b.Property("Actions") + .HasColumnType("action_enum") + .HasColumnName("actions") .HasColumnOrder(3); - b.Property("Counteraction") - .HasColumnType("boolean") - .HasColumnName("counteraction") + b.Property("Duration") + .HasMaxLength(255) + .HasColumnType("integer") + .HasColumnName("duration") .HasColumnOrder(4); - b.Property("Duration") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("duration") - .HasColumnOrder(6); + b.Property("IsFocus") + .HasColumnType("boolean") + .HasColumnName("is_focus") + .HasColumnOrder(5); b.Property("Level") .HasColumnType("integer") .HasColumnName("level") .HasColumnOrder(2); - b.Property("Range") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") + b.Property("Range") + .HasColumnType("integer") .HasColumnName("range") - .HasColumnOrder(7); - - b.Property("Requirements") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("requirements") - .HasColumnOrder(8); - - b.Property("Sustained") - .HasColumnType("boolean") - .HasColumnName("sustained") - .HasColumnOrder(5); + .HasColumnOrder(6); b.Property("Target") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") + .HasColumnType("jsonb") .HasColumnName("target") - .HasColumnOrder(9); + .HasColumnOrder(8); - b.Property("Time") + b.PrimitiveCollection("Traditions") .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("time") - .HasColumnOrder(10); + .HasColumnType("spell_tradition[]") + .HasColumnName("traditions") + .HasColumnOrder(6); + + b.Property("Trigger") + .HasColumnType("jsonb") + .HasColumnName("trigger") + .HasColumnOrder(7); b.HasKey("Id") .HasName("pk_spells"); @@ -1328,30 +1292,36 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.SpellRelation.SpellArea", b => { - b.Property("SpellId") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("integer") - .HasColumnName("spell_id") + .HasColumnName("id") .HasColumnOrder(1); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Details") .HasMaxLength(255) - .HasColumnType("character varying(255)") + .HasColumnType("jsonb") .HasColumnName("details") + .HasColumnOrder(3); + + b.Property("SpellId") + .HasColumnType("integer") + .HasColumnName("spell_id") .HasColumnOrder(2); b.Property("Type") .HasColumnType("spell_area_type") .HasColumnName("type") - .HasColumnOrder(3); - - b.Property("Value") - .HasColumnType("integer") - .HasColumnName("value") .HasColumnOrder(4); - b.HasKey("SpellId") + b.HasKey("Id") .HasName("pk_spell_areas"); + b.HasIndex("SpellId") + .HasDatabaseName("ix_spell_areas_spell_id"); + b.ToTable("spell_areas", (string)null); }); @@ -2106,9 +2076,10 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.SpellRelation.SpellArea", b => { b.HasOne("Pf2eModel.Model.Structure.Spell", "Spell") - .WithOne("SpellArea") - .HasForeignKey("Pf2eModel.Model.Structure.SpellRelation.SpellArea", "SpellId") + .WithMany("SpellAreas") + .HasForeignKey("SpellId") .OnDelete(DeleteBehavior.Cascade) + .IsRequired() .HasConstraintName("fk_spell_areas_spells_spell_id"); b.Navigation("Spell"); @@ -2117,7 +2088,7 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.SpellRelation.SpellDamage", b => { b.HasOne("Pf2eModel.Model.Structure.Spell", "Spell") - .WithMany("SpellDamages") + .WithMany() .HasForeignKey("SpellId") .OnDelete(DeleteBehavior.Cascade) .IsRequired() @@ -2273,9 +2244,7 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Structure.Spell", b => { - b.Navigation("SpellArea"); - - b.Navigation("SpellDamages"); + b.Navigation("SpellAreas"); b.Navigation("SpellDefenses"); diff --git a/Pf2eModel/Model/Enum/SpellTradition.cs b/Pf2eModel/Model/Enum/SpellTradition.cs index 223c047..a5ff392 100644 --- a/Pf2eModel/Model/Enum/SpellTradition.cs +++ b/Pf2eModel/Model/Enum/SpellTradition.cs @@ -19,6 +19,9 @@ public enum SpellTradition [PgName("primal")] Primal, + [PgName("elemental")] + Elemental, + [PgName("all")] All, } diff --git a/Pf2eModel/Model/Structure/Spell.cs b/Pf2eModel/Model/Structure/Spell.cs index a5d6bca..598ed01 100644 --- a/Pf2eModel/Model/Structure/Spell.cs +++ b/Pf2eModel/Model/Structure/Spell.cs @@ -24,20 +24,31 @@ public class Spell : IEntity public ActionEnum Actions { get; set; } = ActionEnum.Other; [Column(Order = 4)] + [MaxLength(255)] + public int? Duration { get; set; } = null; + + [Column(Order = 5)] public bool IsFocus { get; set; } - // --- + [Column(Order = 6)] + public SpellTradition[] Traditions { get; set; } = []; + [Column(Order = 6)] + public int Range { get; set; } + + [Column(Order = 7)] + public LocaleString? Trigger { get; set; } = null; + + [Column(Order = 8)] + public LocaleString? Target { get; set; } = null; + + /* [Column(Order = 4)] public bool Counteraction { get; set; } [Column(Order = 5)] public bool Sustained { get; set; } - [Column(Order = 6)] - [MaxLength(255)] - public string Duration { get; set; } = null!; - [Column(Order = 7)] [MaxLength(255)] public string Range { get; set; } = null!; @@ -56,14 +67,11 @@ public class Spell : IEntity [Column(Order = 11)] public bool CanHeightening { get; set; } - - // --- + */ public virtual ICollection SpellAreas { get; set; } = []; public virtual SpellRitual? SpellRitual { get; set; } - public virtual ICollection SpellDamages { get; set; } = new List(); - public virtual ICollection SpellDefenses { get; set; } = new List(); } diff --git a/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs b/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs index 1480916..7f6185b 100644 --- a/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs +++ b/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.SpellRelation; @@ -7,7 +8,7 @@ namespace Pf2eModel.Model.Structure.SpellRelation; public class SpellArea { [Key] - [DatabaseGenerated(DatabaseGeneratedOption.None)] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] [Column(Order = 1)] public int Id { get; set; } @@ -16,13 +17,10 @@ public class SpellArea [Column(Order = 3)] [MaxLength(255)] - public string? Details { get; set; } + public LocaleString? Details { get; set; } [Column(Order = 4)] public SpellAreaType Type { get; set; } - [Column(Order = 5)] - public int Value { get; set; } - public virtual Spell Spell { get; set; } = null!; } diff --git a/Pf2eModel/Pf2eDbContext.cs b/Pf2eModel/Pf2eDbContext.cs index f886b90..820f45b 100644 --- a/Pf2eModel/Pf2eDbContext.cs +++ b/Pf2eModel/Pf2eDbContext.cs @@ -123,6 +123,7 @@ public partial class Pf2eDbContext : DbContext builder => { builder + .MapEnum() .MapEnum() .MapEnum() .MapEnum() @@ -140,6 +141,7 @@ public partial class Pf2eDbContext : DbContext .MapEnum() .MapEnum() .MapEnum() + .MapEnum() .MapEnum() .MapEnum() .MapEnum(); diff --git a/Pf2eModel/Pf2eDbContextModel.cs b/Pf2eModel/Pf2eDbContextModel.cs index da54736..2ff16f5 100644 --- a/Pf2eModel/Pf2eDbContextModel.cs +++ b/Pf2eModel/Pf2eDbContextModel.cs @@ -46,26 +46,22 @@ public partial class Pf2eDbContext private void BindLocaleString( EntityTypeBuilder entity, - Expression> func + Expression> func, + bool required = true ) where T : class { - /* - entity.OwnsMany(func, d => - { - d.ToJson(); - }); - */ - entity .Property(func) .HasConversion(v => v.Serialize(), v => LocaleString.Deserialize(v)) - .HasColumnType("jsonb"); + .HasColumnType("jsonb") + .IsRequired(required); } private void DefineEnums(ModelBuilder modelBuilder) { modelBuilder + .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() @@ -83,6 +79,7 @@ public partial class Pf2eDbContext .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() + .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum(); @@ -437,6 +434,9 @@ public partial class Pf2eDbContext { BindEntity(entity); + BindLocaleString(entity, s => s.Trigger, false); + BindLocaleString(entity, s => s.Target, false); + entity .HasMany(e => e.SpellAreas) .WithOne(e => e.Spell) @@ -450,20 +450,16 @@ public partial class Pf2eDbContext .OnDelete(DeleteBehavior.Cascade) .IsRequired(false); - entity - .HasMany(e => e.SpellDamages) - .WithOne(e => e.Spell) - .HasForeignKey(e => e.SpellId) - .OnDelete(DeleteBehavior.Cascade); - entity .HasMany(e => e.SpellDefenses) .WithOne(e => e.Spell) .HasForeignKey(e => e.SpellId) .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity(entity => { }); - modelBuilder.Entity(entity => { }); + modelBuilder.Entity(entity => + { + BindLocaleString(entity, a => a.Details, false); + }); modelBuilder.Entity(entity => { }); modelBuilder.Entity(entity => { }); diff --git a/Pf2eViewer/EntityView.cs b/Pf2eViewer/EntityView.cs index 3d4a412..20f619b 100644 --- a/Pf2eViewer/EntityView.cs +++ b/Pf2eViewer/EntityView.cs @@ -75,6 +75,10 @@ public class EntityView context => context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable() ), + new Getter( + EntityType.Spell, + context => context.Spells.Include(s => s.SpellAreas).Include(s => s.SpellDefenses) + ), ]; private abstract class Getter(EntityType type)