Cards and Decks - ArnoldSmith86/virtualtabletop GitHub Wiki

For an overview of how cards and decks work, read the information below and check out a YouTube tutorial at https://www.youtube.com/watch?v=xmdMTWuRO-E&t=2371s (and also later in the video at https://www.youtube.com/watch?v=xmdMTWuRO-E&t=3377s).  

Cards

Cards are a type of widget with specific behavior and default settings designed to simulate different types of cards. Most of the properties of card widgets are defined in their deck, and every card must belong to a deck. Cards are not visible in edit mode, but they are selectable in the JSON editor. In the JSON editor (or by using SET) a property can be set on the cards directly, and that property will override any properties set in cardDefaults or cardTypes.

Although one normally thinks of cards as having two faces, a front and a back, cards in VirtualTableTop can have an arbitrary number of faces.

Decks

A deck consists of a number of cards, each of which is an instance of a cardType. A deck may have multiple instances of a given cardType (for example, a standard 54-card deck might have two identical jokers); it may also have no instances of a given cardType (for example, the default deck contains cardTypes defining jokers, but the deck itself contains no jokers, so contains no cards of that cardType).

A particular property of an individual card in a deck takes its value from the card widget if it is defined there. Otherwise, if it is defined in the card's cardType, it takes its value from that definition. Otherwise, if it is defined in the deck's cardDefaults, it takes its value from that definition. Finally, if it is not defined in any of those places, it takes a default value assigned by the system. In addition, properties specific to a given face may be defined in the properties object of the faceTemplate describing a given face; these become the default for that property when that face is active, and will override properties set in cardDefaults but not properties set in cardTypes or directly on the card.

Decks appear in edit mode as a circle with a pile of cards icon and a number inside it. The number reflects the number of cards actually available in the room for that deck. For example, the default deck shows 52, because the jokers are not in the deck but are included as one of the cardTypes.

A portion of the JSON for the default deck is shown below. The various elements are described after that.

  "cardDefaults": {
    "width": 103,
    "height": 160
  },
  "cardTypes": {
    ...
    "C 01": {
      "image": "/i/cards-default/AC.svg",
      "suit": "C",
      "suitColor": "♣",
      "suitAlt": "1♣",
      "rank": "01",
      "rankA": "5A",
      "rankFixed": "01 C"
    },
    ...
  },
  "faceTemplates": [
    {
      "border": false,
      "radius": false,
      "objects": [
        {
          "type": "image",
          "x": 0,
          "y": 0,
          "width": 103,
          "height": 160,
          "value": "/i/cards-default/2B.svg",
          "color": "transparent"
        }
      ]
    },
    {
      "border": false,
      "radius": false,
      "objects": [
        {
          "type": "image",
          "x": 0,
          "y": 0,
          "width": 103,
          "height": 160,
          "color": "transparent",
          "dynamicProperties": {
            "value": "image"
          }
        }
      ]
    }
  ]

cardDefaults

cardDefaults contain default properties that apply to every card in a deck. Usually the width and height of every card in a deck is the same, so they would normally be set in cardDefaults. (Note that the cardDefaults given here do not actually appear in the JSON for the default deck, as these are the default values for the width and height parameters.)

cardDefaults is where you would put a clickRoutine that you want to have happen every time a card from the deck is clicked. Also, you can modify the attributes of piles by putting an onPileCreation property here.

cardTypes

cardTypes contains properties that only apply to a specific type of card (e.g. an Ace of Clubs, shown). The values of properties for each cardType that are entered in the Deck Editor are stored in cardTypes. Properties set in cardTypes override properties set in cardDefaults.

Each cardType will often contain specific images. For example, a dynamicProperties reference in a faceTemplates object may be "nest": "/assets/-1177660470_1251". "Nest" is the variable and that assets reference is the link to an image uploaded into VTT. In a different card, the nest might reference a difference asset image. Another (more complicated, but easier to adjust once completed) method is demonstrated in the Demo Library in the Card Images with CSS Classes room. Using the methods demonstrated there, you could do something like "nest": "bigNest" and "nest": "littleNest". In that case bigNest and littleNest would have the asset links on the card deck and changes to the image would be applied across all the cards.

