Tabletop Simulator

Tabletop Simulator

Gloomhaven - Fantasy Setup (Scripted UI)
Punkistador Mar 25, 2021 @ 1:45am
Adding Player/Monster Curses
Spent about an hour looking for this. Is there an automated way to add a Monster Curse (or Player Curse/Bless) to the respective Attack modifier decks, or is dragging it the only way to do it?
< >
Showing 1-7 of 7 comments
Lowlife555 Mar 25, 2021 @ 2:50am 
Drag, or right click respective deck choosing Deal.
Punkistador Mar 25, 2021 @ 7:28am 
Selecting Deal puts it into the Player Hand rather than the Monster deck.
But more generally, it sounds like there is not scripting around adding Curses, so we will continue to do manually (I believe there is scripting to remove them once used).
Lowlife555 Mar 26, 2021 @ 2:11am 
That is true, no scripting. Dont think there are any scripting for the cards on the right.
Well except adding a new deck.
Spitler Apr 8, 2021 @ 5:46am 
Hello,

I was actually looking to post this seperately, let me know if that is a more proper way to get this post to the community, anyway this seems the right place

ive written the code to do what you ask for
it sort of follows the code-flow of the original mod

Added 2 buttons to the 'battle-interface'
that call drawBless and drawCurse respectively

function drawBless(player, value, id) local color if id == nil then color = player.color else color = id end local class = getClassFromColor(color) doDrawnBlessCurse("Bless",color,class) end function drawCurse(player, value, id) local color if id == nil then color = player.color else color = id end local class = getClassFromColor(color) doDrawnBlessCurse("Curse",color,class) end function doDrawnBlessCurse(which,color,class) -- check for deck local deck = nil local hitlist = Physics.cast({ origin = toCheck[which], direction = {0, -1, 0}, type = 1, debug = false }) local found = false local found_obj for _, c in pairs(hitlist) do if c.hit_object.tag == "Deck" or c.hit_object.tag == "Card" then found = true found_obj = c break end end -- execute if found local msg = "No more "..which -- if not found if found then msg = which.." the "..class local playerAMDeckPos = colorToPlayer[color].AMDeck local targetPos = { playerAMDeckPos[1], playerAMDeckPos[2]+1, playerAMDeckPos[3] } -- found deck if found_obj.hit_object.tag == "Deck" then local card = found_obj.hit_object.takeObject({ position = targetPos, index = 0, flip = true }) -- found card elseif found_obj.hit_object.tag == "Card" then found_obj.hit_object.flip() found_obj.hit_object.setPositionSmooth(targetPos) end -- and shuffle local findDeck = Physics.cast({ origin = playerAMDeckPos, direction = {0, -1, 0}, type = 3, debug = false }) for _, c in pairs(findDeck) do if c.hit_object.tag == "Deck" then Wait.time(function() c.hit_object.shuffle() end, 0.5, 5) -- c.hit_object.shuffle() break end end end printToAll("Papa "..which..": "..msg ,color) end

Note that the way its now it needs an extra entry at global.ttslua line 25
toCheck["Curse"] = toCheck["Player Curse"] -- Same as player curse but for nicer msg in doDrawnBlessCurse
if you dont want that then replace
doDrawnBlessCurse("Curse",color,class)
in drawCurse() by
doDrawnBlessCurse("Player Curse",color,class)

hope this helps :)

ps. there is not 'no scripting' for the cards on the right, after being drawn, the blesses/curses do get returned to their respective decks automatically ;)

solomojb Apr 25, 2021 @ 1:25pm 
Ok I need this for our group. I am tired of having to drag and shuffle blesses and curses.

I figured out where to add it. For others; you need to add the code above to the globalui in addition to the line 25. I did around line 1172. (But i suppose it can go anywhere?)

Then in the battle interface ui for the buttons you have to add the buttons to that ui.
<HorizontalLayout spacing="5">
<Button onClick="drawBless">
<Text>Draw Bless</Text>
</Button>
<Button onClick="drawCurse">
<Text>Draw Curse</Text>
</Button>
</HorizontalLayout>

