Tabletop Simulator

Tabletop Simulator

Not enough ratings
How to add a Decal Overlay Image to Custom Cards or Tiles
By anniemuse
This is a method to allow users to customize their cards, but for the Tabletop Simulator (TTS) game dev to add a user interface for the card on top of whatever face graphic the user may choose. However, another method is still required to view card decals once they are changed from a card object to a deck object (ie: cards are stacked).

The workaround I'm using for games with stacking cards is to use tile components. This technically works.. but as they are different components, they act differently which is good in some ways and bad in others. Every tile is equal to about 8 stacked playing cards. That said, with a deck of cards over 200 the deck height is fixed, and in comparison 22 stacked tiles are the same height as the tallest deck in TTS. Tiles also do not have hidden content based on which side of the tile is visible, and if states are changed in a player hand zone the object will not be recognized as the component that should be hidden (as it will have a different GUID / unique identifier).
   
Award
Favorite
Favorited
Unfavorite
HS DxD Chess Wars Deck Builder
The image below shows a preview of what you would find if you load the mod I made using the card overlay (decal) technique. Sample decks can be found in the center drawer on the South Side of the table. I've shared decals for High School DxD: Playing Cards, Pinochle, and Uno.



You can use these overlays and swap the source materials to any direct image link. I recommend using mobage card graphics hosted on MG-Renders (MGR)[hsdxd.mg-renders.net]; the settings will likely need to be adjusted for any other card graphics. The Chess Wars game I invented uses the DxD Chess Classes, thus no decals are required. It's important to note the cards with decals cannot be stacked into a deck without the decals vanishing. I've been given hope by the community there is a LUA workaround, but I haven't found a fix for this issue yet. If / when I do I will be sure to update this guide. Please leave a comment if you have any suggestions that might remedy this issue! Thanks :)
Sources
I learned how to add formulaic decals on card objects for my High School DxD Deck Builder Mod (Coming Soon). This Mod allows users to choose Mobage cards from MGR[hsdxd.mg-renders.net] to build their own custom playing card decks. I've added various card user interface (UI) graphics with an add decal script on top of each card "face" so they can be customized as desired and used for various card games.

Thousands of card graphic direct links are identified on google sheets[docs.google.com] to assist with the creation of these cards. This resource also includes assets used in my mod such as: sample deck worksheets, decal (overlay / sticker ) graphics, and card box graphics.

I'd like to thank u/Luxaky_ who's reddit post lead me to a massive MGR Mobage Card Collection. I'm thankful they shared a google drive folder full of images of the HS DXD Mobage Card graphics from MGR[hsdxd.mg-renders.net] This resource was a powerful starting point used to create my HS DxD Mobage Card Database in the first place. Ironically, I still need the direct links from MGR!

I haven't decided the best way to share My Mobage Card database with you yet. However, there will be a full collection available on Youtube in the form of a series of slideshows. Each video has a theme and showcases cards by umbrella category. You can view the playlist and watch my ecchi game content on the KikiNiNyaa youtube channel:
https://www.youtube.com/watch?v=lCKGyRpMsI0&list=PLtzs8h6-zMCMRYQJGihjwxVXlt7ND48hk

Update: Add Decals with Right Click Context Menu
With the help of chatGPT I was able to create the following script to add decals with the right click context menu. For the best UX I omitted the command
self.clearDecals
as it was creating an error cannot find field 'clearDecals' when the selected decal is stamped twice in a row, it does work on the 3rd attempt tho. Luckily for us, we can use the decal tool to remove them manually (F9).

If you want to add decals to cards, try the context menu method. This way you don't need to manually comment the code in and out to update your decals on load. Instead, you can just right click and choose the decal label from the menu. (note the URL ID in this example uses links from imgur, so if you host your images somewhere else you'll need to change the sections that reference the link, ie: url = MYURL.com/

url = "https://i.imgur.com/" .. url_ID_1 .. ".png",

The object script for each card would look something like this (decal option for 3 image overlays):

-- Define decal variables -- Script needs a defined variable for name1, name2, name3, url_ID_1, url_ID_2, and url_ID_3 -- Example: Ace of Spades --[[ Windowed Overlay (adds window of suit to card name1 = "NAME1" -- Active Decal Name url_ID_1 = "IMGUR1" -- Active (IMGUR) URL ID -- end definition example ]] name1 = "spadeBlkSym" url_ID_1 = "8QSRxre" --[[ Normal Overlay (adds suit to card name2 = "NAME2" -- Active Decal Name url_ID_2 = "IMGUR2" -- Active (IMGUR) URL ID -- end definition example ]] name2 = "spadeBlk" url_ID_2 = "Ksp6nsZ" --[[ Text Overlay (adds number / text to card name3 = "NAME3" -- Active Decal Name url_ID_3 = "IMGUR3" -- Active (IMGUR) URL ID -- end definition example ]] name3 = "whtAce" url_ID_3 = "BuBjTAN" --[[ Begin script using defined variable from above --]] -- Define the decals decals = { [name1] = { name = name1, url = "https://i.imgur.com/" .. url_ID_1 .. ".png", position = Vector(0, 0.25, 0), rotation = Vector(90, 0, 0), scale = Vector(-2.11, -3.03, 1) }, [name2] = { name = name2, url = "https://i.imgur.com/" .. url_ID_2 .. ".png", position = Vector(0, 0.26, 0), rotation = Vector(90, 0, 0), scale = Vector(-2.11, -3.03, 1) }, [name3] = { name = name3, url = "https://i.imgur.com/" .. url_ID_3 .. ".png", position = Vector(0, 0.27, 0), rotation = Vector(90, 0, 0), scale = Vector(-2.11, -3.03, 1) } } -- Initialize the decal states function onLoad(save_state) self.setVar(name1 .. "_active", false) self.setVar(name2 .. "_active", false) self.setVar(name3 .. "_active", false) self.addContextMenuItem("Add Decal " .. name1, function() stampDecal(name1) end) self.addContextMenuItem("Add Decal " .. name2, function() stampDecal(name2) end) self.addContextMenuItem("Add Decal " .. name3, function() stampDecal(name3) end) end -- Stamp selected decal function stampDecal(decalName) local active = not self.getVar(decalName .. "_active") self.setVar(decalName .. "_active", active) print("Added Decal " .. decalName .. ": " .. tostring(active)) updateDecal(decalName) end -- Update specific decal based on its state function updateDecal(decalName) if self.getVar(decalName .. "_active") then local success = self.addDecal(decals[decalName]) if not success then print("Error: Failed to load " .. decalName .. " decal.") else print("Successfully loaded " .. decalName .. " decal.") end else -- Remove the specific decal by clearing all and re-adding the others local allDecals = self.getDecals() --self.clearDecals() -- tts doesn't understand this command for _, decal in ipairs(allDecals) do if decal.name ~= decalName then self.addDecal(decal) end end end end
Customizing Cards
The average user will probably not want to create their own decal. So first lets outline how to use the provided resources to update cards in my mod with the graphic you'd like to use.

In the example image below, I've arranged my sample Uno deck on the table to display two types of decals. The top row uses a decal that only covers the original card attributes, and the second row uses a decal that also includes a cutout shape. Since Uno doesn't use playing card suits it's not the best example but you can see the shapes on the left are specially made for Uno.



I tend to include both versions in a single card, and group them using the Create States option from the right click menu. I would recommend removing states in the beginning, to get a feel for the process without over complicating it. To do this you can choose Reset from the right click menu and the card that is visible will stay in the game and the second state will be deleted.



Here I have separated the cards so there aren't any states, but there are two cards side by side that display both of my Uno Skip decal variants. In order to swap the source image, right click a card and choose Custom from the menu.

This is where you can change the FILE_ID at the end of the current direct link to update the card graphic to any other card from MGR[hsdxd.mg-renders.net].



The reason I recommended removing additional states is because it's easy to swap one graphic and forget about the one that isn't visible. As both cards should be referencing the same source face card graphic, or using an x-ray image from the same release group. Once both card graphics have been updated you can recreate the states. To do this, highlight them both and right click the one you want to be the first state and then choose Create States from the menu.


By default Page Up and Page Down can change the visible state, but only goes in one direction.

To activate looping the states with a single button press:
Esc > Configuration > Interface tab > Misc. Settings... >
(towards the bottom of the list) Toggle on Component States Wrap
You might also like to set a custom hotkey:
Esc > Configuration > Controls tab > Previous State and Next State
I like to use the carrot keys: ,< .>



If you wanted to modify cards with multiple states you need to cycle through the states and update each custom card graphic. Alternatively, you can duplicate the multi state cards and reveal both templates while you update them. Then when you create states again, only the visible cards are combined into a new component with multiple states. Thereby removing any game objects that were hidden at the time of creation.

Regardless, of the method remember to use the same card / card variant for both states. In my example, the overlay image will swap from the windowed version to the full version when cycling through the card states.

Pro Tip: sometimes when you paste a link into the box it will look like an empty field. After which, it's easy to paste the link in the face card graphic field twice :( but, when you submit the update request the link won't be valid. Therefore, I recommend pressing the "Import" button regardless of whether you think the link was successfully replaced or not :)
Scripted Decals
Some users may want to customize the card UI, and this can be done for each card via the script editor. Please note, the grouped cards with alternate states will both need to be updated and have different GUID's.

The script is written so on load the function will happen.
Thus, once you are done modifying the script you need to make a new save or overwrite the current save, and then load it to see the changes. EDIT: How to reload the asset without doing the save and load game dance. Swap states and then return to the GUID that was modified. You could also drop the card into a box and pull it out, which is another way to trigger loading the card asset which will apply the script.

**DO NOT CLICK "SAVE & PLAY" in the script workspace or you will lose all your progress since your last save.**








Since one of the decals I'm using is a transparent PNG image that isn't opaque it's easy to see when a decal was stamped more than once. This is because the dark part of the image will be darker every time you run the script / load the asset (until it's at 100% opacity). If you notice too many decals on a card template you can use the decal tool to remove it (the red stamp icon in the toolbar or F9 then press F1 to go back to normal editing mode).


To customize the decals or interact with the card custom script, right click a given card and select, "Scripting" > "Script Editor" from the menu.







If you want to add you own custom overlay, or print a different overlay, first use the decal tool to remove the existing decal.




The script is written for each card template, and square brackets have been added around the block of code. This means the script is commented out, so to apply the script you need to remove the square brackets.



When updating the script a few changes are made to it:
  • Line 1 defines the type of card, "Joker" was changed to "Skip (green)" as a comment.
  • Line 5 no longer has the square brackets, but -- was left in place to retain the usage comments.
  • Line 9 must be unique, so I use my spreadsheet to keep track of the decal names.
  • Line 10 was updated from the overlay tab in my spreadsheet to reference the desired decal image that will be printed over the card face.
    • (imgur has beautiful links that keep the code looking nice, but any direct image link can be used)
    • Note: the decal dimensions I used was 640x800 px, curated for the hsdxd mobage card aspect ratio.
    • EDIT: you do not have to remove the closing square brackets, as they are commented out and will be ignored. Just add / remove the square brackets in line 5.



As previously stated, you need to make a save and load that save to apply changes to the modified script(s) use a method to reload the card / GUID that was modified.





Upon load the script editor will appear empty.



To get back to the modified script right click the card and select, "Scripting" > "Script Editor" from the menu.



Now that the decal has been added you'll need to replace the square brackets on line 5 to comment out the block of code.



Repeat the save and load step, now the script is commented out and will not print another decal. EDIT: Be cautious not to click "Save & Play" in the script workspace. Instead, use the state changing method or draw the card from a box to omit the possibility of losing all your work.
This step is important when you make any changes to the game (ie: rearranging cards, as it will load the last save state when you run the script).



Add Decal Script
Each card custom has a script that reads something like this:


-- Ace of Spades -- card = getObjectFromGUID("") -- state 2 (normal overlay) --[[ remove square brackets to print another card overlay (decal) function onLoad(save_state) self.addDecal ({ name = "1 spade1", url = "https://i.imgur.com/Uwbde6W.png", position = Vector(0, 0.25, 0), rotation = Vector(90, 0, 0), scale = Vector(-2.11, -3.03, 1) }) end -- remove square brackets to print another card overlay (decal) ]]

Below I've broken down each line of the code:
  • The double hyphens indicate a comment
    • --
  • The double hyphens used with square brackets indicate a block comment
    • --[[ <block of code> ]]
  • The first comment I made indicates what type of card the overlay is for.
  • The next comment is a reference for the condition to use a card GUID globally, but since it's in the card custom script we use "self" instead of the GUID.
    • If you needed the GUID it can be found by right clicking > "Scripting" > "GUID" which copies the object id to your clipboard, and it is also displayed at the top of the script editor for each item.
  • The following comment indicates which type of overlay I've attached to the card template, and which state I intended it to be.
  • The next & last line is where I commented out the addDecal code snippet, and to use the decal print function the leading square brackets need to be removed. Additionally, they need to be added back once the new decal is printed or it will place another decal on top of the previous one every time the custom card asset is loaded.
  • Then I called out the function that will occur when the last save state is loaded.
  • The next line would be the second commented out line if use globally, but in this case we want the object to reference it "self" and then add a decal with the following parameters.
  • Then I called out the nameof the decal we are going to print
  • Then I called out the direct link to the image we want to overlay on the card.
  • Then I called out the position in relation to the object. I set this value to (0, 0.25, 0) which prints the image on top of the card face.
  • Then I called out the rotation. I set this value to (90, 0, 0) which rotated it 90 degrees and resulted in overlaying the image over the card. This worked but the image appeared upside down, so I modified the scale to correct that issue.
  • Then I called out the scale, which I had to play with to the get values that worked well for the mobage cards and ended up with: (-2.11, -3.03, 1), fixing the image orientation. These values are different for tile assets, or cards that have been converted into bags.
  • Since I was able to use this script for each card, it allowed me to print / place a given decal in the same position across all the card templates.

The next step is to learn how to get the decal variable from the GUID and print the image that was on the last card to a deck item. I plan to update this guide when I find a solution to this issue. Please let me know if you find the solution I've been needing, Thanks!

Add Decal to Tiles or Card Like Bags
The best workaround I was able to find that fixes the stacking cards issue is to use tiles, and uncheck "stackable" when editing the custom item. The script is fairly similar, but the scale is slightly different.

scale = Vector(-1.6, -2, 1)

Another game asset I've experimented with uses the "Make it a Bag" tool, which sets all the scale values to 1 or -1.
scale = Vector(-1, -1, 1)

While the tile workaround solves the card stacking issue, it has other flaws. The minimum tile height is 1.00, thus the tiles are quite bulky. They also can be moused over when on the table (outside of a players hand) and it will show the name and description even if the tile is placed face down. I was able to test an Uno game and it's a playable game that allows card stacking while keeping the card sticker / decal.



I'm using a bag item / card box to shuffle the tiles, and placing the tiles face down when they enter the container. You can also use the deck feature to draw tiles from the bag into a players hand, or to deal to all players. I also have two states on the tiles, where my second state contains the card details and the cutout window (first state) only indicates there is a second state on mouse / pointer hover. You can mouse over tiles or cards and use Alt + Shift to peak at the other side, TTS will warn the other players you peaked by showing an eye icon over the object.


2 Comments
anniemuse  [author] Jul 5 @ 9:22am 
@ chif-ii I added a section that explains how to add a UI for easily applying decals in game. Please view the "Update: Add Decals with Right Click Context Menu"
note, that my example uses IMGUR links. So you will need to adjust url to fit your needs.
chif-ii Mar 10 @ 4:27pm 
I'm trying to do something with stickers in Magic the Gathering, where in the physical game you can peel off stickers and put them on your cards. Is there a way to add decals to a player's list of available decals?