Page 2 of 2 FirstFirst 12
Results 26 to 31 of 31

Thread: Scripting Examples

  1. #26
    Quote Originally Posted by onelivesleft View Post
    Your shuffle mutates the input table *and* returns a copy of it; it should really only do one or the other.
    I have replaced it with another one form stackExchange... is this correct, if not can you post some working code?
    My Boardgame uTube chan - Tragic's Table Top
    BGG Guild of BoardGame uTubers - Tube Tables

  2. #27

    Turn Button

    I finally gave up on the integrated Turns system. Too hard to use unless you don't really need it all.

    This code goes in the script of a handy custom tile named "Turn Button". (or whatever else you have around to which you want to attach it, but fix the onLoad code in the global script accordingly...)
    If you want to use a fixed GUID, you can just skip the search, and set turnButton.obj to getObjectFromGUID(yourGUID).
    I spent many hours trying to find a way to make the button insert its GUID into the Global script. I was unsuccessful.

    Then you take the code in the initial comment and paste it into your global script, adjust sizes and shapes to your heart's content.
    If you just want either the big global button or the one in the button itself, just comment out the one you don't want...

    I made a test bed:

        -- turn button
        -- Insert this block of code into your global script:
        turnButton = {
            obj = nil, -- filled in in the onLoad script.  Could just use getObjectFromGUID("constant gid") instead.
            init = function()"initTurns") end, -- initialize the turn system
            start = function()"startTurns") end, -- sets order to getSeatedPlayers and chooses a random first player
            curPlayer = function() return"getCurPlayer") end, -- returns the current player
            setOrder = function(list) turnButton.obj.setTable(order, list) end, -- change the order.  This is an array of player colors.  There are no seating requirements.
            order = function() return turnButton.obj.getTable("order") end, -- returns the current order
            passTurn = function(color)"passTurn", {color = color}) end, -- ends the current player's turn as if the end turn button was pressed.  Useful for AIs
            setTurn = function(color)"setTurn", {color = color}) end, -- sets the current player who must be in the order array.
            previous = function() return"prev") end, -- returns the previous entry in order.
            next = function() return"next") end, -- returns the next entry in order.
            reset = function(stat)"onLoad", stat) end, -- clears all turns status
            toggleVerbose = function()"toggleVerbose") end, -- changes whether the button reports on turn changes.
        function onLoad()
            for _, obj in ipairs(getAllObjects()) do -- find the Turn Button.  
                if obj.getName() == "Turn Button" then
                    turnButton.obj = obj
            if not turnButton.obj then
                print("turnButton not initialized.")
                print("TurnButton " .. turnButton.obj.getGUID() .. " Initialized")
    order = {}
    curIndex = 0 -- control variable, indexed into order for curent player; 0 means turns disabled
    verbose = true -- set false to turn off information messages
    buttonXmlTable = {
        tag = "Button",
        attributes = { id="turnButton",
            width="250", height="120", position="0 0 -11", colors="#cccccc|#dddddd|#444444|#00000000",
            text="Start the Game", fontSize="40", fontStyle="Bold", textColor="Black", onClick = "initTurns",
    globalXmlTable = {
        tag = "Button",
        attributes = { id = "turnButton", position = "-500 500 0", width = "300", height = "50",
            text = "Start the Game", fontSize = "40", textColor = "Black",
            colors="#cccccc|#dddddd|#444444|#00000000", onClick = self.getGUID() .. "initTurns",
    function startTurns(player)
        if toggleVerbose then
            print("Game started by ", player.steam_name)
        order = getSeatedPlayers()
        curIndex = math.random(#order)
        local curPlayer = order[curIndex]
        sayTo(curPlayer, curPlayer .. ", it is your turn.")
        showTurns({color = curPlayer, text = "End " .. curPlayer .. "'s turn"})
    function initTurns(player) -- set up the buttons with "start the game".
        showTurns({color = "Black", text = "Start the game"})
    function showTurns(params)
        -- print("showTurns: ", params.color, " ", params.text)
        local baseUI = Global.UI.getXmlTable()
        local ui = {}
        for i, item in ipairs(baseUI) do
            if ~= "turnButton" then
                table.insert(ui, item)
        buttonXmlTable.attributes.textColor = params.color
        buttonXmlTable.attributes.text = params.text
        globalXmlTable.attributes.textColor = params.color
        globalXmlTable.attributes.text = params.text
        if params.color == "Black" then
            globalXmlTable.attributes.onClick = self.getGUID() .. "/startTurns"
            buttonXmlTable.attributes.onClick = "startTurns"
            globalXmlTable.attributes.onClick = self.getGUID() .. "/endTurn"
            buttonXmlTable.attributes.onClick = "endTurn"
        table.insert(ui, globalXmlTable)
        -- print(Global.UI.getXml())
        ui = {}
        baseUI = self.UI.getXmlTable()
        for i, item in ipairs(baseUI) do
            if ~= "turnButton" then
                table.insert(ui, item)
        table.insert(ui, buttonXmlTable)
        -- print(self.UI.getXml())
    function onLoad(JSONTurnState)
        if JSONTurnState and JSONTurnState ~= "" then
            local turnState = JSON.decode(JSONTurnState)
            -- print(JSONTurnState)
            curIndex = turnState.curIndex
            if curIndex ~= 0 then
                order = turnState.order
                verbose = turnState.verbose or true
                print("The game continues.")
                showTurns({color = order[curIndex], text = "End " .. order[curIndex] .. "'s turn"})
                print("No game")
    function onSave()
        turnState = { curIndex = curIndex, order = order, verbose = verbose }
        return JSON.encode(turnState)
    function sayTo(color, message)
        broadcastToAll(message, stringColorToRGB(color))
    function getCurPlayer()
        return order[curIndex]
    function next()
        local ix = curIndex + 1
        if ix > #order then
            ix = 1
        return order[ix]
    function prev()
        local ix = curIndex - 1
        if ix < 1 then
            ix = #order
        return order[ix]
    end -- of prev
    function passTurn(params)
    function setTurn(params)
        local curPlayer = params.color
        for i, color in ipairs(order) do
            if color == curPlayer then
                sayTo(curPlayer, Player[curPlayer].steam_name .. ", it is your turn.")
                curIndex = i
        print(curPlayer, " is not in the player list (order)")
    function toggleVerbose()
        verbose = not verbose
        return verbose
    function endTurn(player)
        local curPlayer = order[curIndex]
        if player.color ~= curPlayer then
            sayTo(player.color, "cannot end another player's turn (" .. (curPlayer or "nil") .. ")")
        if verbose then
            print("End of ", curPlayer, "'s turn")
        if Global.getVar("onTurnEnd") then
        curIndex = curIndex + 1
        if curIndex > #order then
            curIndex = 1
        curPlayer = order[curIndex]
        if verbose then
            print("Start of " .. curPlayer .. "'s turn.")
        showTurns({color = curPlayer, text = "End " .. curPlayer .. "'s turn"})
        if Global.getVar("onTurnStart") then

  3. #28
    While this seems to work ok, I have run into severe problems with data consistency between different scripts.
    I am making a variation that has all functions in the global script. This seems to be working better. Will post when testing done.

  4. #29
    Been a while... here is a new script!

    Custom Name and Description based on rotation of object
    (Original Idea by Mark)

    The custom rotation value system allows you to set your own NAME tooltip very easily. Simply leave your default name space blank and then name your rotations. Now everytime you flip it, it will have the new name. Unfortunately this does not work at the moment for Descriptions as well.

    This is a little hack code that allows this behaviour but it requires the script be placed on all objects. I have no idea how this will affect performance if you have a billion objects with this script.

    **Leave All Rotation name Blank and leave Name and Description of object Blank.**

    The simply copy this code onto your object
    data = {}
        data[1]={n = 'City Garrison', d = ''}
        data[2]={n = 'Ice Catapult', d = 'Attack : 9 (Ice / Cumbersome)\nDefence : 6 (Fortified)\nFame : 7\n\n(Lost Legion)'}
        --data[3]={n='', d=''}
        --data[4]={n='', d=''}
    function onCollisionEnter()
        local face = self.getValue()
    How does it work?

    • What this does is everytime a object collides it will test the Rotation Value. This works on drop and flip and w/e.
    • You set these with the rotations via the "value gizmo"
    • Set your rotations but do not name them. This way they will return the numeric value for the rotation.
    • In the script there is a data variable at the top for easy editing. In my example I have set it up for 2 values (a coin) but it should work for any amount like say a 12 sided die. Just follow the template. Use LUA string stuff to add line breaks like in my example.
    • On collision it finds the current rotation's numeric value and then uses that as a key to find the correct data
    • Then it just assigns that data to the name and description
    Last edited by Tragic; 07-09-2019 at 03:18 AM.
    My Boardgame uTube chan - Tragic's Table Top
    BGG Guild of BoardGame uTubers - Tube Tables

  5. #30
    HandPosition Error (Work Around)

    As any TTS modder knows there is a annoying bug that is there but kind of trivial so it gets passed over for more important things in the dev cycle. I'm talking about, of course, the hand zones kind of "dropping" downward. So if you put a card in the handzone it falls under the default tables. I have no idea why this happens, but it does ALL the time. It is super anoying, as you constantly have to adjust the Y postion of the hands.

    No longer!

    Here is a small hack that can fix this up for you. Currently this particular code dose not handle multible hand zone, but that is just as I did not need this functionality when I wrote the hack. It would be easy to adjust it for multipule hand zones per colour, and even hand zones of diffrent hights.

    The Code!
    function onLoad()
    function FixHandZones()
        local Y = 3.91
        local playerList = Player.getAvailableColors()
        for _, colour in ipairs(playerList) do
            local pramsOLD = Player[colour].getHandTransform()
            pramsNew = {position={pramsOLD.position.x, Y, pramsOLD.position.z}
                       ,scale={15.50, 5.40, 0.82}
            Player[colour].setHandTransform(pramsNew, 1)
    How does this work

    Place this code inside the GOLBAL script of your mod and call in in your onLoad(), you can place it in any onLoad really, I actualy like using global control objects to store variables and functions nowadays. Anyway, by having it in GLOBAL onLoad() the function is called every time the mod is loaded. What it does is simply look through the avaliable player hands and adjust the Y height.

    All you need to do is paste this into your code and then changed the 'Y' variable to your desiered height.

    You can change the numeric value in setHandTransform() to some kind of variable and use if statments and stuff to make this code more complex so it can handle multipul hand zones and heights.

    Hope this helps!!
    My Boardgame uTube chan - Tragic's Table Top
    BGG Guild of BoardGame uTubers - Tube Tables

  6. #31
    SetUp Scripts

    Much of the current scripting in TTS mods is to speed up setup.. placing components and all that. This is particularly true for large expansion games. Most of my modding is around setup scripts and I thought I would share some of my code snips!

    Have Fun!

    Dynamic Setup Bags

    One of this issues with SetupScripts if coding all the GUIDs for all your components and bags and stuff. What a pain. Also if you change anything and a new GUID is loaded, it all breaks. I get around this by using a SetUPZone.. I usually put it under the table away form the sight of the players. What you do is then put anything you want in that scriptzone. Bags, Individual Tokens.. what'evs. I like to use bags or infinite bags. Then all you need to do is name them all uniquely.

    function onLoad()
    function SetGlobalVariables()
        SetUpScriptZone = getObjectFromGUID('9e3779').getObjects()
    function returnSetUpObj(d)
        for _, o in pairs(SetUpScriptZone) do
            if o.getName() == d then
                return o
    Here is an example of how I use this code... basically what I like to do is have a series of function in OnLoad inside GLOBAL. In it I do things like my FixHandZones() or MegaFreeze() functions.. but also I set any global variables I want to use as well. Global runs OnLoad after everything loads in.. so it is the perfect place to put sutff as you are sure to have it called after all objects are done loading.

    The actual meat of this code is the function returnSetUpObj(d). This is what we will be using. Here is an actual code snip form my new Arkham Horror mod I am working on..

    function onLoad(saved_data)
    function setSources()
        CommonBag ='returnSetUpObj', 'Gator Common Componants')
        NoInteractBag ='returnSetUpObj', 'Gator Non-Interactable Componants')
        StaminaBag ='returnSetUpObj', 'Stamina Markers')
        SanityBag ='returnSetUpObj', 'Sanity Markers')
        ClueBag ='returnSetUpObj', 'Clue Markers')
        MoneyBag ='returnSetUpObj', 'Money Markers')
        SkillsBag ='returnSetUpObj', 'Skill Markers')
        DieRollerBagLeft ='returnSetUpObj', 'Die Rollers (Left)')
        DieRollerBagRight ='returnSetUpObj', 'Die Rollers (Right)')
    This is a function I put in called setSources(), again I like to pull it out into a function.. it is placed in Onload... what this is doing is pretty simple. I am making a l variable inside the script, than calling the function returnSetUpObj(d) and sending it the name of the bag I want to use.

    And that is it! All I need to do now is in my setUPScripts use those bags... CommonBag ..takeObject({}) or w/e

    So the point here is that this is dynamic. I can simply drop anything into that zone and it is accessible, I can change things easily. I can set up the zone in a easy to access place during creation of the mod and then just mass move it to hide it after. I find it a great way of handling this stuff.

    In addition you can add the same script zone to your Interactive Code (I call it Mega Freeze, I should probably update my MegaFreeze code in this thread) to make all the items non-interactive for release pretty easy.

        for _, o in pairs(SetUpScriptZone) do
                o.interactable = false
    Setup Positions

    This is what I get asked the most.. how do you get everything to be placed exactly in the correct spot.. well it is actually super easy when you think in world coordinates. TTS like most 3D systems has a 0,0,0 world space coordinate, and it is pretty trivial to use that to easily make everything spawn at the correct spot. I use this code ALL the time for player areas, where you want say a card to be drawn to a certain location, or tokens on setup to go into a certain place, but then that same board could be anywhere on the table.

    Basically you first want to center your object to {0,0,0}.. so for example, if I wanted to put my tokens onto a playerboard during setup, I would simply make a temporary file and put that board at {0,?,0}. Usually I would take note of the Y value in my actual mod, as this can change... what you are trying to do here is have your player board centered at {0,0,0}.

    Then all you need to do place all your components on the board and record their positions. To do this just palce them were you want, then use the move tool and copy the data it shows you... an example of how I do this would be...

    z = {}
    z['Money Dispenser'] = {p={-11.89, 1.15, 6.77}, r={0, 180, 0}, s={0.3, 0.3, 0.3}}
    I have simply made a table, and used the NAME of the object I want to move as the Key and then copied the position, scale and rotation vales into it.

    Now I have all the information I need. Because the PlayerBoard is at {0,0,0} these values are actually how you would modify the boards dynamic position... For example... to place this Money Dispenser correctly on the board, all I need to do is this...

    local pos = playerboard.getPosition()
    pos.x = pos.x + z['Money Dispenser'].p[1]
    pos.y = pos.y + z['Money Dispenser'].p[2]
    pos.z = pos.z + z['Money Dispenser'].p[3]
    Now.. pos is the exact world space needed to place the token in the exact spot you wanted. So the setPostion() now places it correctly.

    Some example Code

    function setPrams(data, guid)
        local p = {}
        p.position = {pBoard.getPosition().x + data.p[1],
                      pBoard.getPosition().z + data.p[3]}
        p.rotation = data.r
        p.scale = data.s
        if guid ~= nil then p.guid = guid end
        return p
    This is a example code I am actually using in my current mod. This is a small bit of code that takes in table "z['Money Dispenser'] = {p={-11.89, 1.15, 6.77}, r={0, 180, 0}, s={0.3, 0.3, 0.3}}", then uses it to create the corrected positional data. The code also allows you to send guids as well if you wish.

    You use it like this...

    ocal prams = setPrams(PositionData['Money Dispenser'])
    --OR Something like this
    (you need to either have your playerbaord as a variable accessible, or you need to pass it ot the setPrams thing)

    Setup Stuff - Cloned bags and Infinite bags

    What I like to do is use the CLONE tool during my setups. This allows me to easily just copy stuff, rather than have separate things. For example if I have possible 10 players, I do not need 10 different bags, one for each player in the setUP zone.. I can simply have a single bag called something like "SetUP_CommonStuff" that I can clone ten times and give to every player.

    Here is the actual code from my current mod that places tokens from a common bag like I just spoke of...

    (OldBook is my player board Object)
    function setup_CommonBag()
        local ClonedBag = CommonBag.clone()
        for _, obj in pairs(ClonedBag.getObjects()) do
            for k, v in pairs(PositionData) do
                if == k then
                    local prams = setPrams(PositionData[k], obj.guid)
                    local o = ClonedBag.takeObject(prams)
    function PosAbove(OldBook)
        p = {OldBook.getPosition().x, 8, OldBook.getPosition().z}
        return p
    • So I have a function I call with setup_CommonBag()
    • It clones the bag in the first line, the bag found and set by the "dynamic setup bag" code at the top of the page.
    • I then have a small bit of code that puts the cloned bag above the player board it is sending items to. This way when they "move" to the correct spot they sorta fly down form above the player board to the correct spot.
    • I then loop through the cloned bag's contents
    • I then also loop through my position data variables which is a big string of these things... "z['Money Dispenser'] = {p={-11.89, 1.15, 6.77}, r={0, 180, 0}, s={0.3, 0.3, 0.3}}"
    • Then any items in the bag that match the KEY of the position data, in this case "Money Dispenser" is found it dose stuff.
    • It sets the correct position prams as I spoke of above, including the GUID
    • It then takes the object from the ClonedBag using those prams
    • Once you have the object itself out of the bag, you can scale it as well, rename it, lock it, set it intractable = false.. whatever
    • Then right at the end it now destroys the cloned bag completely.
    • this means that even though we have taken items and placed them, the "source bag" left untouched and can be used again by another playerboard during setup.

    An infinate bag is even easier as you do not need to clone it at all..
    function spawnSanity(num)
        local prams = setPrams(PositionData['Sanity'])
        local c = SanityBag.takeObject(prams)
        c.interactable = false

    This is probably my longest post in this thread.. hope it may be helpful to some people!

    Have Fun and ROLL HIGH

    Example -
    My Boardgame uTube chan - Tragic's Table Top
    BGG Guild of BoardGame uTubers - Tube Tables

Page 2 of 2 FirstFirst 12

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts