Additional card content
This commit is contained in:
@@ -117,6 +117,18 @@ public class BaseDataSeeder : IDataSeeder
|
||||
}
|
||||
}
|
||||
|
||||
private class CardCosmeticRewardMap : ClassMap<CardCosmeticReward>
|
||||
{
|
||||
public CardCosmeticRewardMap()
|
||||
{
|
||||
Map(m => m.CardId).Name("card_id");
|
||||
Map(m => m.Type).Name("type");
|
||||
Map(m => m.CosmeticId).Name("cosmetic_id");
|
||||
Map(m => m.Quantity).Name("quantity").Default(1);
|
||||
Map(m => m.Card).Ignore();
|
||||
}
|
||||
}
|
||||
|
||||
public void Seed(ModelBuilder builder)
|
||||
{
|
||||
// Migrations bake the HasData rows into InsertData calls — once the migration is
|
||||
@@ -146,6 +158,7 @@ public class BaseDataSeeder : IDataSeeder
|
||||
List<MyPageBackgroundEntry> myPageBackgrounds = ReadCsv<MyPageBackgroundEntry, MyPageBackgroundEntryMap>("mypagebackgrounds.csv");
|
||||
List<RankInfoEntry> rankinfos = ReadCsv<RankInfoEntry, RankInfoEntryMap>("ranks.csv");
|
||||
List<ClassExpEntry> classexp = ReadCsv<ClassExpEntry, ClassExpEntryMap>("classexp.csv");
|
||||
List<CardCosmeticReward> cardCosmeticRewards = ReadCsv<CardCosmeticReward, CardCosmeticRewardMap>("card_cosmetic_rewards.csv");
|
||||
|
||||
builder.Entity<ClassEntry>().HasData(classes);
|
||||
builder.Entity<LeaderSkinEntry>().HasData(leaderSkins);
|
||||
@@ -156,5 +169,6 @@ public class BaseDataSeeder : IDataSeeder
|
||||
builder.Entity<MyPageBackgroundEntry>().HasData(myPageBackgrounds);
|
||||
builder.Entity<RankInfoEntry>().HasData(rankinfos);
|
||||
builder.Entity<ClassExpEntry>().HasData(classexp);
|
||||
builder.Entity<CardCosmeticReward>().HasData(cardCosmeticRewards);
|
||||
}
|
||||
}
|
||||
|
||||
15
SVSim.Database/Enums/CosmeticType.cs
Normal file
15
SVSim.Database/Enums/CosmeticType.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace SVSim.Database.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Subset of UserGoods.Type values that can be granted as a card-acquisition cosmetic.
|
||||
/// Numeric values MUST match Wizard/UserGoods.cs:8-22 so wire serialization
|
||||
/// (reward_type in /pack/open response) is direct passthrough.
|
||||
/// </summary>
|
||||
public enum CosmeticType
|
||||
{
|
||||
Sleeve = 6,
|
||||
Emblem = 7,
|
||||
Degree = 8,
|
||||
Skin = 10,
|
||||
MyPageBG = 15,
|
||||
}
|
||||
42477
SVSim.Database/Migrations/20260524193503_AddCardCosmeticRewards.Designer.cs
generated
Normal file
42477
SVSim.Database/Migrations/20260524193503_AddCardCosmeticRewards.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
1123
SVSim.Database/Migrations/20260524193503_AddCardCosmeticRewards.cs
Normal file
1123
SVSim.Database/Migrations/20260524193503_AddCardCosmeticRewards.cs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
24
SVSim.Database/Models/CardCosmeticReward.cs
Normal file
24
SVSim.Database/Models/CardCosmeticReward.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using SVSim.Database.Enums;
|
||||
|
||||
namespace SVSim.Database.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Association: when a viewer acquires <see cref="CardId"/>, they should also receive
|
||||
/// the cosmetic identified by (<see cref="Type"/>, <see cref="CosmeticId"/>) if they don't
|
||||
/// already own it.
|
||||
///
|
||||
/// Always recorded on the NON-FOIL row of a card. Foil twins (card_id + 1) inherit at
|
||||
/// lookup time — see CardAcquisitionService for the foil-resolution rule.
|
||||
///
|
||||
/// Composite PK on (CardId, Type, CosmeticId): naturally enforces "no duplicates" AND
|
||||
/// satisfies EF's deterministic-PK requirement for HasData seeding.
|
||||
/// </summary>
|
||||
public class CardCosmeticReward
|
||||
{
|
||||
public long CardId { get; set; }
|
||||
public CosmeticType Type { get; set; }
|
||||
public long CosmeticId { get; set; }
|
||||
public int Quantity { get; set; } = 1;
|
||||
|
||||
public ShadowverseCardEntry Card { get; set; } = null!;
|
||||
}
|
||||
@@ -25,6 +25,7 @@ public class SVSimDbContext : DbContext
|
||||
public DbSet<ShadowverseCardEntry> Cards => Set<ShadowverseCardEntry>();
|
||||
public DbSet<ShadowverseCardSetEntry> CardSets => Set<ShadowverseCardSetEntry>();
|
||||
public DbSet<ShadowverseDeckEntry> Decks => Set<ShadowverseDeckEntry>();
|
||||
public DbSet<CardCosmeticReward> CardCosmeticRewards => Set<CardCosmeticReward>();
|
||||
|
||||
public DbSet<ClassEntry> Classes => Set<ClassEntry>();
|
||||
public DbSet<ClassExpEntry> ClassExpCurve => Set<ClassExpEntry>();
|
||||
@@ -115,6 +116,18 @@ public class SVSimDbContext : DbContext
|
||||
modelBuilder.Entity<PackConfigEntry>().OwnsMany(p => p.Banners);
|
||||
modelBuilder.Entity<Viewer>().OwnsMany(v => v.PackOpenCounts);
|
||||
|
||||
modelBuilder.Entity<CardCosmeticReward>(b =>
|
||||
{
|
||||
b.HasKey(r => new { r.CardId, r.Type, r.CosmeticId });
|
||||
b.HasIndex(r => r.CardId);
|
||||
// No inverse nav on the Card side — avoid forcing CosmeticRewards to load on every
|
||||
// Card query. See project_ef_split_query memory for the cartesian-explode risk.
|
||||
b.HasOne(r => r.Card)
|
||||
.WithMany()
|
||||
.HasForeignKey(r => r.CardId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
// GameConfiguration.Config: on Postgres use EF Core 8's OwnsOne+ToJson(jsonb column).
|
||||
// On SQLite (tests) ToJson's WriteJson has a known NullReferenceException when owned
|
||||
// collections are present — use a plain TEXT value converter instead so the same
|
||||
|
||||
Reference in New Issue
Block a user