Skip to content

Instantly share code, notes, and snippets.

@jbaiter
Created April 12, 2020 09:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbaiter/50b7cf9a1261692d4992205365ca03ba to your computer and use it in GitHub Desktop.
Save jbaiter/50b7cf9a1261692d4992205365ca03ba to your computer and use it in GitHub Desktop.
TableTop Simulator: Scripted Azul
--[[ Lua code. See documentation: https://api.tabletopsimulator.com/ --]]
#include <vscode/console>
tileDiffuseUrls = {}
tileDiffuseUrls["http://cloud-3.steamusercontent.com/ugc/967597347416754435/BA4334969D1AD4E8B146AE7E7A1A430F14C7A5DA/"] = "yellow"
tileDiffuseUrls["http://cloud-3.steamusercontent.com/ugc/967597347419272946/C8F005A109AAD820E38A8E377F1026074B830401/"] = "turquois"
tileDiffuseUrls["http://cloud-3.steamusercontent.com/ugc/967597347419272641/EE644ECD0ADC6C78AC61E0C538BAF98FD8703908/"] = "red"
tileDiffuseUrls["http://cloud-3.steamusercontent.com/ugc/967597347419272833/D4C93372171EBA7604524C0E6B6DA29347B5209A/"] = "blue"
tileDiffuseUrls["http://cloud-3.steamusercontent.com/ugc/967597347419273056/A03D249CF12867EB4134A9A7E9A9D2A1632EE704/"] = "black"
-- TODO: For automatic registration of broken tiles
--playerRowZones = {
-- {
-- '731332',
-- '31a0dd',
-- 'e2f094',
-- 'bb4835',
-- 'f42ad5'
-- }
--}
TABLE_HEIGHT = 5
centerDisplayGUID = 'deadbeef'
tileBagGUID = 'd101ec'
discardBagGUID = '68e703'
dealButtonGUID = "8cea79"
centerAreaGUID = "e9d6b5"
hasGameStarted = false
factoryDisplayGUIDs = {}
-- display -> tile mapping
displayToTiles = {deadbeef={}}
-- tile -> display mapping
tileToDisplay = {}
--[[ The onLoad event is called after the game save finishes loading. --]]
function onLoad(saved_data)
-- In order for the save and rewind buttons to function we
-- need to make sure our local variables are saved properly
if(saved_data ~= "") then
local loaded_data = JSON.decode(saved_data)
hasGameStarted = loaded_data.started
if hasGameStarted then
factoryDisplayGUIDs = loaded_data.factoryDisplayGUIDs
end
end
-- Sets the snap points for the scoring area on each board
-- TODO: migrate the snap points that are set on individual boards here
setBoardSnaps()
-- start gaem button
if not(hasGameStarted) then
startPawn = getObjectFromGUID('45cea7')
startPawn.createButton({
click_function = 'startGame',
label = "Start Game",
function_owner = self,
position = {0, 0 ,5},
rotation = {0, 90, 0},
width = 3000,
height = 800,
font_size = 400,
color = {1,1, 1},
font_color = {0, 0, 0},
tooltip = "Wait until all players are seated to start."
})
else
createDealButton()
end
end
--[[ The onUpdate event is called once per frame. --]]
function onUpdate()
--[[ print('onUpdate loop!') --]]
end
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function onObjectPickUp(colorName, obj)
--print(colorName .. " picked up " .. obj.getName() .. " at " .. JSON.encode(obj.getPosition()))
end
function onObjectDrop(colorName, obj)
displayId = tileToDisplay[obj.getGUID()]
if displayId == nil then
-- Not dropped from display, nothing to do
return
end
-- TODO: Do nothing if the tile is not moved from the display
color = obj.getName()
print(colorName .. " dropped " .. color .. " taken from display " .. displayId)
-- Tile is no longer associated with a display
tileToDisplay[obj.getGUID()] = nil
-- Get the other tiles on the same display
otherTiles = displayToTiles[displayId]
-- Every other tile of the same color is horizontally offset
numDropped = 1
offset = 0
for __, objId in ipairs(otherTiles) do
if objId ~= obj.getGUID() then
otherTile = getObjectFromGUID(objId)
if otherTile.getName() == color then
pos = obj.getPosition()
-- Direction of offset depends on table side
if pos.z > 0 then
offset = offset - 2.3
else
offset = offset + 2.3
end
pos.x = pos.x - offset
otherTile.setPositionSmooth(pos, false, false)
otherTile.setRotationSmooth({ x=0, y=0, z=0 }, false, true)
-- Tile is no longer associated with a display
tileToDisplay[objId] = nil
numDropped = numDropped + 1
elseif displayId ~= centerDisplayGUID then
-- Other tiles are moved to a random position the center
x = math.random(-8000, 8000)
z = math.random(-18000, -2000)
otherTile.setPositionSmooth({ x=x/1000.0, y=TABLE_HEIGHT, z=z/1000.0 }, false, true)
-- Tile is now associated with the center pseudo-display
tileToDisplay[objId] = centerDisplayGUID
table.insert(displayToTiles[centerDisplayGUID], objId)
end
end
end
-- Clear display tile registry
if displayId ~= centerDisplayGUID then
displayToTiles[displayId] = {}
end
-- TODO: For automatic registration of broken tiles
--for i, rowGuid in pairs(playerRowZones[1]) do
-- print(i .. " -> " .. rowGuid)
-- zone = getObjectFromGUID(rowGuid)
-- tileIds = {}
-- for _, subObj in pairs(zone.getObjects()) do
-- table.insert(tileIds, subObj.getGUID())
-- end
-- print(i .. " has " .. JSON.encode_pretty(tileIds))
--end
end
function onSave()
--Save data for future loading
if hasGameStarted then
local data_to_save = {
started = hasGameStarted,
Players = getSeatedPlayers(),
factoryDisplayGUIDs = factoryDisplayGUIDs
}
saved_data = JSON.encode(data_to_save)
return saved_data
end
end
-- This deals out the displays and then triggers each display to get tiles
-- TODO: Should this be a button for the user to select the number of players,
-- instead of using the number of seated players?
function dealDisplays(playerCount)
Players = getSeatedPlayers()
playerCount = playerCount or #Players
displayCounts = {
[1] = 9, [2] = 5, [3] = 7, [4] = 9
}
factoryDisplayStack = getObjectFromGUID('914fda')
-- Lay the displays out in a circle
y = TABLE_HEIGHT
basex = 0
basez = -10
radius = 15
numDisplays = displayCounts[playerCount]
step = 2 * math.pi / numDisplays
for i = 1, numDisplays do
theta = step * i
x = basex + radius * math.cos(theta)
z = basez + radius * math.sin(theta)
factoryDisplayStack.takeObject({
position = {x, y, z},
callback_function = function(obj) dealtDisplay(obj) end,
})
end
end
-- callback function that populates each display with tiles
function dealtDisplay(display)
table.insert(factoryDisplayGUIDs, display.getGUID())
display.setLock(true)
dealTiles(display)
end
function startGame(buttonObj)
hasGameStarted = true
dealDisplays()
createDealButton()
buttonObj.removeButton(0)
end
function createDealButton()
-- Add a button to deal the new round
tileBag = getObjectFromGUID(tileBagGUID)
tileBag.shuffle()
tileBag.createButton({
click_function = 'newRound',
label = "Deal Tiles",
function_owner = self,
position = {-6, 0.5,5},
rotation = {0, 0, 0},
width = 2800,
height = 800,
font_size = 340,
color = {1,1, 1},
font_color = {0, 0, 0},
tooltip = "Deals tiles to the factory displays and shuffles in discard bag if needed."
})
end
function newRound()
tileBag = getObjectFromGUID(tileBagGUID)
tileBag.shuffle()
for __, displayGUID in ipairs(factoryDisplayGUIDs) do
-- There's a race condition on reshuffling the tiles that we need to delay for.
-- TODO: find a better way to do this
if tileBag.getQuantity() < 4 then
recycleTiles()
Wait.time(function() fillDisplay(displayGUID) end, 2)
else
fillDisplay(displayGUID)
end
end
end
function fillDisplay(displayGUID)
display = getObjectFromGUID(displayGUID)
dealTiles(display)
end
function dealTiles(display)
tileBag = getObjectFromGUID(tileBagGUID)
displayPos = display.getPosition()
targetPositions = {
{2.3, 0.0, 0},
{-2.3, 0.0, 0},
{0, 0.0, -2.3},
{0, 0.0, 2.3},
}
displayToTiles[display.getGUID()] = {}
for i = 1, 4 do
tilePosition = display.positionToWorld(targetPositions[i])
tileBag.takeObject({
position = tilePosition,
rotation={0,0,0},
callback_function = function (obj)
color = obj.getName()
if (color == nil or color == '') then
color = tileDiffuseUrls[obj.getCustomObject().diffuse]
obj.setName(color)
end
tileToDisplay[obj.getGUID()] = display.getGUID()
table.insert(displayToTiles[display.getGUID()], obj.getGUID())
end
})
end
end
-- Move tiles from the discard bag to the draw bag
function recycleTiles()
tileBag = getObjectFromGUID(tileBagGUID)
discardBag = getObjectFromGUID(discardBagGUID)
discardBagContent = discardBag.getObjects()
log(discardBagContent)
for __, v in ipairs(discardBagContent) do
tileBag.putObject(discardBag.takeObject({ guid = v.guid }))
end
end
function setBoardSnaps()
scoresnaps = {}
for row = 1, 5 do
for i=1, 20 do
currentx = 7.8 - 0.82*(i-1)
currenty = -7.7 + 1*(row-1)
snapdex = (row-1)*20+i
scoresnaps[snapdex] = { position = {currentx, 0.0, currenty}, rotation = {0,0,0}, rotation_snap = true }
end
end
boards = {getObjectFromGUID('18a461'),getObjectFromGUID('9f42ee'),getObjectFromGUID('8259b9'),getObjectFromGUID('4b40cd')}
for __, board in ipairs(boards) do
board.setSnapPoints(scoresnaps)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment