Tabletop Simulator

Tabletop Simulator

90 ratings
TTS LUA Code Snippets
By reimu
Some code snippets that demonstrate various functions/calls for Tabletop Simulator. And then some.
2
   
Award
Favorite
Favorited
Unfavorite
Introduction
A lot of actual code that should be easy to copy, paste, and modify to fit your own scripting needs. I assume anyone reading this understands the basics of programming along with the most basic of Tabletop Simulator functions, mainly getObjectFromGUID(guid). I do not guarantee the code is free of error, or that it is the best code. In fact, I only learned LUA because of TTS.
Tables
Tables make up a large part of TTS scripting. There are several ways of handling them that I'll list in this section because it's important to understand how to do this properly.

Here is a table:
mytable = {}
You can imagine it like this:
key/index
value
There are no keys/indexes and no values. However by doing something like this
mytable.thisisakey = 'value'
or
mytable['thisisakey'] = 'value'
The table now looks like this
key/index
value
thisisakey
'value'
Tables can store whatever value along with whatever key. Keys are unique but values are not. Try to figure out what the table looks like after the following code.
mytable.thisisakey2 = 'value' mytable.thisisakey = 1 mytable.thisisalsoakey = 1
key/index
value
thisisakey
1
thisisakey2
'value'
thisisalsoakey
1
However, you can use indexes instead of keys. The following 2 snippets make the same table.
mytable = {'data', 'moredata'}
mytable = {} mytable[1] = 'data' mytable[2] = 'moredata'
key/index
value
1
'data'
2
'moredata'
There are several ways to get the information out of a table. If you have a table with key-value pairs, you can do it like this. The k is the key while the v is the value for that key in the table.
for k,v in pairs(mytable) do print(k,v) end
If you have a table with indexes instead of keys, you can go through it this way also. #mytable is the length of the table. This only works for tables with indexes.
for i=1,#mytable do print(mytable[i]) end
Also as a final note, you can have tables in tables
mytable = {} mytable.table = {} mytable.something = 'data' mytable.table.table = {} mytable.table.table.table = {'hello'}
This is how it looks
key/index
value
table
key/index
value
table
key/index
value
table
key/index
value
1
'hello'
something
'data'
Sometimes you'll see someone doing something like this
mytable = {['something'] = 'value', ['somethingelse'] = 'value2'}
That is the same as
mytable = {something = 'value', somethingelse = 'value2'}
The only difference is there's extra text to type and if you don't use the brackets, you cannot use numbers in your key
mytable = {['123key'] = 1} -- ok mytable = {123key = 1} -- not ok
Keywords
There are two important keywords, Global and self. Global refers to anything in the main script while self refers to the object that the script is located on.

-- on a deck print(self.name) Global.call('myFunction') -- in global script print(deck.name) myFunction()
Callbacks/Functions in Parameters
Sometimes you'll see a callback parameter on some functions. Callbacks are functions that will be called when the current function is completed. There are up to two other parameters that influence the callback function.
params.callback = 'theFunctionName' params.callback_owner = Global -- this is where function theFunctionName() is located params.params = {1,2,3}
You can imagine this as
-- some function that utilizes the callback parameter i.e. spawnObject or takeObject theFunctionName({1,2,3}) -- this will only run once spawnObject or takeObject has completed

Sometimes when you have a callback function or a function in the parameters table and you're wondering what gets passed to it automatically, just try sending print.
button.click_function = 'print'
Consider that as
print(parameter,parameter,parameter...)
And any print statement with commas in it just concatenates the strings (puts them together) so you would see in the chat log something like
Custom_Object (LuaGameScriptObject)White -- or something like that
Taking that apart, you will have two things:
1. The object
2. The color of who pressed the button
This means that there are two parameters passed to the function of a button, the object it's on and the color of the player who pressed it.
----API----
API[berserk-games.com]
Collisions
function onCollisionEnter(collision_info) print('The object that hit me\'s name is' .. collision_info.collision_object.getName()) for i=1,#collision_info.contact_points do print('Collision point:') for k,v in pairs(collision_info.contact_points[i]) do print(k .. '=' .. v) end end print('Relative velocity values') print('x=' .. collision_info.relative_velocity.x) print('y=' .. collision_info.relative_velocity.y) print('z=' .. collision_info.relative_velocity.z) end

function onCollisionExit(collision_info) print('The object that left me\'s name is' .. collision_info.collision_object.getName()) for i=1,#collision_info.contact_points do print('Collision point:') for k,v in pairs(collision_info.contact_points[i]) do print(k .. '=' .. v) end end print('Relative velocity values') print('x=' .. collision_info.relative_velocity.x) print('y=' .. collision_info.relative_velocity.y) print('z=' .. collision_info.relative_velocity.z) end

