All checks were successful
Reindex Knowledge Base / reindex (push) Successful in 5s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
70 lines
4.0 KiB
Markdown
70 lines
4.0 KiB
Markdown
# 7. Card Model Changes and Variant System
|
|
|
|
[< Back to Index](README.md) | [Next: Display and Visual Identity >](08-display.md)
|
|
|
|
---
|
|
|
|
## 7.1 How the Game Engine Resolves Ratings and Images for a Card
|
|
|
|
When the bot builds a game lineup or displays a card, it checks `card.variant`:
|
|
|
|
**For ratings (game engine):**
|
|
1. Read `card.variant` from the card instance
|
|
2. Call `GET battingcardratings/player/{player_id}?variant={card.variant}` — the API filters by
|
|
`(player_id, variant)` and returns the matching `battingcard` row with its nested vL/vR ratings
|
|
3. Each variant has its own distinct `battingcard` row (with `battingcard.variant` matching
|
|
`card.variant`). The ratings rows themselves don't carry a variant — they belong to a specific
|
|
`battingcard`, and the variant distinguishes which `battingcard` to use
|
|
4. `variant = 0` is the base card (unchanged behavior); evolved/cosmetic variants use higher values
|
|
|
|
> **Current implementation:** `card.variant` does not exist, yet, but `battingcard.variant` column does already exist
|
|
> (default 0). The API endpoint and game engine query path (`gameplay_queries.py →
|
|
> get_batter_scouting_or_none`) already pass variant through. The local Postgres cache
|
|
> (`batterscouting`) keys on `battingcard_id`, which is inherently variant-specific since each
|
|
> variant produces a separate `battingcard` row. Same pattern for `pitchingcard`/`pitcherscouting`.
|
|
|
|
**For images (card display):**
|
|
1. Read `card.variant` from the card instance
|
|
2. If `variant` is not 0: fetch `battingcard.image_url` for that variant — the pre-rendered
|
|
image (PNG or APNG) with cosmetics and evolution visuals baked in
|
|
3. If `variant` is 0: use `player.image` / `player.image2` (base card, unchanged behavior)
|
|
|
|
> **Migration required:** `battingcard.image_url` and `pitchingcard.image_url` columns do not
|
|
> exist yet — they must be added (nullable varchar). Current image resolution uses `player.image`
|
|
> and `player.image2` fields exclusively (checked via `Player.batter_card_url` /
|
|
> `Player.pitcher_card_url` properties in `gameplay_models.py`, and `helpers.player_bcard` /
|
|
> `helpers.player_pcard` in the legacy cog system). Bot display logic must be updated to check
|
|
> `battingcard.image_url` first when `card.variant != 0`, falling back to `player.image`.
|
|
|
|
The variant field on the card instance acts as a simple pointer. The bot does not need to know
|
|
about evolution tiers, cosmetics, or boost profiles — it just reads the variant and gets the
|
|
right ratings and image. All complexity is in the variant creation/update path, not the read path.
|
|
|
|
**All card instances of the same `player_id` on the same team share the same variant.** The
|
|
variant is stored authoritatively on `evolution_card_state` and propagated to `card.variant` on
|
|
all matching instances when it changes. Duplicate cards are not differentiated.
|
|
|
|
## 7.2 Evolved Card Naming
|
|
|
|
When `evolution_card_state.fully_evolved = true`, the card display name is modified:
|
|
- Base name: `"Mike Trout"`
|
|
- Evolved name: `"[TeamName]'s Evolved Mike Trout"`
|
|
|
|
The `player.p_name` field is NOT modified (it is a shared blueprint). The evolved name is
|
|
computed dynamically from the card state record at display time. This preserves data integrity
|
|
and allows future re-display if the naming convention changes.
|
|
|
|
## 7.3 Card Uniqueness
|
|
|
|
**Within a team:** All copies of the same `player_id` are identical — same evolution state, same
|
|
variant, same boosts, same cosmetics. Duplicates are not differentiated in any way.
|
|
|
|
**Across teams:** Two teams that evolve the same `player_id` with the same boost profile and
|
|
same cosmetics will share the same variant hash — and therefore the same ratings rows and S3
|
|
image. This is by design: shared variants avoid redundant storage and rendering.
|
|
|
|
Uniqueness diverges when teams purchase different cosmetics — each distinct combination produces
|
|
a different variant hash, creating a genuinely different card with its own rendered image.
|
|
Premium cosmetic customization is the path to a truly unique card. Rating boosts are determined
|
|
by auto-detected card profile and are not customizable.
|