Files
AppleHillsProduction/docs/card_system_playbook.md

340 lines
14 KiB
Markdown
Raw Normal View History

# Apple Hills Card System Designer Playbook
This playbook is for designers working with the raritybased Card System in Apple Hills. It provides a highlevel architecture overview, fast TL;DR playbooks for common tasks (with code-first snippets), and deeper guidance on how the system fits together.
## Table of Contents
- [Architecture at a Glance](#architecture-at-a-glance)
- [TL;DR Playbooks (Code-First)](#tldr-playbooks-code-first)
- [Open Booster Packs](#open-booster-packs)
- [Grant a Specific Card](#grant-a-specific-card)
- [Query the Collection](#query-the-collection)
- [Configure Visuals by Code (optional)](#configure-visuals-by-code-optional)
- [Clear the Collection](#clear-the-collection)
- [Authoring with the Card Editor (No Manual Setup)](#authoring-with-the-card-editor-no-manual-setup)
- [What happens automatically (under the hood)](#what-happens-automatically-under-the-hood)
- [In-Depth Overview](#in-depth-overview)
- [Designer HowTos (Quick Reference)](#designer-how-tos-quick-reference)
- [Best Practices & Tips](#best-practices--tips)
- [Known Gaps](#known-gaps)
- [FAQ](#faq)
- [Technical Reference (Quick)](#technical-reference-quick)
- [Change Log](#change-log)
## Architecture at a Glance
The system is split into two main layers with clear responsibilities and a lightweight event bridge.
- Data Layer (`Assets/Scripts/Data/CardSystem`)
- `CardSystemManager` (singleton)
- Source of truth for all card definitions, booster logic, and player `CardInventory`.
- Emits events on collection changes and booster activity.
- `CardInventory`
- Players owned cards and booster pack counters.
- Utility APIs for filtering cards (by rarity, zone, etc.).
- `CardDefinition` (`ScriptableObject`)
- Authoring asset that defines a card (name, visuals, zone, rarity tiering).
- `CardData`
- Runtime instance of a card in the player collection (ID, rarity, copies owned, links back to `CardDefinition`).
- `CardVisualConfig` (`ScriptableObject`)
- Central mapping for rarity/zone → visual treatment (colors, frames, shapes).
- UI Layer (`Assets/Scripts/UI/CardSystem`)
- `CardAlbumUI`
- Entry point for the card UI (menu, album browser, booster opening).
- `UIPageController` + `UIPage`
- Stack-based navigation (push/pop) and shared transitions.
- Page Components
- `CardMenuPage` Card hub/menu
- `AlbumViewPage` Grid album view with filter/sort options
- `BoosterOpeningPage` Interactive three-card reveal
- `CardUIElement`
- Renders a single card using `CardData` and `CardVisualConfig`
- `BoosterNotificationDot`
- Shows available booster pack count
- Events (Data ↔ UI)
- `OnBoosterCountChanged`
- `OnBoosterOpened`
- `OnCardCollected`
- `OnCardRarityUpgraded`
- Lifecycle
- Initialize during boot: `BootCompletionService.RegisterInitAction(...)`
- `CardSystemManager.Instance` is the access point from UI and tools (e.g., `CardSystemTester`)
## TL;DR Playbooks (Code-First)
These are the most common tasks youll perform in code. Paste the snippets into a temporary test `MonoBehaviour`, a unit test, or hook them into your gameplay systems.
### Open Booster Packs
```csharp
using AppleHills.Data.CardSystem;
using UnityEngine;
public class BoosterOpenerSample : MonoBehaviour
{
private void Start()
{
// Subscribe to events (optional, for feedback/UI updates)
CardSystemManager.Instance.OnBoosterOpened += cards =>
{
Debug.Log($"Opened booster with {cards.Count} cards");
};
CardSystemManager.Instance.OnCardCollected += card =>
{
Debug.Log($"Collected: {card.Definition.Name} (rarity: {card.Rarity})");
};
CardSystemManager.Instance.OnCardRarityUpgraded += card =>
{
Debug.Log($"Upgraded rarity: {card.Definition.Name} -> {card.Rarity}");
};
// Give the player some boosters and open one
CardSystemManager.Instance.AddBoosterPack(1);
var revealed = CardSystemManager.Instance.OpenBoosterPack();
foreach (var cd in revealed)
{
Debug.Log($"Revealed: {cd.Definition.Name}");
}
}
}
```
### Grant a Specific Card
```csharp
using System.Linq;
using AppleHills.Data.CardSystem;
using UnityEngine;
public class GrantCardSample : MonoBehaviour
{
[SerializeField] private string cardName = "Apple Picker"; // example
private void Start()
{
var def = CardSystemManager.Instance
.GetAllCardDefinitions()
.FirstOrDefault(d => d.Name == cardName);
if (def == null)
{
Debug.LogWarning($"CardDefinition not found: {cardName}");
return;
}
var cardData = CardSystemManager.Instance.AddCardToInventory(def);
Debug.Log($"Granted: {cardData.Definition.Name} (copies: {cardData.CopiesOwned})");
}
}
```
### Query the Collection
```csharp
using AppleHills.Data.CardSystem;
using UnityEngine;
public class QueryCollectionSample : MonoBehaviour
{
private void Start()
{
var inv = CardSystemManager.Instance.GetCardInventory();
// All cards
var all = inv.GetAllCards();
Debug.Log($"Total cards owned: {all.Count}");
// By rarity (example)
var rares = inv.GetByRarity(CardRarity.Rare);
Debug.Log($"Rares: {rares.Count}");
// By zone (example)
var forest = inv.GetByZone(CardZone.Forest);
Debug.Log($"Forest: {forest.Count}");
// Counts (progression checks)
var byRarity = inv.CountByRarity();
var byZone = inv.CountByZone();
// Iterate dictionaries as needed
}
}
```
### Clear the Collection
```csharp
using AppleHills.Data.CardSystem;
using UnityEngine;
public class ClearCollectionSample : MonoBehaviour
{
private void Start()
{
CardSystemManager.Instance.ClearAllCards();
Debug.Log("Card collection cleared (dev/test only).");
}
}
```
## Authoring with the Card Editor (No Manual Setup)
Designers should use the dedicated editor window: `AppleHills/Card Editor`.
- Open via Unity menu: `AppleHills/Card Editor` (menu path defined in `CardEditorWindow`)
- The window provides a card list, detail editor, and a live preview of the UI card.
- You dont need to create folders or assets manually—the tool handles setup and discovery.
### What happens automatically (under the hood)
The `Assets/Editor/CardSystem/CardEditorWindow.cs` implements the following behaviors:
- Ensures the data directory exists: `Assets/Data/Cards` (creates it on first run).
- Discovers all `CardDefinition` assets under `Assets/Data/Cards` automatically via `AssetDatabase.FindAssets("t:CardDefinition")` and keeps the list sorted by `Name`.
- Creates new card assets as `Card_<Name>.asset` inside `Assets/Data/Cards`.
- Loads a UI preview prefab from `Assets/Prefabs/UI/Cards/SIngleCardDisplayUI.prefab` to render a live card preview inside the editor using `PreviewRenderUtility`.
- Tries to load `CardVisualConfig` at `Assets/Data/Cards/CardVisualConfig.asset` for consistent rarity/zone styling in the preview.
- Responds to Undo/Redo and Editor recompiles to stay in sync.
- When you duplicate or delete cards from the window, the corresponding assets are created/removed accordingly.
Result: Designers focus on content (name, description, zone, art, etc.) in the Card Editor. The system takes care of folders, discovery, previewing, and syncing definitions with runtime.
## In-Depth Overview
Data Layer Details
- `CardDefinition` (authoring)
- What it is: a `ScriptableObject` that defines a cards identity and static presentation (name, description, zone, default visuals).
- Typical fields: `Name`, `Description`, `Zone`, rarity metadata, sprites/textures, optional tags.
- Helpers: `CreateCardData()` to generate a runtime `CardData` instance for inventory.
- `CardData` (runtime instance)
- Purpose: Represents what the player owns unique ID, rarity state, copies owned.
- Behavior: When duplicates are collected, `CardData` may upgrade rarity (e.g., duplicate thresholds).
- Links: Holds reference to its `CardDefinition` for display and classification.
- `CardInventory` (player collection)
- Responsibilities: Store cards, track counts, support filters/lookups (by rarity/zone), manage booster counters.
- Common operations: `AddCard(def)`, `GetAllCards()`, `GetByRarity()`, `GetByZone()`, `CountByRarity()`, `CountByZone()`.
- Notes: Designed for efficiency and clarity; returns collections for the UI to display.
- `CardSystemManager` (singleton coordinator)
- Responsibilities: Owns `CardInventory`, holds the card definition catalog, controls booster generation/opening, exposes public APIs.
- Events: `OnBoosterCountChanged`, `OnBoosterOpened`, `OnCardCollected`, `OnCardRarityUpgraded`.
- Typical APIs:
- `GetAllCardDefinitions()``IReadOnlyList<CardDefinition>`
- `GetCardInventory()``CardInventory`
- `AddBoosterPack(int count)`
- `OpenBoosterPack()``List<CardData>` (or similar)
- `AddCardToInventory(CardDefinition def)`
- `ClearAllCards()`
- `CardVisualConfig` (visual mappings)
- Purpose: Central place to establish the brand/look per rarity and zone.
- Implementation: `ScriptableObject` with dictionaries for quick lookups by rarity/zone.
UI Layer Details
- `UIPageController` + `UIPage`
- Navigation: `PushPage(page)`, `PopPage()`, `Clear()`; optional transition animations.
- Pattern: Each page is a `UIPage` subclass registered under `CardAlbumUI`.
- `CardAlbumUI` (system entry)
- Role: Wires pages together, responds to `CardSystemManager` events, routes to `AlbumViewPage` or `BoosterOpeningPage`.
- `AlbumViewPage`
- Role: Displays a grid of `CardUIElement` items from `CardInventory` with filtering/sorting UI.
- Notes: Uses TextMeshPro components and simplified presentation (no stack/slot system).
- `BoosterOpeningPage`
- Role: Handles the interactive reveal flow for 3 cards per booster; flip animations, particle FX, rarity feedback.
- Notes: Timings and art are still under refinement; supports individual reveal clicks.
- `CardUIElement`
- Role: Displays a single `CardData` name, art, frame, rarity chips, zone styling.
- Data Source: Uses `CardVisualConfig` for color/frame mapping.
- `BoosterNotificationDot`
- Role: Indicates current booster count; listens to `OnBoosterCountChanged`.
Event Flow Examples
- Opening a booster
- Player code calls `CardSystemManager.OpenBoosterPack()` → generates/awards 3 cards → raises `OnBoosterOpened`.
- Inventory updated; for each card, `OnCardCollected` (and maybe `OnCardRarityUpgraded`) fires.
- `BoosterOpeningPage` animates reveals; `AlbumViewPage` updates when returning.
- Adding a card via quest reward
- Gameplay script calls `AddCardToInventory(def)`.
- Inventory updates; events fire to update UI.
Rarity & Duplicates
- The system tracks copies owned per card.
- Duplicates can trigger rarity upgrades based on thresholds (implemented in `CardData`).
- UI can react with special effects via `OnCardRarityUpgraded` (e.g., distinct VFX/SFX per rarity).
## Designer HowTos (Quick Reference)
Author a New Card (use the editor tool)
1) Open `AppleHills/Card Editor`.
2) Click New/Duplicate, edit `Name`, `Description`, `Zone`, and assign art.
3) Save—asset is created as `Assets/Data/Cards/Card_<Name>.asset`. Its auto-discovered by runtime.
Grant a Card by Code (quest reward)
```csharp
CardSystemManager.Instance.AddCardToInventory(rewardDefinition);
```
Open a Booster by Code
```csharp
CardSystemManager.Instance.AddBoosterPack(1);
CardSystemManager.Instance.OpenBoosterPack();
```
Filter Cards for a Page
```csharp
var inv = CardSystemManager.Instance.GetCardInventory();
var list = inv.GetByZone(CardZone.Quarry);
```
## Best Practices & Tips
- Always access data via `CardSystemManager.Instance` to keep a single source of truth.
- Keep `CardDefinition` assets lightweight and consistent; use tags/zones for filtering.
- When adding new rarities/zones, update `CardVisualConfig` and verify UI styling.
- For smoother reveals, coordinate with VFX/SFX to differentiate rarities.
- Large collections: consider paging or virtualized grids if performance becomes a concern.
## Known Gaps
- Save/Load not implemented test sessions wont persist player collection.
- Booster Opening polish (timings/art/audio) is ongoing.
- Collection statistics/achievements UI pending.
## FAQ
- Q: My new `CardDefinition` doesnt show up in the album.
- A: Ensure the asset is in `Assets/Data/Cards` (the editor tool places it there) and that definitions load before UI opens. Check the `CardSystemManager` discovery and your zones/rarities.
- Q: How do I trigger the card UI from another part of the game?
- A: Use `CardAlbumUI`s public entry points or route through your UI flow to push the relevant page via `UIPageController`.
- Q: Can I guarantee a specific rarity in a booster for a tutorial?
- A: Add a temporary tutorial hook in `CardSystemManager` to override booster generation rules for guided moments, or directly grant specific `CardDefinition`s for the tutorial step.
- Q: How do duplicates upgrade rarity?
- A: The logic lives in `CardData`; when copies cross thresholds, it fires `OnCardRarityUpgraded`. Coordinate with design to set thresholds and with UI to show feedback.
## Technical Reference (Quick)
- Initialize after boot: `BootCompletionService.RegisterInitAction(InitializePostBoot)`
- Data access: `CardSystemManager.Instance`
- Navigation: `UIPageController.Instance.PushPage()` / `PopPage()`
- Show a card in UI: `CardUIElement.SetupCard(cardData)`
- Test harness: `CardSystemTester` (add to scene, link `CardAlbumUI`)
## Change Log
- v1.1: Added Table of Contents, inline code formatting, code-first TL;DR playbooks, and an authoring section based on `CardEditorWindow`.
- v1.0: Initial designer playbook, aligned with Implementation Plan and Integration & Testing Guide.