function onCollisionStay(collision_info) print('The object is hitting me\'s name is' .. collision_info.collision_object.getName()) for i=1,#collision_info.contact_points do print('Collision point:') for k,v in pairs(collision_info.contact_points[i]) do print(k .. '=' .. v) end end print('Relative velocity values') print('x=' .. collision_info.relative_velocity.x) print('y=' .. collision_info.relative_velocity.y) print('z=' .. collision_info.relative_velocity.z) end
Picked Up/Dropped
function onDropped(player_color) print(player_color .. ' dropped ' .. self.getName()) end

function onPickedUp(player_color) print(player_color .. ' dropped ' .. self.getName()) end

function onObjectPickedUp(player_color, object) print(player_color .. ' dropped ' .. object.getName()) end

function onObjectDropped(player_color, object) print(player_color .. ' dropped ' .. object.getName()) end
Save/Load
function onSave() return JSON.encode({savedSomething=true}) end function onLoad(save_state) print('Everythings loaded!') if JSON.decode(save_state).savedSomething then print('Something was saved from before') end end

Comments: onSave() must return a string. JSON.encode() turns tables into strings. JSON.decode() turns those strings back into tables.
Destroy
function onDestroy() print('I\'m dying!! (' .. self.getName() .. ')') end

function onObjectDestroyed(dying_object) print(dying_object.getName() .. 'is dying!!') end

destroyObject(obj)
Scripting Zones
function onObjectEnterScriptingZone(zone, enter_object) print(enter_object.getName() .. ' has entered zone ' .. zone.getGUID()) end

function onObjectLeaveScriptingZone(zone, exit_object) print(leave_object.getName() .. ' has left zone ' .. zone.getGUID()) end
Player Color Change
onPlayerChangedColor(player_color) print('Someone moved to ' .. player_color) end
Player Turns
onPlayerTurnEnd(player_color_end, player_color_next) print(player_color_end .. ' just ended his turn. Now it is ' .. player_color_next .. '\'s turn.') end

onPlayerTurnStart(player_color_start, player_color_previous) print(player_color_previous .. ' just ended his turn. Now it is ' .. player_color_start .. '\'s turn.') end
Notebook Tabs
local params = {} params.title = 'myNewNotebookTab' params.body = 'myNewNotebookTabContents\nHere's a new line' params.color = 'White' addNotebookTab(params) params.title = 'myNewNotebookTab2' addNotebookTab(params)

local params = {} params.index = 1 params.title = 'myNewTitle' params.body = 'myNewBody\nAnd Other' params.color = 'Red' editNotebookTab(params)

local tabs = getNotebookTabs() for i=1,#tabs do print(tabs[i].index) print(tabs[i].title) print(tabs[i].body) print(tabs[i].color) end

removeNotebookTab(1)
Copy, Paste, and Spawn
local objects = {} objects[1] = obj1 objects[2] = obj2 copy(objects) paste({position={0, 0, 0}, snap_to_grid=false})

local params = {} params.type = 'myObjectType' params.position = {0, 0, 0} params.rotation = {0, 0, 0} params.scale = {0, 0, 0} params.callback = 'myCallbackFunction' params.callback_owner = Global params.snap_to_grid = false params.params = {'myCallbackParams1', 'myCallbackParams2'} spawnObject(params)
Notes
local notesText = getNotes() print(notesText)

setNotes('These are the notes')
Printing
print('Only host can read this')

printToAll('Everyone can read this', {1, 1, 1})

printToColor('Only white can read this', 'White', {1, 1, 1})
Misc.
flipTable()

getAllObjects()

getObjectFromGUID('objguid')

local seated = getSeatedPlayers() for i=1,#seated do print(seated[i]) end

function update() print('frame gone by') end

clearPixelPaint() clearVectorPaint()

startLuaCoroutine(Global, 'myFunction')

local color_table = stringColorToRGB('White') print('White\'s R value is ' .. color_table.r) print('White\'s G value is ' .. color_table.g) print('White\'s B value is ' .. color_table.b)
----Objects----
Objects[berserk-games.com]
Clone
myclonedobject = obj.clone({position={0,0,0},snap_to_grid=false}) -- is the same as copy({obj}) myclonedobject = paste({position={0,0,0},snap_to_grid=false})[1]
Buttons
Method 1:
local button_parameters = {} button_parameters.click_function = 'myFunction' button_parameters.function_owner = Global button_parameters.label = 'myTextHere' button_parameters.position = {0, 0, 0} button_parameters.rotation = {0, 0, 0} button_parameters.width = 500 button_parameters.height = 500 button_parameters.font_size = 20 object.createButton(button_parameters) button_parameters.click_function = 'myOtherFunction' object2.createButton(button_parameters)