I added that around line 120 in the global ui file.
Last edited by solomojb; Apr 25, 2021 @ 8:25pm
Spitler Apr 27, 2021 @ 5:41pm 
Originally posted by solomojb:
Ok I need this for our group. I am tired of having to drag and shuffle blesses and curses.

I figured out where to add it. For others; you need to add the code above to the globalui in addition to the line 25. I did around line 1172. (But i suppose it can go anywhere?)

Then in the battle interface ui for the buttons you have to add the buttons to that ui.
<HorizontalLayout spacing="5">
<Button onClick="drawBless">
<Text>Draw Bless</Text>
</Button>
<Button onClick="drawCurse">
<Text>Draw Curse</Text>
</Button>
</HorizontalLayout>

I added that around line 120 in the global ui file.

yes, the code can go anywhere in global.ttslua
Spitler Apr 27, 2021 @ 5:58pm 
ok, so i have edited as well as expanded the code above. heres the new part.
Changes:
1) Biggest Change is that there is now a return bless/curse button (mainly for if you make a mistake)
2) Monster Curse/Bless !

As before, im not sure this is the right way to share these things, pasting code in comments is rather cumbersome and inefficient tbh
please let me know if there is a more proper way, or if you simply want more info :)
anyway, here we go


Add Buttons to the Battle Interface:
<HorizontalLayout spacing="5"> <HorizontalLayout spacing="1" width="50%"> <Button onClick="drawBlessCurseButton" id="Bless" minWidth="10%" preferredWidth="60%" flexibleWidth="100"> <Text class="BImenu">Bless</Text> </Button> <Button onClick="returnBlessCurseButton" id="Bless" minWidth="10%" preferredWidth="10%" flexibleWidth="5"> <Text class="BImenu" alignment="UpperCenter">-</Text> </Button> </HorizontalLayout> <HorizontalLayout spacing="1" width="50%"> <Button onClick="drawBlessCurseButton" id="Curse" minWidth="10%" preferredWidth="60%" flexibleWidth="100"> <Text class="BImenu">Curse</Text> </Button> <Button onClick="returnBlessCurseButton" id="Curse" minWidth="10%" preferredWidth="10%" flexibleWidth="5"> <Text class="BImenu" alignment="UpperCenter">-</Text> </Button> </HorizontalLayout> </HorizontalLayout> <Text class="BImenu" color="#828282">Monster</Text> <HorizontalLayout spacing="5"> <HorizontalLayout spacing="1" width="50%"> <Button onClick="drawBlessCurseButton" id="Monster Bless" minWidth="10%" preferredWidth="60%" flexibleWidth="100"> <Text class="BImenu">Bless</Text> </Button> <Button onClick="returnBlessCurseButton" id="Monster Bless" minWidth="10%" preferredWidth="10%" flexibleWidth="5"> <Text class="BImenu" alignment="UpperCenter">-</Text> </Button> </HorizontalLayout> <HorizontalLayout spacing="1" width="50%"> <Button onClick="drawBlessCurseButton" id="Monster Curse" minWidth="10%" preferredWidth="60%" flexibleWidth="100"> <Text class="BImenu">Curse</Text> </Button> <Button onClick="returnBlessCurseButton" id="Monster Curse" minWidth="10%" preferredWidth="10%" flexibleWidth="5"> <Text class="BImenu" alignment="UpperCenter">-</Text> </Button> </HorizontalLayout> </HorizontalLayout>

Add These at global.ttslua line 25: (they are the location of the bless/curse cards)
toCheck["Player Curse"] = {51.60, 4.00, 23.65} toCheck["Curse"] = toCheck["Player Curse"] -- Same as player curse but for nicer msg in doDrawnBlessCurse toCheck["Bless"] = {51.60, 4.00, 19.99} toCheck["Monster Bless"] = {51.63, 1.72, -27.28} toCheck["Monster Curse"] = {51.60, 1.82, -23.66}
Note we decided to houserule that the monster has its own 10 blesses, its not shared with the players, This means you have to copy 10 Player Blesses, and set their "Name" (tts property) to "Monster Bless". The location defined in the code above refers to right below the monster curses.
if you don't want that, set
toCheck["Monster Bless"] = toCheck["Bless"]