faceTemplates: text, image, icon

faceTemplates is used to define the layout for each face of the cards.

The objects within each face are the components that are displayed on the face from the bottom up in the order listed. The object types can be text, image, icon, write, or html. The first three are discussed here; write objects (text players can type into while playing) and html types are discussed in the sections that follow.

Most decks have playing cards with identical back sides. In the default deck the back side is the only object in the top face. It is given a type of image (because it is an image), and a value that refers to the url of the image if desired. The x and y given are relative to the card, so an image object with a y of 50 would appear 50 pixels below the top of the card. For images, the color is what will show through transparent portions of the image, and for text it is the text color. Background color would need to be set in css in either cardDefaults, or in faceTemplates (in the same spot where border and radius are set in the default deck).

The second faceTemplate in the default deck specifies that the face of each card will have a different image, by using dynamicProperties. For this faceTemplate, "dynamicProperties":{"value":"image"} says use the value of the card's "image" property (stored in cardTypes) for the object's "value" property.

Each card in the default deck has two faces, so there are two faceTemplates defined. In general, however, a faceTemplate can be an array of objects, one for each face. Each object contains an array , "objects". Each element of the "objects" array specifies a property of the corresponding face. A property is the same for all cards if the value is set directly in the object element. dynamicProperties is used to map card properties to object properties. Each key of dynamicProperties specifies the name of an object property to provide. The value of that key is the name of the property of the card to use. Consider the following faceTemplates specification:

  "faceTemplates": [
    {
      "objects": [
        {
          "type": "image",
          "color": "blue",
          "width": 103,
          "height": 160
        }
      ],
      "border": 4
    },
    {
      "objects": [
        {
          "type": "icon",
          "x": 25,
          "y": 55,
          "size": 50,
          "dynamicProperties": {
            "value": "iconShape",
            "color": "iconColor"
          }
        },
        {
          "type": "text",
          "x": 0,
          "y": 0,
          "fontSize": 20,
          "textAlign": "center",
          "width": 103,
          "dynamicProperties": {
            "value": "city",
            "color": "textColor"
          }
        }
      ],
      "border": 1,
      "radius": 8
    }
  ]

This provides face templates for a deck in which each card contains two faces. The first face is blue for all cards, with the given width and height, and a 4 pixel border. The contents of the second face depend on the card. The second face has two elements on it. The first is an icon whose shape and color is set on each card using the dynamicProperties values for iconShape and iconColor. It is positioned approximately in the middle of the card and is size 50. The second element on the face is text with fontSize 20, positioned at the upper left corner of the card (at (0,0)) and centered. The text on each card is given by the city property of the card and the color of the text is the textColor property of the card.

Example cardTypes that would work with this:

"cardTypes": {
    "newYork": {
      "city": "New York",
      "textColor": "red",
      "iconShape": "various-artists/infinity",
      "iconColor": "red"
    },
    "london": {
      "city": "London",
      "textColor": "green",
      "iconShape": "heat",
      "iconColor": "blue"
    }
  },

In addition to defining face objects, faceTemplates can set properties linked to the active face through a "properties" object. Properties defined in the "properties" object of a face become the default for that property when that face is active (overriding properties sent in cardDefaults but not properties set in cardTypes or directly on the card).

In the following often requested example from the default deck, a properties parameter has been added that enlarges only the face up side since there is no point in enlarging the back of the card.

"faceTemplates": [
    {
      "objects": [
        {
          "type": "image",
          "width": 103,
          "height": 160,
          "value": "/i/cards-default/2B.svg"
        }
      ]
    },
    {
      "objects": [
        {
          "type": "image",
          "width": 103,
          "height": 160,
          "dynamicProperties": {
            "value": "image"
          }
        }
      ],
      "properties": {
        "enlarge": 2     
      }
    }
  ]

Additional properties that will not be displayed on the cards can be added to each cardType directly. For example, it is possible to create a changeRoutine or clickRoutine.

faceTemplates: html