Method 2:
local button_parameters = {click_function = 'myFunction', function_owner = Global, label = 'myTextHere', position = {0, 0, 0}, rotation = {0, 0, 0}, width = 500, height = 500, font_size = 20} object.createButton(button_parameters) button_parameters.click_function = 'myOtherFunction' object2.createButton(button_parameters)

Method 3:
object.createButton({click_function = 'myFunction', function_owner = Global, label = 'myTextHere', position = {0, 0, 0}, rotation = {0, 0, 0}, width = 500, height = 500, font_size = 20}) object2.createButton({click_function = 'myOtherFunction', function_owner = Global, label = 'myTextHere', position = {0, 0, 0}, rotation = {0, 0, 0}, width = 500, height = 500, font_size = 20})

All of these snippets do the same thing, but in different ways. Choose what is best for your script.

function myFunction(object, player) print(player .. ' pressed the button on ' .. object.getName()) end
Taking out of Containers
myBag.takeObject({position={0,1,0}, rotation={0,0,180}, callback='myFunction', callback_owner = Global, params = {self,1}, flip = false, top = false}) function myFunction(paramstable) for k,v in pairs(paramstable) do print(k,v) end end
----Notable Functions/Classes----
Somewhat more indepth.
spawnObject( Table parameters )
At first glance, you might think spawnObject is not that impressive, however with it, you can make two impressive things, text tools, scripting zones, and even tables... like physical tables. Wait that's three things.

I'll just go over the general idea of working with spawnObject. First, make an object that you want to be able to spawn then print its name. Now you know the type and you can spawn it.

guid = 297853 -- pretend this is the guid of a scripting zone print(getObjectFromGUID(guid).name) -- this will print 'ScriptingTrigger' spawnObject({type='ScriptingTrigger'}) -- this will spawn a scripting zone

This works for tables also and text tools also (I believe the text tools name is 3DText), however you can't get the guid of a table, so instead you can use onCollisionEnter to get the table object.

function onCollisionEnter(info) print(info.collision_object.name) end

The best part about spawning in tables is you can scale them. I think.
--my huge table spawnObject({type='table_custom',scale={10,1,10}})

One other thing you can spawn is hidden zones. However, maybe you're wondering how I got the name of a hidden zone. An alternative way of finding the name field for an object is to go directly into the .JSON save files and look at the name fields. If you make a save file with only a hidden zone in it, you will notice the line
"Name": "FogOfWarTrigger",
What's that? That's the hidden zone.
spawnObject({type='FogOfWarTrigger'})
And now you have a hidden zone you can mess around with.
onDestroy()
While it is called onDestroy(), destroy is not as simple as you think it is. It will be called when you delete an object, but it will also be called when the object 'disappears' i.e. enters a deck or a bag. I'm not sure how to get the deck or bag though.

