Commit Graph

3 Commits

Author SHA1 Message Date
gamer147
5c1db83967 feat(pack): /pack/set_rotation_starter_class + selected_class_id read-back
Implements Task 9 from docs/superpowers/plans/2026-06-13-decomp-only-followups.md
— the pre-purchase commit endpoint the client fires from StarterClassSelectDialog
before opening a RotationStarterCardPack. Without this the per-class draw path
from yesterday's work is unreachable: the dialog blocks on the AJAX call and
never advances to /pack/open.

Wire surface:
- POST /pack/set_rotation_starter_class { pack_id, class_id } → EmptyData.
  Validates 1..8, rejects unknown packs (404), non-RS packs (400), and second
  commits (400 — one-shot per pack per spec).
- PackConfigDto carries selected_class_id (nullable, omitted via global
  WhenWritingNull policy when unset). Placement verified against decomp at
  PackInfoTask.cs:86 — it's on the parent PackConfig, NOT the child gacha as
  the original plan text had it.
- /pack/open cross-checks request.class_id against the persisted choice for
  RS packs; rejects 400 on missing-commit or class-mismatch so a tampered
  request can't swap pools after the choice is locked.

Storage:
- ViewerPackStarterClass owned entity; (ViewerId, PackId) unique index via the
  pattern from project_owned_collection_unique_index memory.
- Migration PackStarterClass — composite PK + FK cascade verified against
  fresh Postgres bootstrap.

Tests (9 new in PackControllerSetRotationStarterClassTests):
- Round-trip set → /pack/info shows selected_class_id.
- Field absent before any commit.
- Invalid class (0/9/-1/100) → 400.
- Already-chosen rejection.
- Non-RS pack / unknown pack rejection.
- /pack/open rejects no-prior-commit AND class-mismatch; succeeds when class
  matches the persisted choice.

Full suite: 1552 passed (1543 + 9).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-27 20:06:46 -04:00
gamer147
27ebb5114c fix(pack): tutorial flow display + open end-to-end
Four targeted fixes that together let /tutorial/pack_info display
the legendary starter at index 0, let /tutorial/pack_open succeed
on it, and let the pack drop out of the shop after.

1. /pack/info now loads viewer.Items into a Dictionary<long,int>
   and threads it through ToDto so child_gacha_info.item_number
   reflects the viewer's actual owned count of item_id. Previously
   defaulted to 0 for every pack, so the legendary pack 99047
   reported item_number=0 immediately after the gift granted 1×
   ticket id=90001. Verified against the prod tutorial capture.

2. PackRepository.GetActivePacks now orders parent_gacha_id DESC
   to match prod's /pack/info wire order (99047, 92001, 80047,
   16015...10001). The tutorial pack UI runs with controls locked
   and auto-selects index 0 via GachaUI.GetCurrentLegendPackId
   (FirstOrDefault on IsLegendPackId), so the legendary starter
   needs to be the first legend pack in the list.

3. DbCardPoolProvider.GetPool falls back to all in-rotation cards
   when a LegendCardPack's base set has no rows. Pack 99047's
   base_pack_id is 90001, a synthetic "Throwback Rotation" category
   that doesn't correspond to a real card_set in the prod card
   master — its real pool is curated across older rotation sets
   (Altersphere through Colosseum). We don't have that membership
   map captured yet; the rotation fallback is broader than prod
   but produces a valid 8-card draw, which is what the tutorial
   needs to advance to step 100. TODO in code points at the
   real fix.

4. PackController.Open's tutorial path now consumes the granted
   ticket (decrement viewer.Items by packNumber for child.ItemId)
   and emits the post-state count in reward_list as
   {reward_type:4, reward_id:item_id, reward_num:post_count}.
   Without this, the pack stayed at item_number=1 forever, the
   shop kept showing it post-tutorial, and the next click hit
   /pack/open (not /tutorial/pack_open) which 501s on type_detail=5.

Also: docstring on PackConfigDto.SalesPeriodInfo flags the deferred
wire-fidelity fix (prod emits {"sales_period_time": "<complete_date>"}
for limited windows, [] for evergreens; we always emit {}) and the
retype from Dictionary<string,string?> to a typed
PackSalesPeriodInfoDto. Doesn't affect tutorial flow, deferred for
the pack-system rework.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 18:04:13 -04:00
gamer147
79209bd70b Pack opening 2026-05-24 02:03:13 -04:00