The faceTemplate examples discussed in the previous section objects consisting of either image or text. A third available category is html. This allows for styled text, inline embedded images, and generally most anything that is allowed in html. Here is an example where html is the 3rd element on this particular object. Additional examples are in the Decks tutorial, HTML variant.

"faceTemplates": [
    {
      ...
    },
    {
      "objects": [
       {
          "type": "image",
          ...
        },
        {
          "type": "text",
          ...
        },
        {
          "type": "html",
          "css": {
            "body": {
              "display": "flex",
              "flex-direction": "column"
            },
            ".desc": {
              "flex": 1
            },
            ".footer": {
              "font-size": "12px"
            }
          },
          "x": 0,
          "y": 60,
          "width": 140,
          "height": 64,
          "value": "<div class=desc>${PROPERTY description}</div><div class=footer>${PROPERTY summary}</div>"
        }
      ]
    }
  ]

faceTemplates: write

A write object is a text object that players can type into while playing, so a game does not need a label widget parked on top of a card to let players write on it. What a player types is stored on the card, in the card property that the object's value is bound to through dynamicProperties. Because it ends up in a normal widget property it syncs to every player, is part of the room state and its history, is still there after a room reload, triggers the card's changeRoutine, and can be read and written by routines like any other property (${PROPERTY note}, SET).

That binding is what makes the text persistent, so it is required: a write object without "dynamicProperties": { "value": ... } β€” or one with a static value, which would override the binding β€” simply renders as read-only text. Binding it to a property the engine itself owns (type, parent, x, width, ...) is refused as well, since every keystroke would overwrite it. The debug table reports both cases.

{
  "type": "write",
  "placeholder": "write your note here…",
  "x": 12,
  "y": 40,
  "width": 176,
  "height": 88,
  "fontSize": 14,
  "dynamicProperties": {
    "value": "note"
  }
}

On top of the properties a text object has (x, y, width, height, fontSize, textAlign, color, css, ...), a write object has:

  • placeholder β€” the hint shown in the box while the bound card property is still empty. It is what tells a player that the card can be written on at all, and it is also what the Deck Editor and the card previews show for the object, since those are read-only copies that cannot be typed into.
  • backgroundColor β€” fills the box, without needing a css object. Default transparent.
  • borderColor β€” outlines the box, so players can see where they can write. Defaults to currentColor, the object's own text color, so the outline stays readable on a dark card face too; set it to transparent for an invisible box.
  • spellCheck β€” whether the browser underlines misspelled words in what players type. Off by default, like on a label.
  • editable β€” true by default; a write object is writable by definition and this property exists to take that away again. Bound per card ("dynamicProperties": { "editable": "unlocked" }) it lets a routine lock a card once it has been filled in. A locked object is rendered as plain text: its text overflows the object like every other text object instead of being clipped into a box nobody can scroll, it keeps its background and border so the card does not change looks, and clicks and drags on it flip and move the card again. A locked box that was never written on is not outlined at all.

While a box can be written on, its text is clipped into the box and scrolls, the way a text field does. Only the active face is interactive, and clicking the card next to a writable box still flips and drags it as before β€” but a card cannot be dragged or flipped by a write box, so leave a strip of card free for players to grab. Card previews (the Deck Editor, the card choosers of INPUT, ...) render the text as plain text, since they are not part of the room state.

Editing decks and cards

Some of the information below is duplicated from the Edit Widgets discussion about the Properties section of the Menu.

Deck Editor