function onDestroy() print('I've disappeared into a container') end
Timer
Timer is a way to delay the calling of a function for a while.

Timer.create({identifier='uniqueGUID', function_name='myFunction', function_owner=Global, delay=2}) function myFunction() print('2 seconds have passed since the Timer was created') end
----Random Snippets----
Some random bits of code I use in my mods
Getting Player Count
#getSeatedPlayers()
String Manipulation
function split(input, split) local returnarr = {} local i = 1 for str in string.gmatch(input, '([^' .. split .. ']+)') do returnarr[i] = str i = i + 1 end return returnarr end options = split('option1=val1','=') print(options.option1) -- val 1

Couldn't find the default string split function for LUA so I made my own.
Color Manipulation
function onetotfs(rgbtableone) rgbtableone.r = dechex(rgbtableone.r * 255) rgbtableone.g = dechex(rgbtableone.g * 255) rgbtableone.b = dechex(rgbtableone.b * 255) return '[' .. rgbtableone.r .. rgbtableone.g .. rgbtableone.b .. ']' end function dechex(number) local base=16 local hexvals='0123456789ABCDEF' local returnval = '' local remainder while number>0 do number,remainder=math.floor(number/base),number%base+1 returnval=string.sub(hexvals,remainder,remainder)..returnval end return returnval end function niceColorName(color) return onetotfs(stringColorToRGB(color[1])) .. Player[color[1]].steam_name .. '[-]' end print(dechex(255)) -- ff print(niceColorName({'White'})) -- [ffffff]reimu[-] : note bb tags will just color the text

Changes numbers from base 10 to base 16. You can change to any base but this is specifically for changing the RGB tables that TTS uses (max value 1) to bbcode tags.
Searching Containers
function getIndexOf(col,table) for i=1,#table do if table[i] == col then return i end end return -1 end table = {1,2,3} print(getIndexOf(2,table)) -- 2

That is for searching a table/array

function searchForIn(object, containerguid, params2) params2.guid = '000000' local deckorbag local guids = {} if type(containerguid) == 'string' then deckorbag = getObjectFromGUID(containerguid) else deckorbag = containerguid end local obj = nil for k,v in pairs(deckorbag.getObjects()) do if string.find(string.lower(v.name==nil and v.nickname==nil and '' or v.name or v.nickname), string.lower(object)) then table.insert(guids, v.guid) end end if #guids > 0 then params2.guid = guids[math.random(#guids)] return deckorbag.takeObject(params2) else return false end end
Somewhat complicated function that will search a bag for all objects with matching names, then take out a random one:
Parameters:
String object - the name of the object you are searching for
String/Object containerguid - the guid of the container/deck you want to search or the actual container/deck object
Table params2 - the parameters when taking out the object
Returns - false if object not found in container/deck, reference to object if object found in container/deck
----Player Control----
Several functions that are not listed in the documentation on the official site that deal with players.
Kick, promote
These are things you can do with scripting

print(Player.white.promoted) Player.white.promote() Player.white.kick()
17 Comments
Finaryman 14 Dec, 2022 @ 11:17am 
i have something to include in ur button section

pos = global position of the button to be placed
object = object on which to place button at

pos=(pos-object.getPosition()):reflect(object.getTransformRight())+object.getPosition()
pos=object.positionToLocal(pos)

after that pos can be inserted into button creation and it will be at correct spot.
Reh3dDoes 31 Dec, 2019 @ 6:40am 
This is nice. TY for sharing. I wonder if there is a site that has a database of scripts with short gifs that show what it does (like for making UI button effects, or card/dice/tile values and effects, etc). Would be nice if it had a searchable database, too. Can wikis have gifs?
RendCycle 25 Feb, 2018 @ 9:54pm 
Your suggestion seems plausible but might be more difficult to do. What I have in mind is, cards are the objects that will rotate and snap. A player has the choice to arrange from 1 to 12 cards as a part of a single group and some cards may also partially overlap. There can possibly also have a max. set of 4 groups of cards per player. I just made one player area and plotted a 9 x 37 Rotate Snap points on it like a grid and on the second to fourth player's area, I learned I now have to rotate each point as it is facing the wrong way. :steamfacepalm: Here is a part of the Rotate Snap points I made: https://drive.google.com/file/d/1nGHJVHc_JwX_D88zW5AicNmOGEkfwHJF/view?usp=sharing

I just wish when a player changes color/seat position, the Rotate Snap point would default to rotate accordingly to where the player is facing. Anyhow, thanks for the reply and suggestion.
reimu  [author] 25 Feb, 2018 @ 8:06pm 
I haven't scripted TTS in a while (> 1 year) but as far as I know it's not possible with scripting because scripting can't deal with snap points directly. One possible idea I have is to have an object with snap points on it already, then to just make a script move the object into a spot so that the snap points are where you want them to be.

Another thing you could do to ease your life would be to figure out the structure of saved object (I believe they are just JSON) and then to find out how snap points are defined within that file. With these two things, you could use a script in any language you prefer to generate a file for an object with snap points on it.
RendCycle 25 Feb, 2018 @ 5:54pm 
Thanks for the guide! I have a question. Is it possible to make a script that auto creates and loads several Rotate Snap Points on four defined areas of the play table? I am planning to create 9 x 37 number of equally spaced and distributed Rotate Snap points for each of the four play table areas
Woa 29 Oct, 2016 @ 12:26am 
SOrry, I don't know how to do that. Try checking the official website.
olejika 29 Oct, 2016 @ 12:12am 
funtion that just add checkmark on label of pressed button
Woa 28 Oct, 2016 @ 11:52pm 
Do you want the same function for each button or a different function for each button?
olejika 28 Oct, 2016 @ 11:49pm 
Can you give me examples how to do that?
Woa 28 Oct, 2016 @ 11:43pm 
no, I think they can all have the same function, but it will be shared between them if they are on the same object.