Reference  ›  DeckCraft → TcgEngine map

Structure map: DeckCraft → TcgEngine (Unity)

LIVING DOCUMENT — update on every model change. Goal: if DeckCraft games are ever exported into the purchased TcgEngine template as a digital playtest client before printing, this map says what each DeckCraft structure becomes on their side, what adapts with a mapping table, and what would need engine surgery. Companion to the TcgEngine study.

clean — direct asset generation adapt — needs a mapping table or convention surgery — needs C# changes in the template

Purpose & the honest verdict

TcgEngine is a Hearthstone-shaped engine: fixed turn structure (draw → main → end), fixed resources (hp + ramping mana), fixed piles, combat between board creatures. DeckCraft is genre-generic. Consequence: games that fit the Hearthstone shape export cleanly; deck-builders and market games need C# work in the template first (their sanctioned extension path is subclassing EffectData/ConditionData and editing GameLogic.cs — the docs say so explicitly). The map below is the itemized version of that sentence. Second consequence: the template would be a client, not a second referee — its engine is not ours, so balance data still comes from our simulator; the export is for humans to FEEL the game.

The map — structure by structure

DeckCraft structureTcgEngine counterpartFitNotes
card (name, description, type, cost, attributes) CardData asset (title, desc, type, mana, attack/hp) adapt Their type is a closed enum (Hero/Character/Spell/Artifact/Secret/Equipment) — needs a per-game mapping table (unit→Character, event→Spell, structure→Artifact). attack/hp only exist as two fixed stats; other numeric attributes become TraitStats.
tags[] / families (+ all_tags view) TraitData assets + TeamData (one axis) clean One TraitData asset per distinct tag; a family axis can ride TeamData (gets UI color treatment for free).
Free-form attributes + counters TraitStat (trait + int) / EffectSetStatCustom clean Identical concept. ƒx-computed attributes must be baked to numbers at export (their side has no expression evaluator) — or dropped with a warning.
Card cost (multi-currency ƒx) mana (single int) adapt Single-currency games map the currency to mana. Multi-currency (Ascension-style) has no home — export the primary, warn on the rest.
Effects: trigger presets
on_play / at_start_of_turn / at_end_of_turn / on_destroy / activated
AbilityTrigger:
OnPlay / StartOfTurn / EndOfTurn / OnDeath / Activate
clean 1:1. Our activation.costmana_cost (single currency), exhaustexhaust.
Pattern triggers (event bus: card_moved, tracker_adjusted, action_performed…) OnPlayOther / OnDeathOther + Secrets adapt Only play/death of other cards exist as triggers on their side; tracker-adjusted or zone-movement subscriptions need new AbilityTrigger enum values (surgery) or approximation.
Effect subject (self/opponent/each_opponent/all_players) + amounts AbilityTarget (PlayerSelf/PlayerOpponent/AllPlayers) + value clean each_opponent ≡ PlayerOpponent in 1v1. Amounts must be literal ints (bake ƒx).
Effect mechanics (draw/damage/heal/gain/discard/adjust-attribute/apply-status/remove-status/create-card) Effect* primitives (EffectDraw, EffectDamage, EffectHeal, EffectAddStat, EffectAddTrait, status[] on abilities, EffectSummon/Create) clean Their 30-primitive catalog covers ours. gain_resource maps to EffectMana only if the tracker mapped to mana.
Selection specs (chooser/count/filter) + selectors (random/highest/lowest) SelectTarget / CardSelector + conditions + FilterRandom/HighestStat/LowestStat clean Their filters are the model we copied (U6). Player-chosen = SelectTarget; automatic = filters.
Named conditions (tracker_compare, count_cards_compare, card_has_tag, zone_empty) ConditionData assets (ConditionPlayerStat, ConditionCount, ConditionCardType, ConditionCardPile) clean Preset-for-preset — their catalog seeded ours (U1). One asset per named condition, referenced by every ability that uses it: same single-source pattern.
Card sets / has_set surgery No same-field grouping concept; would be a custom ConditionData subclass (~50 lines of C#, their sanctioned path).
Bindings requires: conditions_trigger on abilities adapt Their conditions gate ABILITIES, not verbs; per-verb legality gates need GameLogic edits.
Effect condition: conditions_trigger on the ability clean Same semantics: ability silently doesn't fire.
Statuses (dynamic behaviours, value + duration) StatusData + CardStatus (value + duration) adapt Same runtime model (we copied it) — but their StatusType is a CLOSED enum with hardcoded semantics. Behaviours matching their enum (Taunt↔Outpost-ish, Stealth, Armor…) map; custom behaviours need enum + GameLogic additions.
Modifiers (buy_cost / draw_amount / damage_dealt / attribute:* / forbid) Ongoing abilities (EffectAddStat ongoing, AddManaCost status) adapt Stat auras map cleanly. buy_cost→AddManaCost. action_legality forbid has no counterpart (surgery).
Trackers (arbitrary, per-player/shared, regen rules) hp + mana only surgery THE structural gap. Two trackers map (life→hp, main resource→mana with their ramp OR a custom refill edit). Every further tracker (Dominion's actions/buys, VP) needs new Player fields + UI.
Zones (authored, per-player/shared, refills, markets) Fixed piles: deck/hand/board/discard/secret/equip/temp surgery Standard piles map by role bindings. Shared market rows, token piles, refills: no counterpart — new List<Card> + UI per zone.
Custom actions (step compositions) + action bindings — (fixed verb set: play / attack / activate / move) surgery The biggest gap. Jaipur-style verbs (take/trade/sell) don't exist; each would be a new GameAction + server handler + UI affordance. Simple ones could be faked as activated abilities on a hero.
Phases / turn structure (authored, automatic phases, step queue) Fixed StartTurn → Main → EndTurn adapt Games with one player-driven phase fit. Multi-phase turns (action/buy/cleanup) collapse into Main or need surgery.
Endings + scoring (JSON Logic, sum_cards metrics, turn-limit borrow) hp ≤ 0 (or deck+hand+board empty) adapt Combat games map. Scoring/emptiness endings need a CheckForWinner edit (small, contained).
Decks, copies (ƒx), starters DeckData assets (+ GameplayData deck lists) clean Bake ƒx copies at export per player count.
Setup spec (fill_zone, opening deals) Hardcoded StartGame() (draw 5, coin) adapt Hand size/start values are GameplayData fields; anything richer is surgery.
Bots (greedy/random, deterministic replays) Minimax + heuristic (AILogic) clean Their AI plays whatever the data says — free opponent for playtests. Determinism/replay parity is NOT preserved (different engine); balance stays our simulator's job.
Card art (cards/ renders) art_full / art_board sprites clean Our generated images drop in as sprites — the visual payoff of the whole idea.

Export pipeline sketch (when the day comes)

TcgEngine loads everything from Resources/ ScriptableObject assets (DataLoader + per-type Load() calls). Two viable routes: (a) generate Unity .asset YAML files directly from a game-def (they are plain text; GUID wiring is the fiddly part), or (b) a small Unity editor script that reads our deckcraft-game/1 JSON and instantiates the ScriptableObjects — cleaner, keeps GUIDs Unity's problem. Either way the exporter's input is exportGameDefinition() output, and ƒx values are baked at a chosen player count. Start with a Hearthstone-shaped probe game to keep the surgery list empty.

Maintenance rule

Every model change that ships (new structure, new mechanic, new card field) adds or updates its row here — treat this page as a fifth column of the four-level working rule. Rows added so far track the model through: ƒx, subjects, decisions, triggers/bus, bindings, modifiers, scoring, custom actions, card-state, tags, sets+conditions (U1), statuses (U2), activated abilities (U3), and the U4–U9 batch (combat events, choose-one, selectors, event-card targeting, runtime tokens, dice).