diff --git a/SVSim.Bootstrap/Data/seeds/items.json b/SVSim.Bootstrap/Data/seeds/items.json
new file mode 100644
index 0000000..7e20450
--- /dev/null
+++ b/SVSim.Bootstrap/Data/seeds/items.json
@@ -0,0 +1,362 @@
+[
+ {
+ "item_id": 1,
+ "name": "Challenge Ticket",
+ "type": 1,
+ "thumbnail_path": "ticket_1"
+ },
+ {
+ "item_id": 2,
+ "name": "Grand Prix Ticket",
+ "type": 4,
+ "thumbnail_path": "ticket_colosseum"
+ },
+ {
+ "item_id": 1000,
+ "name": "Seer's Globe",
+ "type": 3,
+ "thumbnail_path": "thumbnail_orb"
+ },
+ {
+ "item_id": 1001,
+ "name": "Seer's Globe Shards",
+ "type": 5,
+ "thumbnail_path": "thumbnail_orb_piece"
+ },
+ {
+ "item_id": 2001,
+ "name": "Umamusume Bingo Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_2001"
+ },
+ {
+ "item_id": 2002,
+ "name": "Chiikawa Bingo Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_2002"
+ },
+ {
+ "item_id": 2003,
+ "name": "7th Anniversary Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_2003"
+ },
+ {
+ "item_id": 2004,
+ "name": "Fennie Bingo Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_2004"
+ },
+ {
+ "item_id": 10001,
+ "name": "Classic Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10001"
+ },
+ {
+ "item_id": 10002,
+ "name": "Darkness Evolved Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10002"
+ },
+ {
+ "item_id": 10003,
+ "name": "Rise of Bahamut Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10003"
+ },
+ {
+ "item_id": 10004,
+ "name": "Tempest of the Gods Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10004"
+ },
+ {
+ "item_id": 10005,
+ "name": "Wonderland Dreams Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10005"
+ },
+ {
+ "item_id": 10006,
+ "name": "Starforged Legends Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10006"
+ },
+ {
+ "item_id": 10007,
+ "name": "Chronogenesis Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10007"
+ },
+ {
+ "item_id": 10008,
+ "name": "Dawnbreak, Nightedge Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10008"
+ },
+ {
+ "item_id": 10009,
+ "name": "Brigade of the Sky Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10009"
+ },
+ {
+ "item_id": 10010,
+ "name": "Omen of the Ten Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10010"
+ },
+ {
+ "item_id": 10011,
+ "name": "Altersphere Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10011"
+ },
+ {
+ "item_id": 10012,
+ "name": "Steel Rebellion Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10012"
+ },
+ {
+ "item_id": 10013,
+ "name": "Rebirth of Glory Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10013"
+ },
+ {
+ "item_id": 10014,
+ "name": "Verdant Conflict Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10014"
+ },
+ {
+ "item_id": 10015,
+ "name": "Ultimate Colosseum Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10015"
+ },
+ {
+ "item_id": 10016,
+ "name": "World Uprooted Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10016"
+ },
+ {
+ "item_id": 10017,
+ "name": "Fortune's Hand Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10017"
+ },
+ {
+ "item_id": 10018,
+ "name": "Storm Over Rivayle Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10018"
+ },
+ {
+ "item_id": 10019,
+ "name": "Eternal Awakening Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10019"
+ },
+ {
+ "item_id": 10020,
+ "name": "Darkness Over Vellsar Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10020"
+ },
+ {
+ "item_id": 10021,
+ "name": "Renascent Chronicles Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10021"
+ },
+ {
+ "item_id": 10022,
+ "name": "Dawn of Calamity Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10022"
+ },
+ {
+ "item_id": 10023,
+ "name": "Omen of Storms Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10023"
+ },
+ {
+ "item_id": 10024,
+ "name": "Edge of Paradise Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10024"
+ },
+ {
+ "item_id": 10025,
+ "name": "Roar of the Godwyrm Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10025"
+ },
+ {
+ "item_id": 10026,
+ "name": "Celestial Dragonblade Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10026"
+ },
+ {
+ "item_id": 10027,
+ "name": "Eightfold Abyss: Azvaldt Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10027"
+ },
+ {
+ "item_id": 10028,
+ "name": "Academy of Ages Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10028"
+ },
+ {
+ "item_id": 10029,
+ "name": "Heroes of Rivenbrandt Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10029"
+ },
+ {
+ "item_id": 10030,
+ "name": "Order Shift Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10030"
+ },
+ {
+ "item_id": 10031,
+ "name": "Resurgent Legends Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10031"
+ },
+ {
+ "item_id": 10032,
+ "name": "Heroes of Shadowverse Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_10032"
+ },
+ {
+ "item_id": 60001,
+ "name": "4th Birthday Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60001"
+ },
+ {
+ "item_id": 60019,
+ "name": "Eternal Awakening Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60019"
+ },
+ {
+ "item_id": 60020,
+ "name": "Darkness Over Vellsar Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60020"
+ },
+ {
+ "item_id": 60021,
+ "name": "Renascent Chronicles Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60021"
+ },
+ {
+ "item_id": 60022,
+ "name": "Dawn of Calamity Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60022"
+ },
+ {
+ "item_id": 60023,
+ "name": "Omen of Storms Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60023"
+ },
+ {
+ "item_id": 60024,
+ "name": "Edge of Paradise Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60024"
+ },
+ {
+ "item_id": 60025,
+ "name": "Roar of the Godwyrm Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60025"
+ },
+ {
+ "item_id": 60026,
+ "name": "Celestial Dragonblade Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60026"
+ },
+ {
+ "item_id": 60027,
+ "name": "Eightfold Abyss: Azvaldt Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60027"
+ },
+ {
+ "item_id": 60028,
+ "name": "Academy of Ages Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60028"
+ },
+ {
+ "item_id": 60029,
+ "name": "Heroes of Rivenbrandt Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60029"
+ },
+ {
+ "item_id": 60030,
+ "name": "Order Shift Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60030"
+ },
+ {
+ "item_id": 60031,
+ "name": "Resurgent Legends Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60031"
+ },
+ {
+ "item_id": 60032,
+ "name": "Heroes of Shadowverse Temporary Deck Ticket",
+ "type": 6,
+ "thumbnail_path": "ticket_60032"
+ },
+ {
+ "item_id": 70001,
+ "name": "4th Birthday Leader Ticket",
+ "type": 7,
+ "thumbnail_path": "ticket_70001"
+ },
+ {
+ "item_id": 70002,
+ "name": "Champion's Battle Leader Ticket",
+ "type": 7,
+ "thumbnail_path": "ticket_70002"
+ },
+ {
+ "item_id": 80001,
+ "name": "Throwback Rotation Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_80001"
+ },
+ {
+ "item_id": 90001,
+ "name": "Legendary Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_90001"
+ },
+ {
+ "item_id": 92001,
+ "name": "4th Birthday Card Pack Ticket",
+ "type": 2,
+ "thumbnail_path": "ticket_92001"
+ }
+]
diff --git a/SVSim.Bootstrap/Importers/ItemImporter.cs b/SVSim.Bootstrap/Importers/ItemImporter.cs
new file mode 100644
index 0000000..93da5fb
--- /dev/null
+++ b/SVSim.Bootstrap/Importers/ItemImporter.cs
@@ -0,0 +1,52 @@
+using Microsoft.EntityFrameworkCore;
+using SVSim.Bootstrap.Models.Seed;
+using SVSim.Database;
+using SVSim.Database.Models;
+
+namespace SVSim.Bootstrap.Importers;
+
+///
+/// Idempotent upsert of the item catalog from seeds/items.json. Source is the client's
+/// item_master.csv + itemtext.json (extracted via
+/// data_dumps/extract/extract-items.py). Rows missing from the seed are LEFT INTACT.
+///
+public class ItemImporter
+{
+ public async Task ImportAsync(SVSimDbContext context, string seedDir)
+ {
+ string path = Path.Combine(seedDir, "items.json");
+ var seed = SeedLoader.LoadList(path);
+ if (seed.Count == 0)
+ {
+ Console.WriteLine("[ItemImporter] No seed rows; skipping.");
+ return 0;
+ }
+
+ var existing = await context.Items.ToDictionaryAsync(e => e.Id);
+ int created = 0, updated = 0;
+
+ foreach (var s in seed)
+ {
+ if (s.ItemId == 0) continue;
+
+ var entry = existing.TryGetValue(s.ItemId, out var ex)
+ ? ex : new ItemEntry { Id = s.ItemId };
+
+ entry.Name = s.Name;
+ entry.Type = s.Type;
+ entry.ThumbnailPath = s.ThumbnailPath;
+
+ if (ex is null)
+ {
+ context.Items.Add(entry);
+ existing[s.ItemId] = entry;
+ created++;
+ }
+ else updated++;
+ }
+
+ await context.SaveChangesAsync();
+ Console.WriteLine($"[ItemImporter] +{created}/~{updated}");
+ return created + updated;
+ }
+}
diff --git a/SVSim.Bootstrap/Models/Seed/ItemSeed.cs b/SVSim.Bootstrap/Models/Seed/ItemSeed.cs
new file mode 100644
index 0000000..fdadf70
--- /dev/null
+++ b/SVSim.Bootstrap/Models/Seed/ItemSeed.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+
+namespace SVSim.Bootstrap.Models.Seed;
+
+public sealed class ItemSeed
+{
+ [JsonPropertyName("item_id")] public int ItemId { get; set; }
+ [JsonPropertyName("name")] public string Name { get; set; } = "";
+ [JsonPropertyName("type")] public int Type { get; set; }
+ [JsonPropertyName("thumbnail_path")] public string ThumbnailPath { get; set; } = "";
+}
diff --git a/SVSim.Bootstrap/Program.cs b/SVSim.Bootstrap/Program.cs
index 74fda71..d556abd 100644
--- a/SVSim.Bootstrap/Program.cs
+++ b/SVSim.Bootstrap/Program.cs
@@ -97,6 +97,7 @@ public static class Program
await new PracticeOpponentImporter().ImportAsync(context, opts.SeedDir);
await new PaymentItemImporter().ImportAsync(context, opts.SeedDir);
+ await new ItemImporter().ImportAsync(context, opts.SeedDir);
var puzzleImporter = new PuzzleImporter();
await puzzleImporter.ImportGroupsAsync(context, opts.SeedDir);
await puzzleImporter.ImportPuzzlesAsync(context, opts.SeedDir);
diff --git a/SVSim.Database/Migrations/20260528013825_AddItemTypeAndThumbnail.Designer.cs b/SVSim.Database/Migrations/20260528013825_AddItemTypeAndThumbnail.Designer.cs
new file mode 100644
index 0000000..db7f221
--- /dev/null
+++ b/SVSim.Database/Migrations/20260528013825_AddItemTypeAndThumbnail.Designer.cs
@@ -0,0 +1,3278 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using SVSim.Database;
+
+#nullable disable
+
+namespace SVSim.Database.Migrations
+{
+ [DbContext(typeof(SVSimDbContext))]
+ [Migration("20260528013825_AddItemTypeAndThumbnail")]
+ partial class AddItemTypeAndThumbnail
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.8")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.HasSequence("ShortUdidSequence")
+ .StartsAt(400000000L);
+
+ modelBuilder.Entity("DegreeEntryViewer", b =>
+ {
+ b.Property("DegreesId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("DegreesId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("DegreeEntryViewer");
+ });
+
+ modelBuilder.Entity("EmblemEntryViewer", b =>
+ {
+ b.Property("EmblemsId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("EmblemsId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("EmblemEntryViewer");
+ });
+
+ modelBuilder.Entity("LeaderSkinEntryViewer", b =>
+ {
+ b.Property("LeaderSkinsId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("LeaderSkinsId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("LeaderSkinEntryViewer");
+ });
+
+ modelBuilder.Entity("MyPageBackgroundEntryViewer", b =>
+ {
+ b.Property("MyPageBackgroundsId")
+ .HasColumnType("integer");
+
+ b.Property("ViewersId")
+ .HasColumnType("bigint");
+
+ b.HasKey("MyPageBackgroundsId", "ViewersId");
+
+ b.HasIndex("ViewersId");
+
+ b.ToTable("MyPageBackgroundEntryViewer");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Entities.Story.SpecialBattleSetting", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("BanishEffectOverride")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ClassDestroyEffectOverride")
+ .HasColumnType("integer");
+
+ b.Property("EnemyAttachSkill")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("EnemyStartLife")
+ .HasColumnType("integer");
+
+ b.Property("EnemyStartPp")
+ .HasColumnType("integer");
+
+ b.Property("IdOverrideInBattleLog")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Note")
+ .HasColumnType("text");
+
+ b.Property("PlayerAttachSkill")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PlayerFirstTurn")
+ .HasColumnType("integer");
+
+ b.Property("PlayerStartLife")
+ .HasColumnType("integer");
+
+ b.Property("PlayerStartPp")
+ .HasColumnType("integer");
+
+ b.Property("ResultSkip")
+ .HasColumnType("integer");
+
+ b.Property("SpecialTokenDrawEffectOverride")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TokenDrawEffectOverride")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("VsEffectOverride")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("SpecialBattleSettings");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Entities.Story.StoryChapter", b =>
+ {
+ b.Property("StoryId")
+ .HasColumnType("integer");
+
+ b.Property("Battle3dFieldId")
+ .HasColumnType("integer");
+
+ b.Property("BattleExists")
+ .HasColumnType("boolean");
+
+ b.Property("BgFileName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("BgmId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ChapterClearTextId")
+ .HasColumnType("text");
+
+ b.Property("ChapterEffectPath")
+ .HasColumnType("text");
+
+ b.Property("ChapterId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CharaId")
+ .HasColumnType("integer");
+
+ b.Property("EnemyAiId")
+ .HasColumnType("integer");
+
+ b.Property("EnemyCharaId")
+ .HasColumnType("integer");
+
+ b.Property("EnemyClass")
+ .HasColumnType("integer");
+
+ b.Property("IsCameraMovable")
+ .HasColumnType("integer");
+
+ b.Property("IsMaintenanceChapter")
+ .HasColumnType("boolean");
+
+ b.Property("IsPlayAnotherEndAppearanceAnimation")
+ .HasColumnType("boolean");
+
+ b.Property("IsReleasedAnotherEnd")
+ .HasColumnType("boolean");
+
+ b.Property("IsSkipEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("NextChapterId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ReleasePoint")
+ .HasColumnType("integer");
+
+ b.Property("RequiredChapterId")
+ .HasColumnType("text");
+
+ b.Property("SectionId")
+ .HasColumnType("integer");
+
+ b.Property("SelectionDisplayPosition")
+ .HasColumnType("text");
+
+ b.Property("SelectionTextId")
+ .HasColumnType("text");
+
+ b.Property("ShowCoordinate")
+ .HasColumnType("integer");
+
+ b.Property("ShowSubtitles")
+ .HasColumnType("integer");
+
+ b.Property("SpecialBattleSettingId")
+ .HasColumnType("integer");
+
+ b.Property("UnlockText")
+ .HasColumnType("text");
+
+ b.Property("XCoordinate")
+ .HasColumnType("numeric");
+
+ b.Property("YCoordinate")
+ .HasColumnType("numeric");
+
+ b.HasKey("StoryId");
+
+ b.HasIndex("NextChapterId");
+
+ b.HasIndex("SpecialBattleSettingId");
+
+ b.HasIndex("SectionId", "CharaId", "ChapterId");
+
+ b.ToTable("StoryChapters");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Entities.Story.StorySection", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AllStoryOrderId")
+ .HasColumnType("integer");
+
+ b.Property("BackGroundId")
+ .HasColumnType("integer");
+
+ b.Property("ChapterSelectType")
+ .HasColumnType("integer");
+
+ b.Property("ImageName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsLeaderSelect")
+ .HasColumnType("boolean");
+
+ b.Property("IsPlayAnotherEndAppearanceAnimation")
+ .HasColumnType("boolean");
+
+ b.Property("IsSpoiler")
+ .HasColumnType("integer");
+
+ b.Property("IsUnderMaintenance")
+ .HasColumnType("boolean");
+
+ b.Property("NameTextKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrderId")
+ .HasColumnType("integer");
+
+ b.Property("SpoilerMessage")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("StoryApiType")
+ .HasColumnType("integer");
+
+ b.Property("StoryTypeOverwrite")
+ .HasColumnType("integer");
+
+ b.Property("WorldId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("WorldId");
+
+ b.ToTable("StorySections");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Entities.Story.StoryWorld", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("PanelImageName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("RibbonText")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TitleTextKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("StoryWorlds");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Entities.Story.ViewerStoryBranchUnlock", b =>
+ {
+ b.Property("ViewerId")
+ .HasColumnType("bigint");
+
+ b.Property("StoryId")
+ .HasColumnType("integer");
+
+ b.Property("UnlockedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("ViewerId", "StoryId");
+
+ b.ToTable("ViewerStoryBranchUnlocks");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Entities.Story.ViewerStoryProgress", b =>
+ {
+ b.Property("ViewerId")
+ .HasColumnType("bigint");
+
+ b.Property("StoryId")
+ .HasColumnType("integer");
+
+ b.Property("FinishedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsFinish")
+ .HasColumnType("boolean");
+
+ b.Property("IsSkipped")
+ .HasColumnType("boolean");
+
+ b.Property("SkippedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("ViewerId", "StoryId");
+
+ b.ToTable("ViewerStoryProgress");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.AchievementCatalogEntry", b =>
+ {
+ b.Property("AchievementType")
+ .HasColumnType("integer");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("EventArg")
+ .HasColumnType("integer");
+
+ b.Property("EventType")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrderNum")
+ .HasColumnType("integer");
+
+ b.Property("RequireNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardDetailId")
+ .HasColumnType("bigint");
+
+ b.Property("RewardNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardType")
+ .HasColumnType("integer");
+
+ b.HasKey("AchievementType", "Level");
+
+ b.HasIndex("AchievementType");
+
+ b.HasIndex("EventType", "EventArg");
+
+ b.ToTable("AchievementCatalog");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ArenaSeasonConfig", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Cost")
+ .HasColumnType("numeric(20,0)");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Enable")
+ .HasColumnType("integer");
+
+ b.Property("FormatInfo")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("IsJoin")
+ .HasColumnType("boolean");
+
+ b.Property("Mode")
+ .HasColumnType("integer");
+
+ b.Property("RupyCost")
+ .HasColumnType("numeric(20,0)");
+
+ b.Property("TicketCost")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("ArenaSeasons");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.AvatarAbilityEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Ability")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("AbilityCost")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("AbilityDesc")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("BattleStartFirstPlayerTurnBp")
+ .HasColumnType("integer");
+
+ b.Property("BattleStartMaxLife")
+ .HasColumnType("integer");
+
+ b.Property("BattleStartSecondPlayerTurnBp")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LeaderSkinId")
+ .HasColumnType("integer");
+
+ b.Property("PassiveAbility")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PassiveAbilityDesc")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("AvatarAbilities");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BannerEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("ChangeTime")
+ .HasColumnType("integer");
+
+ b.Property("Click")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ImageName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ImagePaths")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("RemainingTime")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Banners");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlePassLevelEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("RequiredPoint")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("BattlePassLevels");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlePassMonthlyMissionEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BattlePassPoint")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EventArg")
+ .HasColumnType("integer");
+
+ b.Property("EventType")
+ .HasColumnType("text");
+
+ b.Property("Month")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrderNum")
+ .HasColumnType("integer");
+
+ b.Property("RequireNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardDetailId")
+ .HasColumnType("bigint");
+
+ b.Property("RewardNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardType")
+ .HasColumnType("integer");
+
+ b.Property("Year")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Year", "Month");
+
+ b.HasIndex("Year", "Month", "OrderNum")
+ .IsUnique();
+
+ b.ToTable("BattlePassMonthlyMissions");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlePassRewardEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("bigint");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsAppealExclusion")
+ .HasColumnType("boolean");
+
+ b.Property("Level")
+ .HasColumnType("integer");
+
+ b.Property("RewardDetailId")
+ .HasColumnType("bigint");
+
+ b.Property("RewardNumber")
+ .HasColumnType("integer");
+
+ b.Property("RewardType")
+ .HasColumnType("integer");
+
+ b.Property("SeasonId")
+ .HasColumnType("integer");
+
+ b.Property("Track")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SeasonId", "Track", "Level")
+ .IsUnique();
+
+ b.ToTable("BattlePassRewards");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlePassSeasonEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("CanPurchase")
+ .HasColumnType("boolean");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("EndDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("MaxLevel")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PriceCrystal")
+ .HasColumnType("integer");
+
+ b.Property("StartDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("StartDate", "EndDate");
+
+ b.ToTable("BattlePassSeasons");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BattlefieldEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsOpen")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Battlefields");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BuildDeckProductEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeckCode")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FeaturedCardId")
+ .HasColumnType("bigint");
+
+ b.Property("IntroPriceCrystal")
+ .HasColumnType("integer");
+
+ b.Property("IntroPriceRupy")
+ .HasColumnType("integer");
+
+ b.Property("IsEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("LeaderId")
+ .HasColumnType("integer");
+
+ b.Property("ProductNameKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PurchaseNumMax")
+ .HasColumnType("integer");
+
+ b.Property("RegularPriceCrystal")
+ .HasColumnType("integer");
+
+ b.Property("RegularPriceRupy")
+ .HasColumnType("integer");
+
+ b.Property("SeriesId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SeriesId");
+
+ b.ToTable("BuildDeckProducts");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.BuildDeckSeriesEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DrumrollPath")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IntroKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsNew")
+ .HasColumnType("boolean");
+
+ b.Property("NameKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrderIndex")
+ .HasColumnType("integer");
+
+ b.Property("TitlePath")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("BuildDeckSeries");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.CardCosmeticReward", b =>
+ {
+ b.Property("CardId")
+ .HasColumnType("bigint");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.Property("CosmeticId")
+ .HasColumnType("bigint");
+
+ b.Property("Quantity")
+ .HasColumnType("integer");
+
+ b.HasKey("CardId", "Type", "CosmeticId");
+
+ b.HasIndex("CardId");
+
+ b.ToTable("CardCosmeticRewards");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ClassEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Classes");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ClassExpEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("NecessaryExp")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.ToTable("ClassExpCurve");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ColosseumConfig", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("CardPoolName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ColosseumId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ColosseumName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeckFormat")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("EndTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsAllCardEnabled")
+ .HasColumnType("integer");
+
+ b.Property("IsColosseumPeriod")
+ .HasColumnType("boolean");
+
+ b.Property("IsDisplayTips")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsNormalTwoPick")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("IsRoundPeriod")
+ .HasColumnType("boolean");
+
+ b.Property("IsSpecialMode")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("NowRound")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SalesPeriodInfo")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("StartTime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TipsId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Colosseums");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DailyLoginBonusEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("BonusData")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("BonusId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("DailyLoginBonuses");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DefaultDeckEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("CardIdArray")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("ClassId")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeckName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DeckNo")
+ .HasColumnType("integer");
+
+ b.Property("LeaderSkinId")
+ .HasColumnType("integer");
+
+ b.Property("SleeveId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.ToTable("DefaultDecks");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.DegreeEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("Degrees");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.EmblemEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("Emblems");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.FeatureMaintenanceEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Data")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FeatureKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("FeatureMaintenances");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.GameConfigSection", b =>
+ {
+ b.Property("SectionName")
+ .HasColumnType("text");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DateUpdated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ValueJson")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.HasKey("SectionName");
+
+ b.ToTable("GameConfigs");
+ });
+
+ modelBuilder.Entity("SVSim.Database.Models.ItemEntry", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("DateCreated")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property