Also Define this table in global.ttslua (i did so at line ~1600, right after the definition of colorToPlayer, as they fulfill the same role)
MonsterAMPos = { Yellow = { -- Monster color = {0.905, 0.898, 0.172}, AMDiscard = {2.05, 1.87, -16.05}, AMDeck = {-2.07, 1.87, -16.03} } }


Then, anywhere, in global.ttslua, drop these functions (for me its lines ~700)
function returnBlessCurseButton(player, value, id) local color = player.color local class = "" if string.find( id, "Monster" ) then -- id = string.gsub( id, "Monster", "" ) class = "Monster" color = "Yellow" else class = getClassFromColor(color) end doReturnBlessCurse(id,color,class) end function doReturnBlessCurse(which,color,class) local noFoundMSG = ": None Found" local yesFoundMSG = "by " .. class local targetPos = toCheck[which] local ColorToInfo = colorToPlayer if color == "Yellow" then ColorToInfo = MonsterAMPos end local playerAMDeckPos = ColorToInfo[color].AMDeck -- Get the AMDeck local hitlist = Physics.cast({ origin = playerAMDeckPos, direction = {0, -1, 0}, type = 3, debug = false }) -- print(hitlist) local foundDeck = nil for _, c in pairs(hitlist) do if c.hit_object.tag == "Deck" or c.hit_object.tag == "Card" then foundDeck = c.hit_object break end end -- Check the AMDeck and act -- print(foundDeck) -- print(foundDeck.tag) local msg = noFoundMSG if foundDeck then -- found deck if foundDeck.tag == "Deck" then for _, u in pairs(foundDeck.getObjects()) do if u.name:find(which) then -- if toCheck[u.name] then local card = foundDeck.takeObject({ position = toCheck[u.name], guid = u.guid, flip = true }) msg = yesFoundMSG break end end -- found card elseif foundDeck.tag == "Card" then if foundDeck.getName():find(which) then foundDeck.flip() foundDeck.setPositionSmooth(toCheck[foundDeck.getName()]) msg = yesFoundMSG end end -- else -- print('pass') end msg = "Return " .. which .. " " .. msg printToAll(msg ,color) end function drawBlessCurseButton(player, value, id) local color = player.color local class = "" if string.find( id, "Monster" ) then -- id = string.gsub( id, "Monster", "" ) class = "Monster" color = "Yellow" else class = getClassFromColor(color) end doDrawnBlessCurse(id,color,class) end function doPing(pos,color) local color = color or "Green" Player[color].pingTable(pos) end function doDrawnBlessCurse(which,color,class) -- check for deck local deck = nil -- local hitlist = Physics.cast({ -- origin = toCheck[which], -- direction = {0, -1, 0}, -- type = 1, -- debug = false -- }) local hitlist = Physics.cast({ origin = toCheck[which], direction = {0, -1, 0}, size = { 1,5,1 }, type = 3, debug = false }) local found = false local found_obj for _, c in pairs(hitlist) do if c.hit_object.tag == "Deck" or c.hit_object.tag == "Card" then found = true found_obj = c break end end -- execute if found local msg = "No More " --..which -- if not found if found then msg ="the "..class -- which.. local ColorToInfo = colorToPlayer if color == "Yellow" then ColorToInfo = MonsterAMPos end local playerAMDeckPos = ColorToInfo[color].AMDeck local targetPos = { playerAMDeckPos[1], playerAMDeckPos[2]+1, playerAMDeckPos[3] } -- found deck if found_obj.hit_object.tag == "Deck" then local card = found_obj.hit_object.takeObject({ position = targetPos, index = 0, flip = true }) -- found card elseif found_obj.hit_object.tag == "Card" then found_obj.hit_object.flip() found_obj.hit_object.setPositionSmooth(targetPos) end -- and shuffle local findDeck = Physics.cast({ origin = playerAMDeckPos, direction = {0, -1, 0}, type = 3, debug = false }) for _, c in pairs(findDeck) do if c.hit_object.tag == "Deck" then Wait.time(function() c.hit_object.shuffle() end, 0.5, 5) -- c.hit_object.shuffle() break end end end printToAll("Papa "..which.." "..msg ,color) end

Last edited by Spitler; Apr 27, 2021 @ 6:02pm
< >
Showing 1-7 of 7 comments
Per page: 1530 50