You can start a new deck in the Deck Editor. The "Add New Deck" dialog groups the ways to get a deck into three sections, only one open at a time: New blank deck (an empty deck you design here in the deck editor, open when the dialog opens), Use an existing deck (any of the decks in the public library, a ready-made traditional deck β€” standard playing cards, Spanish/German/Swiss suited cards, tarot, hanafuda, mahjong tiles or dominoes β€” or an import from Tabletop Simulator) and Create a custom deck, which builds the cards for you from suits and ranks, from one picture per card (one image file per card, with one shared back for the whole deck), from one picture with all the cards on it (a single sheet cut into one card per grid cell, with either one shared back or a second sheet of backs cut by the same grid), from a front and a back image per card (upload all fronts and all backs; they are sorted by file name and paired up by position, so each card gets its own back) or from text cards (one card per line of typed text, or per paragraph when a card's text needs line breaks, with card/text color, font size, card size, copies per card and an optional deck label shown on the backs). The dialog also has two "Add along with the deck" checkboxes that apply to all of those modes, both on by default: Holder for the deck, with the cards in it and Reset button that recalls, flips to back and shuffles (the reset button recalls the cards into the holder, so it is only available together with one). Untick the holder and you get just the deck widget and a pile with all its cards on the table. Once a deck exists, the fastest and most approachable way to build it out is the Deck Editor, a fullscreen visual tool opened with the "Deck editor" toggle button on the room toolbar (or via "Open deck editor" buttons in the properties module of a selected deck or card). It shows a big preview of the selected card face on the left, a sidebar on the right split into four tabs (All Cards, Card Type, Face, Object) so only one property scope is on screen at a time, and a bottom strip listing every card type with buttons to add or copy one.

The whole point of the Deck Editor is that you never have to touch faceTemplates/cardTypes JSON or learn its quirks (dynamicProperties, face indices, object ordering, etc.) to build a deck: you add text/image/icon/solid-color objects with toolbar buttons, drag and resize them directly on the card preview, and choose whether a property is "the same on every card" (shown in blue) or "different per card type" (shown in amber, which automatically wires up a dynamicProperties binding for you). Renaming and copying card types, adding/removing card counts, and switching faces or decks are all just buttons and dropdowns.

You can work on several face objects at once: Ctrl/Cmd+click an object (on the card or in the tree on the left) adds it to or removes it from the selection, Shift+click selects the range up to the clicked object, and Ctrl+A selects every object of the shown face. With more than one object selected, the Object tab edits all of them together β€” a property they disagree about shows an empty (mixed) field until you type a new value β€” and dragging, resizing, rotating, copying and deleting apply to the whole group. The Object tab's toolbar also has align (left/center/right, top/middle/bottom) and distribute (equalize horizontal/vertical spacing) buttons; aligning needs two selected objects, distributing needs three. Dynamic property bindings remain a single-object affair, since they name a per-object card-type property. The property rows of the Object and All Cards tabs are sorted into collapsible Content, Position, Size, Colors, Appearance blocks (plus Custom for a game's own properties), like the Edit Widget sidebar; the Card Type tab stays a flat list.

The Deck Editor is opened with the "Deck editor" toggle button on the room toolbar or the Open deck editor button of a selected deck or card, and closed again with that same toggle button, the Close button next to Card view in the upper right, or Escape. Edits are applied live with full undo/redo (including its own in-editor history), and everything is still just writing to the same deck JSON described above, so it stays fully compatible with direct JSON editing if you ever want to drop down a level.

See Deck Editor for an overview of the editor β€” how to add a deck, what each area does and what the buttons, tabs and shortcuts are β€” and Deck Editor Step By Step Example for a full walkthrough that builds a 16-card deck from scratch using only the Deck Editor.

Once a deck is created, decks and cards can be edited in a few different ways.

  1. The Deck Editor described above, for anyone who wants to build or tweak a deck visually without needing to understand the underlying JSON.

  2. The most powerful way of editing decks and cards is by editing the JSON directly. While in the JSON Editor, on the Menu to the right, click on "{} JSON". This method allows you to edit the JSON directly, which has the advantage that you can zero in immediately on what to change, and gives you the ability to add and create properties whereas with the other method, you can only change existing properties. Note, if you delete the cardType entry while a widget with that cardType still exists, the widget does not get automatically deleted. It remains in the room as a card with a transparent front until you refresh. You can adjust the number of cards actually available in the room by placing the cursor on the cardType for that specific card and looking for the blue buttons on the right to "add card n" or "remove card n" (where n will be the new number of cards of that type). See JSON Editor Step By Step Example (deprecated) for a walkthrough of this older approach β€” the Deck Editor above is now the recommended way to build a deck from scratch.

⚠️ **GitHub.com Fallback** ⚠️