fix(pack): include all pack legendaries in gacha-point catalog + correct class_id

This commit is contained in:
gamer147
2026-05-29 08:36:37 -04:00
parent a8bbc39bfd
commit 7292c44082
2 changed files with 128 additions and 34 deletions

View File

@@ -41,6 +41,13 @@ public sealed class GachaPointService : IGachaPointService
.Select(c => c.Id)
.ToHashSet();
// Re-query legendaries with Class loaded — pool provider doesn't include navs,
// so card.Class is null on every pool entry and class_id would collapse to "0".
var legendariesWithClass = await _db.Cards
.Where(c => legendaryCardIds.Contains(c.Id))
.Include(c => c.Class)
.ToListAsync();
// Pull both cosmetic types in one trip. Group by card_id for O(1) lookup below.
var cosmeticsByCard = await _db.CardCosmeticRewards
.Where(r => legendaryCardIds.Contains(r.CardId)
@@ -53,56 +60,56 @@ public sealed class GachaPointService : IGachaPointService
var standard = new List<GachaPointRewardDto>();
var leader = new List<GachaPointRewardDto>();
foreach (var card in pool
.Where(c => c.Rarity == Rarity.Legendary && !c.IsFoil)
foreach (var card in legendariesWithClass
// Neutral cards have Class=null; client wire-encodes them as class_id="0".
.OrderBy(c => c.Class?.Id ?? 0).ThenBy(c => c.Id))
{
if (!cosmeticLookup.TryGetValue(card.Id, out var cosmetics)) continue;
var emblems = cosmetics.Where(c => c.Type == CosmeticType.Emblem).ToList();
var skin = cosmetics.FirstOrDefault(c => c.Type == CosmeticType.Skin);
if (emblems.Count == 0) continue; // every gacha-point entry has at least one emblem
cosmeticLookup.TryGetValue(card.Id, out var cosmetics);
var emblems = cosmetics?.Where(c => c.Type == CosmeticType.Emblem).ToList()
?? new List<CardCosmeticReward>();
var skin = cosmetics?.FirstOrDefault(c => c.Type == CosmeticType.Skin);
var classId = (card.Class?.Id ?? 0).ToString(CultureInfo.InvariantCulture);
var isReceived = receivedCardIds.Contains(card.Id);
if (IsLeaderCard(skin))
{
// Leader card — 3 entries in capture order: Sleeve/Card-cosmetic (type 6),
// Skin (type 10), Emblem (type 7). The reward_type=6 entry's detail id is the
// card_id itself, mirroring the prod capture exactly (no Sleeve cosmetic row
// is required — synthesizing from card.Id is robust to missing rows). For the
// emblem we take the first row only; no capture has shown leader cards with
// multiple emblems, and the verified shape is exactly 1 Sleeve + 1 Skin + 1
// Emblem. Revisit if such a capture lands.
var emblem = emblems[0];
// Leader card — 2 or 3 entries: Sleeve/Card-cosmetic (type 6) with detail=card_id,
// Skin (type 10) with detail=leader_skin_id, and an Emblem (type 7) per emblem row.
// Most leader cards in captured packs have exactly 1 emblem, but we emit per-emblem
// for consistency with the standard-legendary branch.
var rewardList = new List<GachaPointRewardDetailEntry>
{
new GachaPointRewardDetailEntry
{
RewardType = (int)UserGoodsType.Sleeve, RewardDetailId = card.Id, RewardNumber = 1,
},
new GachaPointRewardDetailEntry
{
RewardType = (int)UserGoodsType.Skin,
RewardDetailId = skin!.CosmeticId, RewardNumber = 1,
},
};
foreach (var emblem in emblems)
{
rewardList.Add(new GachaPointRewardDetailEntry
{
RewardType = (int)UserGoodsType.Emblem,
RewardDetailId = emblem.CosmeticId, RewardNumber = 1,
});
}
leader.Add(new GachaPointRewardDto
{
ClassId = classId, CardId = card.Id, IsReceived = isReceived,
RewardList =
{
new GachaPointRewardDetailEntry
{
RewardType = (int)UserGoodsType.Sleeve, RewardDetailId = card.Id, RewardNumber = 1,
},
new GachaPointRewardDetailEntry
{
RewardType = (int)UserGoodsType.Skin,
RewardDetailId = skin!.CosmeticId, RewardNumber = 1,
},
new GachaPointRewardDetailEntry
{
RewardType = (int)UserGoodsType.Emblem,
RewardDetailId = emblem.CosmeticId, RewardNumber = 1,
},
},
RewardList = rewardList,
});
}
else
{
// Standard legendaries can have multiple emblem cosmetics (e.g. prod capture
// pack 10008 card 108044010 has emblem ids 900041040 and 900041050). Emit one
// reward_list entry per emblem.
// Standard legendary — one reward_list entry per emblem cosmetic (possibly zero
// entries for packs whose emblem mappings weren't in the capture sweep, e.g. pack
// 10001 Classic). The card is still grantable; the exchange's cosmetic cascade
// delivers whatever rows actually exist in CardCosmeticRewards.
var dto = new GachaPointRewardDto
{
ClassId = classId, CardId = card.Id, IsReceived = isReceived,