Additional card content

This commit is contained in:
gamer147
2026-05-24 17:07:05 -04:00
parent 12fb2f4801
commit 34bcc579a5
18 changed files with 53025 additions and 16 deletions

View File

@@ -13,6 +13,7 @@ using SVSim.EmulatedEntrypoint.Infrastructure;
using SVSim.EmulatedEntrypoint.Models.Dtos;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
using SVSim.EmulatedEntrypoint.Models.Dtos.Responses;
using SVSim.EmulatedEntrypoint.Services;
namespace SVSim.EmulatedEntrypoint.Controllers;
@@ -41,14 +42,17 @@ public class LoadController : SVSimController
private readonly ICardRepository _cardRepository;
private readonly ICollectionRepository _collectionRepository;
private readonly IGlobalsRepository _globalsRepository;
private readonly ICardAcquisitionService _acquisition;
public LoadController(IViewerRepository viewerRepository, ICardRepository cardRepository,
ICollectionRepository collectionRepository, IGlobalsRepository globalsRepository)
ICollectionRepository collectionRepository, IGlobalsRepository globalsRepository,
ICardAcquisitionService acquisition)
{
_viewerRepository = viewerRepository;
_cardRepository = cardRepository;
_collectionRepository = collectionRepository;
_globalsRepository = globalsRepository;
_acquisition = acquisition;
}
[HttpPost("index")]
@@ -66,6 +70,18 @@ public class LoadController : SVSimController
return NotFound();
}
// Backfill any card-associated cosmetics the viewer should already own. Idempotent.
// We MUST re-fetch the viewer after this call because GetViewerByShortUdid uses
// .AsNoTracking() — the local `viewer` instance is detached, and the service's writes
// (on a separate tracked instance) won't appear on this snapshot. Without the re-fetch,
// the response payload would be one /load/index behind on newly-granted cosmetics.
await _acquisition.GrantAsync(viewer.Id, newCardIds: null);
viewer = await _viewerRepository.GetViewerByShortUdid(shortUdid);
if (viewer is null)
{
return NotFound(); // defensive — should never happen
}
// user_card_list policy (see docs/api-spec/endpoints/post-login/load-index.md
// §user_card_list for the full discussion):
//

View File

@@ -27,19 +27,22 @@ public class PackController : SVSimController
private readonly ICardPoolProvider _pools;
private readonly IRandom _rng;
private readonly SVSimDbContext _db;
private readonly ICardAcquisitionService _acquisition;
public PackController(
IPackRepository packs,
PackOpenService opener,
ICardPoolProvider pools,
IRandom rng,
SVSimDbContext db)
SVSimDbContext db,
ICardAcquisitionService acquisition)
{
_packs = packs;
_opener = opener;
_pools = pools;
_rng = rng;
_db = db;
_acquisition = acquisition;
}
[HttpPost("info")]
@@ -193,16 +196,15 @@ public class PackController : SVSimController
// Draw + persist. DAILY single overrides packNumber to 1 (it's a one-card open).
int drawCount = child.IsDailySingle ? 1 : packNumber;
var draw = _opener.Draw(pack, _pools, drawCount, request.ExcludeCardIds, _rng);
await _packs.GrantCardsToViewer(viewerId, draw.Cards.Select(c => c.CardId));
var grant = await _acquisition.GrantAsync(viewerId, draw.Cards.Select(c => c.CardId));
// Build reward_list with post-state totals. The client's PlayerStaticData.UpdateHaveUserGoodsNum
// does direct assignment (`UserRupyCount = reward_num`, owned-count = reward_num), so we
// emit the new totals — not deltas. Without these the on-screen rupee/crystal/collection
// counts stay stale until the next /mypage/refresh or restart.
// Build reward_list. The service produces the type=5 (Card) entries with post-state counts
// plus any cosmetic grants. Currency entry (type=2 Crystals or type=9 Rupy) stays in the
// controller — it's a pack-purchase concern, not a card-grant concern. The client's
// PlayerStaticData.UpdateHaveUserGoodsNum does direct assignment, so currency/card counts
// must be the new TOTAL — emitting deltas would leave the on-screen balances stale.
var rewardList = new List<RewardListEntry>();
var postViewer = await _db.Viewers
.Include(v => v.Cards).ThenInclude(c => c.Card)
.FirstAsync(v => v.Id == viewerId);
var postViewer = await _db.Viewers.FirstAsync(v => v.Id == viewerId);
if (child.TypeDetail == 2)
{
@@ -212,12 +214,7 @@ public class PackController : SVSimController
{
rewardList.Add(new RewardListEntry { RewardType = 9, RewardId = 0, RewardNum = (int)postViewer.Currency.Rupees });
}
var drawnCardIds = draw.Cards.Select(c => c.CardId).Distinct().ToHashSet();
foreach (var owned in postViewer.Cards.Where(c => drawnCardIds.Contains(c.Card.Id)))
{
rewardList.Add(new RewardListEntry { RewardType = 5, RewardId = owned.Card.Id, RewardNum = owned.Count });
}
rewardList.AddRange(grant.RewardList);
return new PackOpenResponse
{