Working title 'tgame' is provisional. Top-level samples/ and docs/samples/ are gitignored; visual/art pipeline lives outside this repo.
163 lines
8.6 KiB
Markdown
163 lines
8.6 KiB
Markdown
# Item Cards & Visual Composition
|
||
|
||
Items aren't authored as finished PNGs. They're **composed** at runtime from a stack of layered sprites driven by the item's actual components, materials, quality, and effects. The player's crafting choices show up visually.
|
||
|
||
This is the trick that lets the game have functionally infinite visual variety from a finite asset library.
|
||
|
||
## Layer model
|
||
|
||
Most items render as a card with these layers, bottom to top:
|
||
|
||
```
|
||
1. Card frame — quality band (Crude / Common / Fine / Masterwork / Legendary)
|
||
2. Background — effect or material flavor (forge ember, poison mist,
|
||
holy rays, cursed shadow)
|
||
3. Base silhouette — the item type (longsword, dagger, vial, ring, helmet)
|
||
4. Material tint — primary material (moonsilver sheen, obsidian gleam, bronze)
|
||
5. Component overlays — per-slot: pommel gem, hilt wrap, stopper, label,
|
||
engraving — driven by the actual components used
|
||
6. Effect aura — enchantment (poison wisp, fire flicker, curse,
|
||
holy radiance)
|
||
7. Quality flourish — Masterwork particle, Legendary frame glow
|
||
8. Stamps / marks — maker's mark (engineer who crafted it), faction sigil
|
||
```
|
||
|
||
Each base item sprite defines **anchor points** for component overlays — pommel anchor, blade anchor, stopper anchor, etc. Components are authored to land on those anchors, so the gem inhabits the pommel rather than floating beside it.
|
||
|
||
## Examples
|
||
|
||
### Lapis-infused Moonsilver Falchion (Fine)
|
||
- Frame: Fine (silver border)
|
||
- Background: faint blue arcane glow
|
||
- Base: falchion silhouette
|
||
- Material tint: moonsilver pale-blue sheen
|
||
- Component overlay: lapis lazuli at pommel anchor
|
||
- Engraving overlay: frost rune
|
||
- Aura: cold mist
|
||
- Stamp: Theodric's maker's mark
|
||
|
||
### Cursed Ruby-binding Black Draught (Masterwork)
|
||
- Frame: Masterwork (gold)
|
||
- Background: deep crimson swirl
|
||
- Base: rounded apothecary vial
|
||
- Material tint: liquid opaque black with red glints
|
||
- Component overlay: bone stopper, red wax seal, ruby fragment in liquid
|
||
- Aura: dark heat-shimmer
|
||
- Flourish: drifting particles
|
||
- Stamp: Theora's apothecary mark
|
||
|
||
The player sees both items and immediately knows which components went into each. Crafting decisions are visible.
|
||
|
||
## Why this is the right call
|
||
|
||
- **Asset count collapses.** ~30 base silhouettes × ~20 material tints × ~30 component overlays × ~15 effect auras × 5 frames = combinatorial variety from a small authored library.
|
||
- **Crafting becomes visually expressive.** Every craft choice has visible consequence — the strongest feedback loop possible.
|
||
- **Functionally infinite uniques.** Even when a recipe is shared, your specific instance has your specific components, engineer stamp, and quality. No one else's is identical.
|
||
- **Future bazaar gets richer.** Listings show full composed cards, browsing becomes part of the fun.
|
||
- **Names compose too.** `[quality adjective] [material] [base] of [effect noun]` gives Diablo-style procgen names from the layer stack itself.
|
||
|
||
## Tradeoffs
|
||
|
||
- **Consistency burden.** Components must be authored to compose cleanly — same perspective, same lighting direction, same scale, same color discipline. Strict style guides + a composition test step before any new component ships.
|
||
- **Color clashes.** Lapis blue + ruby red on one sword can look muddy. Either gameplay-prevent it via recipe constraints, or lean into it as visual feedback that the craft is unstable.
|
||
- **Reserve bespoke art for Legendary or named items.** When the player discovers a truly unique recipe — *Crucible-Forged Sun-Sword of Aelis* — that one gets full hand-finished treatment. Composition for 99%, bespoke for 1%.
|
||
|
||
## Rendering: alpha layers + shader passes
|
||
|
||
Composition isn't flat sticker-stacking. Every layer carries an alpha channel and lower layers bleed through where higher ones are transparent. Atmosphere layers (swirling shadows, drifting mist, dust motes) sit between solid layers and add depth without hiding the base art.
|
||
|
||
After the layer stack composes into a single card texture, **post-process shaders** run on the composed result. Many effects need no PNG asset at all — they're pure fragment shaders.
|
||
|
||
### Pass model
|
||
|
||
```
|
||
Pass 1 — Layer composite (alpha-aware)
|
||
frame → background gradient → atmosphere overlay (alpha) →
|
||
base silhouette → material tint → component overlays →
|
||
aura layer → flourish particles → stamps
|
||
= composed card RGBA texture
|
||
|
||
Pass 2 — Effect shaders
|
||
apply post-process shader stack to the composed texture
|
||
(glow / holo / foil / corruption / first-reveal / ...)
|
||
= final card pixels
|
||
|
||
Pass 3 — UI compositing
|
||
place card in inventory frame, drop shadow, hover state
|
||
```
|
||
|
||
**Pixel art note:** the visual style is pixel art with palette-locked dithering ([09-art-style.md](09-art-style.md)). Effect shaders on the composed card should pixelate-quantize to the pixel grid when they're large or visible (holo, foil, corruption); subtle glow/bloom can stay smooth because the eye reads it as atmosphere rather than as art.
|
||
|
||
### Effects that need zero assets
|
||
|
||
| Effect | Implementation | Use case |
|
||
|---|---|---|
|
||
| Outer glow | radial blur on alpha mask, colored | Magical items |
|
||
| Holographic foil | animated UV shimmer, hue rotation over time | Rare quality bands |
|
||
| Foil shimmer | specular angle-based highlight | Legendary frames |
|
||
| Heat haze | UV displacement by animated noise | Fire / forge effects |
|
||
| Frost crackle | overlay noise, hue toward white-blue | Frost enchantments |
|
||
| Cursed corruption | chromatic aberration + vignette darkening | Cursed items |
|
||
| Holy radiance | god-rays + bloom | Blessed items |
|
||
| First-reveal sweep | animated alpha mask wipe on card open | "New Discovery!" moment |
|
||
| Faded / locked | desaturation + alpha reduction | Items beyond your tier |
|
||
|
||
This collapses asset counts even further — atmospheric / textural effects don't need a PNG, they need a shader.
|
||
|
||
### Quality bands become partly shader-driven
|
||
|
||
- **Crude**: no shader pass.
|
||
- **Common**: no shader pass.
|
||
- **Fine**: subtle outer glow, colored by primary effect.
|
||
- **Masterwork**: animated foil shimmer + drifting particles.
|
||
- **Legendary**: holographic shader + animated frame glow + drifting particles + bespoke art.
|
||
|
||
Quality is felt instantly through visual treatment, not just stat numbers.
|
||
|
||
### Performance
|
||
|
||
- **Active card view** (focused, large): all shaders + animation enabled.
|
||
- **Inventory list** (200 thumbnails): drop the shader pass, render Pass 1 only, cache the result. Or render at reduced resolution.
|
||
- **Transition**: cross-fade from cached static thumbnail → live shader version on tap-to-focus.
|
||
- **Animation gating**: shaders pause when offscreen — save battery.
|
||
- Mobile GPUs handle this trivially at 1–2 simultaneous focused cards. Don't try to run the shader stack across 50 cards in a grid.
|
||
|
||
### Engine implications (input, not decision)
|
||
|
||
Pushes mildly toward an engine with strong custom-shader support.
|
||
- **Unity**: Shader Graph + URP, well-trodden mobile path.
|
||
- **Godot**: visual shader editor is solid and free, mobile improving.
|
||
- **Flutter**: possible via `FragmentShader` + `CustomPainter`, rougher for shader-heavy UI.
|
||
|
||
## Data model implication
|
||
|
||
Every item instance stores a composition recipe, not a baked image:
|
||
|
||
```
|
||
ItemInstance {
|
||
id: UUID
|
||
base_id: "falchion"
|
||
material_ids: ["moonsilver"]
|
||
component_ids: ["pommel_gem:lapis_lazuli", "hilt_wrap:silver_thread"]
|
||
effect_ids: ["frost_aura"]
|
||
quality: Fine
|
||
maker_id: minion_uuid
|
||
}
|
||
```
|
||
|
||
The renderer reconstructs the card on demand. Save data is tiny. Serialization for future bazaar is trivial — transmit the composition recipe, receiving client reconstructs the card.
|
||
|
||
## UI implications
|
||
|
||
- **Card view is a first-class UI object**, not just an icon.
|
||
- **Card flip on craft completion** is a key game moment — the reveal is where the dopamine lives. Choreography per quality band designed in [13-reveal-choreography.md](13-reveal-choreography.md).
|
||
- **Inventory, market, share screenshots, patron orders** all reuse the same card renderer.
|
||
- **Tooltips can be lighter** because the card itself communicates so much already (gem, material, aura, quality).
|
||
|
||
## Open questions
|
||
|
||
- Do non-card uses (inventory icons, list views) use a low-detail composition or a baked thumbnail cache?
|
||
- Where does the composition test step live in the asset pipeline — automated screenshot diffing? Curator review?
|
||
- Do we expose composition recipes to players (they see the layer breakdown), or stay opaque?
|
||
- Shader budget for mid-tier mobile devices — what's the realistic ceiling for simultaneous focused cards?
|
||
- Do atmosphere layers ever animate (scrolling UV swirls), or are they static PNGs and animation only happens via shader passes?
|