Commit Graph

10 Commits

Author SHA1 Message Date
gamer147
81fe842c15 fix(pack): daily half-off single spends crystals and gates re-purchase
The DAILY child gacha (type_detail=3, is_daily_single=true) is the once-per-day
half-off crystal single-pack. Two bugs:

1. /pack/open Daily case was calling TrySpendAsync(Rupee) — decompiled client
   (GachaUI.cs:1046 CheckBuyPackWithCrystal, :1187 SetClystalConfirmDialog)
   confirms it's a crystal purchase. Freeplay mode masked the mismatch in
   testing. Swapped to Crystal + insufficient_crystals error.

2. /pack/info kept emitting is_daily_single=true after the viewer's first
   claim, so the client re-showed the half-off button and a second click
   400'd with daily_free_already_claimed. Added a per-parent-pack gate that
   flips is_daily_single false when LastDailyFreeAt.Date == today. The child
   entry itself stays so the CRYSTAL_MULTI full-price button still activates.

TODO(daily-reset) comments now reference docs/plans/daily-reset-service.md
(in the outer repo) — the current UTC-midnight comparison will migrate to
the JST 02:00 reset boundary via that plan.

Updated the existing daily test to use cost=50 and assert
Crystals: 200 -> 150 + Rupees untouched (previously used cost=0 so the
currency choice was undetectable). Added two /pack/info gate tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-04 11:23:51 -04:00
gamer147
869727cc06 feat(pack-draw): per-class card pools for rotation-starter packs
Add a nullable ClassId axis to PackDrawCardWeightEntry. Null means the
row applies to any draw (the existing behavior for non-RS packs); 1..8
restricts the row to a specific class draw on a RotationStarterCardPack.
Slot rates are class-invariant (verified against per-klan captures), so
PackDrawSlotRateEntry is unchanged.

PackOpenService.Draw now takes an optional classId parameter. With a
class supplied, the card pool is filtered to (ClassId == classId ||
ClassId == null); without one, class-tagged rows are excluded so a
mistaken non-RS call against a mixed table can't leak class-specific
cards.

PackController.Open lifts the 501 guard on RotationStarterCardPack and
plumbs class_id through. Class_id is now required (and validated 1..8)
for RS packs and rejected as 400 BadRequest on non-RS packs. The skin
overload (target_card_id) remains 501 — out of scope for this work.

Seed JSON regenerated end-to-end: 32 RS packs now carry 30,160 class-
tagged weight rows; 247 non-RS packs keep their existing ~87k null-class
rows. Slot rate fix from the upstream extractor change replaces the
~99.99% conditional-rate sums on 97xxx/93xxx packs with the page's
authoritative 1.5/6/25/67.5 distribution.

Migration AddPackDrawCardWeightClassId. End-to-end bootstrap verified
against fresh Postgres; pack 93025 reads back exactly 122/119/120/120/
120/121/118/119 weights for classes 1-8.

Tests: 3 new PackOpenServiceTests (class filter happy path, disjoint
pools, defensive null-class isolation); existing class_id-on-non-RS
controller test updated 501 → 400 BadRequest; one stub-importer test
corrected to match extractor-authored gacha_detail.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-27 19:35:08 -04:00
gamer147
7118b92522 refactor(pack): type PackChildGachaEntry.TypeDetail as CardPackType enum 2026-06-09 08:48:16 -04:00
gamer147
57d231cd56 feat(pack): /pack/open supports type_detail=10 FREE_PACKS with per-campaign daily quota 2026-06-08 21:43:04 -04:00
gamer147
9c9d0fc41f feat(packs): accept all currently-supported currencies on /pack/open
Extends /pack/open beyond the v1 CRYSTAL_MULTI=2 / DAILY=3 / RUPY_MULTI=7
trio to cover every type_detail whose payment primitive already exists:

  1 CRYSTAL              -> ICurrencySpendService crystal debit
  6 RUPY                 -> ICurrencySpendService rupee debit
  4 TICKET / 5 TICKET_MULTI -> debit child.ItemId from OwnedItemEntry
                            (ticketsNeeded = cost * packNumber), 400 on
                            missing/short balance; reward_list gets a
                            RewardType=4 post-state Item entry to mirror
                            project_wire_reward_list_post_state

Skin-overload type_details (8/9/13) and free-pack overlays (10/11/12)
stay 501 — they need selection / banner plumbing the current code
doesn't have.

Tutorial alias unchanged: it still consumes the gating ticket post-draw
and stamps tutorial_step=100. The two ticket flows diverge by intent
(tutorial = free server-grant; normal = paid by ticket inventory).

Removed Open_rejects_ticket_type_detail (asserted the old 501 path);
covered by Open_rejects_insufficient_tickets. Updated
NonTutorial_pack_open_does_not_emit_tutorial_step to assert the new
200-on-ticket-success behavior — same invariant under test.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:17:11 -04:00
gamer147
1c386b5ed0 feat(packs): rewrite PackOpenService against per-pack draw table
Sampler is now driven by PackDrawTable: roll DrawTier per slot by
cumulative slot-rate weights, then pick a card within tier by per-card
weights renormalized within the tier. Rate-less Guaranteed-Leader-Card
rows draw uniform over (pool minus owned), falling back to the full
pool when all are owned. Bonus slot fires once at the end of a 10-pack
open when HasBonusSlot is set.

Slot 8 falls back to the general slot's per-card weights for the rolled
tier when slot-8 has only a rarity-level rate quoted (the common shape
on normal packs).

PackController.Open loads the draw table + viewer owned card ids and
passes them to the sampler; the category-based forced-Legendary slot-8
override is gone. ICardFoilLookup replaces ICardPoolProvider for the
foil-twin heuristic.

Drops the test-fixture pack-draw seed overlay so the production seed
flows through the importer tests; controller tests that fabricate their
own card sets now call factory.SeedPackDrawTableAsync(...) to install a
matching stub draw table.

WeightedPick helper handles the cumulative-band roll for both stages.
Five sampler tests + four WeightedPick tests + five importer/repo
tests; full suite is 653/653 green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 22:26:45 -04:00
gamer147
163299504a refactor(pack): route currency spend through CurrencySpendService (freeplay) 2026-05-29 14:10:50 -04:00
gamer147
739f629996 feat(pack): accrue gacha points on /pack/open (skip tutorial) 2026-05-29 00:07:28 -04:00
gamer147
34bcc579a5 Additional card content 2026-05-24 17:07:05 -04:00
gamer147
79209bd70b Pack opening 2026-05-24 02:03:13 -04:00