Appearance
Riftbound card data
Ruleset and calculator scripts get Riftbound cards as plain objects straight from the card catalog. This page lists every field on those objects and in the catalog globals, with a real example and a few recipes.
Where the cards show up:
- Card rules receive one card as their first argument.
- Deck and deck set rules receive
cardsById, an object keyed by card ref that holds every card in the deck (it can hold more). - Calculators receive
cardsByIdas their second argument.
See the ruleset script API and the calculator script API for the full function signatures.
Card keys and deck refs
A Riftbound card in the catalog is a card identity: one playable card with all of its printings (showcase, signature and alternate-art prints, reprints in later sets) listed under printings. Two kinds of id matter:
- The identity id,
card.id:rb-plus a hash, likerb-34ece2975dc9e4adcb5e. - A printing id: set, collector number and set size, like
ogn-030-298. Treatments add a suffix:ogn-030a-298is the alternate art.
Decks store printing ids (decks saved before printing refs existed may still use identity ids). cardsById is keyed by the deck's refs, and each one points to its identity, so two printings of the same card in one deck resolve to the same object:
- Count copies of "the same card" by
card.id, not by ref. - To read the printing a ref points to, find it in the list:
card.printings.find((p) => p.id === ref). ctx.globals.aliases[ref]gives the identity id for any printing or variant id, when globals are available.
A card rule gets the identity card itself, so card.id is always the identity id.
The game fields describe the card's default printing, the first entry of printings (game.defaultPrintingId). That matters for per-printing fields such as rarity, variant and artistName.
Guard against a missing card before reading from it: cardsById[ref] can be undefined when the catalog doesn't know a ref.
Card fields
Every card, in every game, has these top-level fields:
| Property | Type | Description |
|---|---|---|
id | string | Catalog id of the card identity ("01DE012" in LoR, "pikachu-ex--1f29b66b68bc" in Pokémon). |
name | string | Display name in the catalog locale. |
setCode | string | Set of the card, or of its default printing ("set1", "sv2", "OGN"). |
collectible? | boolean | False for cards that can't go in a deck (LoR tokens and level-ups). |
assets? | CardCatalogCardAssetContract[] | Images of the card's default printing. |
printings? | CardPrintingContract[] | Every printing of the identity, default printing first. Absent in LoR. |
hasImage? | boolean | False when none of the card's printings has an image in this language. Absent means it has one. |
game? | TGameData | Game-specific fields; see the per-game *CardGameData type. |
Printings
Each entry of printings (CardPrintingContract):
| Property | Type | Description |
|---|---|---|
id | string | Printing id; deck refs use it ("sv2-11" for Pokémon, "ogn-030-298" for Riftbound). |
setCode | string | Set the printing belongs to ("sv2", "OGN"). |
number? | string | Collector number within the set. |
language? | string | Language of the printing, when it is not the catalog's own. |
releaseDate? | string | Release date as the source gives it ("2023/06/09"). |
assets? | CardCatalogCardAssetContract[] | This printing's own images. |
variants? | CardVariantContract[] | Treatments of this printing (foil, alternate art, …). |
metadata? | JsonObject | Extra per-printing data (rarity, artist, store ids); game-specific. |
Each entry of a printing's variants:
| Property | Type | Description |
|---|---|---|
id | string | Variant id, usually <printing id>:<kind> ("ogn-030-298:normal"). |
kind? | string | Treatment kind ("normal", "foil", "alternate_art"); values are per game. |
label? | string | Display label, when the kind alone is not enough. |
metadata? | JsonObject | Extra per-variant data; game-specific. |
Each entry of assets (on the card and on each printing):
| Property | Type | Description |
|---|---|---|
kind | CardAssetKind | What the image shows: "card" (framed render), "full" (full art), "icon", "banner", "slice", "portrait", … |
url | string | Image URL. |
format? | CardAssetFormat | Image format ("webp"), when known. |
width? | number | Width in pixels, when known. |
height? | number | Height in pixels, when known. |
metadata? | JsonObject | Extra per-asset data; game-specific. |
Game fields (card.game)
Everything specific to Riftbound lives on card.game. Riftbound is English only, so classification values (type, rarity, domains) are lower-case tokens you can compare directly; look them up in the globals for display names.
| Property | Type | Description |
|---|---|---|
type? | RiftboundCardType | Card type: "unit", "spell", "gear", "battlefield", "rune" or "legend". Champions are units with supertype: "Champion". |
supertype? | string | Printed supertype when there is one: "Champion", "Signature", "Basic", "Token". |
rarity? | RiftboundRarity | Rarity of the default printing: "common", "uncommon", "rare", "epic", "showcase", … |
domains? | RiftboundDomain[] | Domains (colors): ["fury"], ["calm", "mind"]. A card with no domain carries ["colorless"]. |
tags? | string[] | Printed tags such as champion names and factions (["Jinx", "Zaun"]). |
energy? | number | Energy cost. |
might? | number | Might (combat strength); units only. |
power? | number | Power cost: the domain-rune part of the cost, paid on top of energy. |
collectorNumber? | string | Collector number of the default printing within its set, without leading zeros ("30"). |
defaultPrintingId? | string | Id of the default printing ("ogn-030-298"): the first regular printing, from the earliest set, with the normal treatment. Always the first entry of printings. |
legalities? | RiftboundCardLegalities | Legality per format ({ standard: "legal" } or "banned"). A missing format counts as legal. |
text? | string | Rules text as HTML, with [Keyword] markers and :rb_…: symbol tokens. |
textPlain? | string | Rules text without HTML tags; [Keyword] markers and :rb_…: tokens stay. |
flavorText? | string | Flavor text. |
keywordRefs? | string[] | Keywords in the rules text, as globals.keywords keys (["accelerate", "assault"]); [Assault 2] becomes "assault". |
orientation? | RiftboundCardOrientation | "portrait", or "landscape" for battlefields. |
variant? | string | Treatment of the default printing: "normal", "foil", "showcase", "ultimate", "alternate_art" or "signature". |
foil? | boolean | True when the default printing is foil. |
alternateArt? | boolean | True when the default printing is an alternate-art print. |
signature? | boolean | True when the default printing is a signature print. |
artistName? | string | Artist of the default printing. |
Globals (ctx.globals)
The catalog's globals are lookup tables for the values on a card (domains: ["fury"] resolves through ctx.globals.domains.fury), the keyword glossary, the rules-text symbols and the printing alias map.
When globals are available
Globals are passed to ruleset scripts as ctx.globals (the last argument). Calculators may not receive ctx at all, and a ruleset can run without it too. Always check ctx && ctx.globals before using them and fall back to the raw value.
| Property | Type | Description |
|---|---|---|
domains? | Record<string, RiftboundCatalogDomainLookupEntry> | Domains by domains value ("fury" → Fury). |
rarities? | Record<string, RiftboundCatalogRarityLookupEntry> | Rarities by rarity value ("epic" → Epic). |
types? | Record<string, RiftboundCatalogTypeLookupEntry> | Card types by type value ("unit" → Unit). |
sets? | Record<string, RiftboundCatalogSetLookupEntry> | Sets by the card's setCode ("OGN" → Origins). |
formats? | Record<string, RiftboundCatalogFormatLookupEntry> | Formats by legalities key ("standard"). |
keywords? | Record<string, RiftboundCatalogKeywordEntry> | Keyword glossary, keyed by normalized keyword id; filtered to keywords the ingested catalog actually references. |
symbols? | Record<string, RiftboundCatalogSymbolEntry> | Inline symbol-token lexicon (:rb_...:), keyed by token key. |
aliases? | Record<string, string> | Printing or variant id → owning card identity id ("ogn-030-298" → "rb-34ece2975dc9e4adcb5e"); identity ids map to themselves. Deck refs may be either. |
Lookup entries
domains, rarities, types, sets, formats and keywords map a key to an entry with these fields:
| Property | Type | Description |
|---|---|---|
label | string | Display name ("Fury", "Origins"). |
color? | string | Display color set by an operator override; usually absent. |
icon? | string | null | Icon image URL set by an operator override; usually absent. |
order? | number | Sort position in filters and lists, lowest first. |
showInFilter? | boolean | Whether the entry is offered as a filter option; absent means shown. |
metadataRef? | RiftboundCatalogMetadataReference | Anchor an operator override is matched on. |
Keyword entries
keywords entries also have:
| Property | Type | Description |
|---|---|---|
description? | string | Reminder text; may contain :rb_…: symbol tokens. |
Symbol entries
Each entry of symbols:
| Property | Type | Description |
|---|---|---|
token | string | The full inline token as it appears in rich text, e.g. ":rb_energy_1:". |
Example card
Jinx, Demolitionist, trimmed to two printings with one image each:
json
{
"id": "rb-34ece2975dc9e4adcb5e",
"name": "Jinx - Demolitionist",
"setCode": "OGN",
"collectible": true,
"assets": [
{
"kind": "card",
"url": "https://cdn.turny.gg/cards/riftbound/assets/ogn-030-298-card-80a26a1b25235660a480721f141f212244485ad006bbd2602247425901e8b11e.webp"
}
],
"printings": [
{
"id": "ogn-030-298",
"setCode": "OGN",
"number": "30",
"assets": [
{
"kind": "card",
"url": "https://cdn.turny.gg/cards/riftbound/assets/ogn-030-298-card-80a26a1b25235660a480721f141f212244485ad006bbd2602247425901e8b11e.webp"
}
],
"variants": [{ "id": "ogn-030-298:normal", "kind": "normal" }],
"metadata": { "tcgplayerId": "652802" }
},
{
"id": "ogn-030a-298",
"setCode": "OGN",
"number": "30",
"assets": [
{
"kind": "card",
"url": "https://cdn.turny.gg/cards/riftbound/assets/ogn-030a-298-card-03360e2876cef6388860f6cd8445bd352c6a95eea244e015019869cd5d693365.webp"
}
],
"variants": [
{ "id": "ogn-030a-298:alternate_art", "kind": "alternate_art" }
],
"metadata": { "tcgplayerId": "652803" }
}
],
"hasImage": true,
"game": {
"type": "unit",
"supertype": "Champion",
"rarity": "rare",
"domains": ["fury"],
"tags": ["Jinx", "Zaun"],
"energy": 3,
"might": 4,
"power": 1,
"collectorNumber": "30",
"defaultPrintingId": "ogn-030-298",
"legalities": { "standard": "legal" },
"text": "<p>[Accelerate] (You may pay :rb_energy_1::rb_rune_fury: as an additional cost to have me enter ready.)<br />[Assault 2] (+2 :rb_might: while I'm an attacker.)<br />When you play me, discard 2.</p>",
"textPlain": "[Accelerate] (You may pay :rb_energy_1::rb_rune_fury: as an additional cost to have me enter ready.)[Assault 2] (+2 :rb_might: while I'm an attacker.)When you play me, discard 2.",
"keywordRefs": ["accelerate", "assault"],
"flavorText": "I really need a new gun. But don't tell my other guns.",
"orientation": "portrait",
"variant": "normal",
"artistName": "Kudos Productions"
}
}Common recipes
js
// Calculator: cards per type, with champions split out.
export default function compute(pile, cardsById) {
const counts = {};
Object.keys(pile).forEach((ref) => {
const game = (cardsById[ref] && cardsById[ref].game) || {};
const type =
game.supertype === "Champion" ? "champion" : game.type || "other";
counts[type] = (counts[type] || 0) + pile[ref];
});
return {
widgets: [
{
kind: "donut",
label: "Card types",
segments: Object.keys(counts).map((type) => ({
label: type,
value: counts[type],
})),
},
],
};
}js
// Calculator: main-deck copies at each energy cost, 7+ grouped.
export default function compute(pile, cardsById) {
const buckets = [0, 0, 0, 0, 0, 0, 0, 0];
Object.keys(pile).forEach((ref) => {
const game = (cardsById[ref] && cardsById[ref].game) || {};
if (typeof game.energy !== "number") {
return; // Legends, runes and battlefields have no energy cost.
}
buckets[Math.min(game.energy, 7)] += pile[ref];
});
return {
widgets: [
{
kind: "histogram",
label: "Energy curve",
buckets: buckets,
labels: ["0", "1", "2", "3", "4", "5", "6", "7+"],
},
],
};
}js
// Card rule: commons and uncommons only. Legends, runes and battlefields pass.
export default function validateCard(card) {
const game = card.game || {};
const exempt = ["legend", "rune", "battlefield"];
if (exempt.indexOf(game.type) !== -1) {
return true;
}
if (game.rarity === "common" || game.rarity === "uncommon") {
return true;
}
return {
valid: false,
issues: [{ message: card.name + " is " + game.rarity + "." }],
};
}js
// Card rule: not banned in Standard. A card with no entry counts as legal.
export default function validateCard(card) {
const legalities = (card.game && card.game.legalities) || {};
if (legalities.standard !== "banned") {
return true;
}
return {
valid: false,
issues: [{ message: card.name + " is banned in Standard." }],
};
}js
// Card rule: Fury cards only. Uses the domain's display name when globals exist.
export default function validateCard(card, ctx) {
const domains = (card.game && card.game.domains) || [];
if (domains.indexOf("fury") !== -1) {
return true;
}
const globals = ctx && ctx.globals;
const fury =
globals && globals.domains && globals.domains.fury
? globals.domains.fury.label
: "fury";
return {
valid: false,
issues: [{ message: card.name + " is not a " + fury + " card." }],
};
}