-- Bundled by luabundle {"version":"1.6.0"} local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire) local loadingPlaceholder = {[{}] = true} local register local modules = {} local require local loaded = {} register = function(name, body) if not modules[name] then modules[name] = body end end require = function(name) local loadedModule = loaded[name] if loadedModule then if loadedModule == loadingPlaceholder then return nil end else if not modules[name] then if not superRequire then local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name) error('Tried to require ' .. identifier .. ', but no such module has been registered') else return superRequire(name) end end loaded[name] = loadingPlaceholder loadedModule = modules[name](require, loaded, register, modules) loaded[name] = loadedModule end return loadedModule end return require, loaded, register, modules end)(nil) __bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules) require("chaosbag/BlessCurseManager") end) __bundle_register("chaosbag/BlessCurseManager", function(require, _LOADED, __bundle_register, __bundle_modules) local chaosBagApi = require("chaosbag/ChaosBagApi") local tokenArrangerApi = require("accessories/TokenArrangerApi") -- common button parameters local buttonParamaters = {} buttonParamaters.function_owner = self buttonParamaters.color = { 0, 0, 0, 0 } buttonParamaters.width = 700 buttonParamaters.height = 700 local altState = false local MODE = { [false] = "Add / Remove", [true] = "Take / Return" } local BUTTON_COLOR = { [false] = { 0.4, 0.4, 0.4 }, [true] = { 0.9, 0.9, 0.9 } } local FONT_COLOR = { [false] = { 1, 1, 1 }, [true] = { 0, 0, 0 } } local whitespace = " " local updating --------------------------------------------------------- -- creating buttons and menus + initializing tables --------------------------------------------------------- function onSave() return JSON.encode(altState) end function onLoad(saved_state) if saved_state ~= nil then altState = JSON.decode(saved_state) end -- index: 0 - bless buttonParamaters.click_function = "clickBless" buttonParamaters.position = { -1.03, 0.05, 0.46 } self.createButton(buttonParamaters) -- index: 1 - curse buttonParamaters.click_function = "clickCurse" buttonParamaters.position[1] = -buttonParamaters.position[1] self.createButton(buttonParamaters) -- index: 2 - alternative mode (take / return) buttonParamaters.click_function = "enableAlt" buttonParamaters.width = 900 buttonParamaters.height = 210 buttonParamaters.position = { -1.03, 0.05, -0.85 } self.createButton(buttonParamaters) -- index: 3 - default mode (add / remove) buttonParamaters.click_function = "enableDefault" buttonParamaters.position[1] = -buttonParamaters.position[1] self.createButton(buttonParamaters) -- load labels, tooltips and colors updateButtons() -- context menu self.addContextMenuItem("Remove all", doRemove) self.addContextMenuItem("Reset", doReset) -- initializing tables initializeState() broadcastCount("Curse") broadcastCount("Bless") end function resetTables() numInPlay = { Bless = 0, Curse = 0 } tokensTaken = { Bless = {}, Curse = {} } sealedTokens = {} end function initializeState() resetTables() -- count tokens in the bag local chaosbag = chaosBagApi.findChaosBag() local tokens = {} for _, v in ipairs(chaosbag.getObjects()) do if v.name == "Bless" then numInPlay.Bless = numInPlay.Bless + 1 elseif v.name == "Curse" then numInPlay.Curse = numInPlay.Curse + 1 end end -- find tokens in the play area for _, obj in ipairs(getObjects()) do local pos = obj.getPosition() if pos.x > -65 and pos.x < 10 and pos.z > -35 and pos.z < 35 then if obj.getName() == "Bless" then table.insert(tokensTaken.Bless, obj.getGUID()) numInPlay.Bless = numInPlay.Bless + 1 elseif obj.getName() == "Curse" then table.insert(tokensTaken.Curse, obj.getGUID()) numInPlay.Curse = numInPlay.Curse + 1 end end end end function broadcastCount(token) local count = formatTokenCount(token) if count == "(0/0)" then return end broadcastToAll(token .. " Tokens " .. count, "White") end function broadcastStatus(color) broadcastToColor("Curse Tokens " .. formatTokenCount("Curse"), color, "White") broadcastToColor("Bless Tokens " .. formatTokenCount("Bless"), color, "White") end -- context menu function 1 function doRemove(color) local chaosbag = chaosBagApi.findChaosBag() -- remove tokens from chaos bag local count = { Bless = 0, Curse = 0 } for _, v in ipairs(chaosbag.getObjects()) do if v.name == "Bless" or v.name == "Curse" then chaosbag.takeObject({ guid = v.guid, position = { 0, 5, 0 }, callback_function = function(obj) obj.destruct() end }) count[v.name] = count[v.name] + 1 end end broadcastToColor("Removed " .. count.Bless .. " Bless and " .. count.Curse .. " Curse tokens from the chaos bag.", color, "White") broadcastToColor("Removed " .. removeTakenTokens("Bless") .. " Bless and " .. removeTakenTokens("Curse") .. " Curse tokens from play.", color, "White") resetTables() tokenArrangerApi.layout() end -- context menu function 2 function doReset(color) initializeState() broadcastCount("Curse") broadcastCount("Bless") tokenArrangerApi.layout() end -- removing tokens that were 'taken' function removeTakenTokens(type) local count = 0 for _, guid in ipairs(tokensTaken[type]) do local token = getObjectFromGUID(guid) if token ~= nil then token.destruct() count = count + 1 end end return count end --------------------------------------------------------- -- click functions --------------------------------------------------------- -- click function 1 function clickBless(_, color, isRightClick) playerColor = color callFunctions("Bless", isRightClick) end -- click function 2 function clickCurse(_, color, isRightClick) playerColor = color callFunctions("Curse", isRightClick) end -- click function 3 function enableAlt() if altState then return end altState = not altState updateButtons() end -- click function 4 function enableDefault() if not altState then return end altState = not altState updateButtons() end --------------------------------------------------------- -- called functions --------------------------------------------------------- function updateButtons() self.editButton({ index = 0, tooltip = MODE[altState] .. " Bless" }) self.editButton({ index = 1, tooltip = MODE[altState] .. " Curse" }) self.editButton({ index = 2, label = whitespace .. MODE[true] .. (altState and " ✓" or whitespace) .. " ", color = BUTTON_COLOR[not altState], font_color = FONT_COLOR[not altState] }) self.editButton({ index = 3, label = whitespace .. MODE[false] .. (altState and whitespace or " ✓") .. " ", color = BUTTON_COLOR[altState], font_color = FONT_COLOR[altState] }) end -- function that is called by click_functions 1+2 and calls the other functions function callFunctions(token, isRightClick) if not chaosBagApi.canTouchChaosTokens() then return end local success if not altState then if isRightClick then success = takeToken(token, true) else success = addToken(token) end else if isRightClick then success = returnToken(token) else success = takeToken(token, false) end end if success ~= 0 then tokenArrangerApi.layout() end end -- returns a formatted string with information about the provided token type (bless / curse) function formatTokenCount(type) if type == nil then type = mode end return "(" .. (numInPlay[type] - #tokensTaken[type]) .. "/" .. #tokensTaken[type] .. ")" end -- called by cards that seal bless/curse tokens ---@param param Table This contains the type and guid of the sealed token function sealedToken(param) table.insert(tokensTaken[param.type], param.guid) broadcastCount(param.type) end -- called by cards that seal bless/curse tokens ---@param param Table This contains the type and guid of the released token function releasedToken(param) for i, v in ipairs(tokensTaken[param.type]) do if v == param.guid then table.remove(tokensTaken[param.type], i) break end end if not updating then updating = true Wait.frames(function() broadcastCount(param.type) updating = false end, 1) end end --------------------------------------------------------- -- main functions: add, take and return --------------------------------------------------------- function addToken(type) if numInPlay[type] == 10 then printToColor("10 tokens already in play, not adding any.", playerColor) return 0 end numInPlay[type] = numInPlay[type] + 1 printToAll("Adding " .. type .. " token " .. formatTokenCount(type)) return chaosBagApi.spawnChaosToken(type) end function takeToken(type, remove) local chaosbag = chaosBagApi.findChaosBag() if not remove and not SEAL_CARD_MESSAGE then broadcastToColor("For sealing tokens on cards try right-clicking on the card for seal options.", playerColor) SEAL_CARD_MESSAGE = true end local tokens = {} for _, v in ipairs(chaosbag.getObjects()) do if v.name == type then table.insert(tokens, v.guid) end end if #tokens == 0 then printToColor("No " .. type .. " tokens in the chaos bag.", playerColor) return 0 end local pos = self.getPosition() + Vector(2.25, 0, 0.85) if type == "Curse" then pos[3] = pos[3] - 1.7 end chaosbag.takeObject({ guid = table.remove(tokens), position = pos, smooth = false, callback_function = function(obj) if remove then numInPlay[type] = numInPlay[type] - 1 printToAll("Removing " .. type .. " token " .. formatTokenCount(type)) obj.destruct() else table.insert(tokensTaken[type], obj.getGUID()) printToAll("Taking " .. type .. " token " .. formatTokenCount(type)) end end }) end function returnToken(type) local guid = table.remove(tokensTaken[type]) if guid == nil then printToColor("No " .. type .. " tokens to return", playerColor) return 0 end local token = getObjectFromGUID(guid) if token == nil then printToColor("Couldn't find token " .. guid .. ", not returning to bag", playerColor) return 0 end local chaosbag = chaosBagApi.findChaosBag() if chaosbag == nil then return 0 end chaosbag.putObject(token) printToAll("Returning " .. type .. " token " .. formatTokenCount(type)) end --------------------------------------------------------- -- Wendy Menu (context menu for cards on hotkey press) --------------------------------------------------------- function addMenuOptions(parameters) local playerColor = parameters.playerColor local hoveredObject = parameters.hoveredObject if hoveredObject == nil or hoveredObject.getVar("MENU_ADDED") == true then return end if hoveredObject.tag ~= "Card" then broadcastToColor("Right-click seal options can only be added to cards", playerColor) return end hoveredObject.addContextMenuItem("Seal Bless", function(color) sealToken("Bless", color, hoveredObject) tokenArrangerApi.layout() end, true) hoveredObject.addContextMenuItem("Release Bless", function(color) releaseToken("Bless", color, hoveredObject) tokenArrangerApi.layout() end, true) hoveredObject.addContextMenuItem("Seal Curse", function(color) sealToken("Curse", color, hoveredObject) tokenArrangerApi.layout() end, true) hoveredObject.addContextMenuItem("Release Curse", function(color) releaseToken("Curse", color, hoveredObject) tokenArrangerApi.layout() end, true) broadcastToColor("Right-click seal options added to " .. hoveredObject.getName(), playerColor) hoveredObject.setVar("MENU_ADDED", true) sealedTokens[hoveredObject.getGUID()] = {} end function sealToken(type, playerColor, enemy) local chaosbag = chaosBagApi.findChaosBag() if chaosbag == nil then return end local pos = enemy.getPosition() for i, token in ipairs(chaosbag.getObjects()) do if token.name == type then chaosbag.takeObject({ position = { pos.x, pos.y + 1, pos.z }, index = i - 1, smooth = false, callback_function = function(obj) Wait.frames(function() table.insert(sealedTokens[enemy.getGUID()], obj) table.insert(tokensTaken[type], obj.getGUID()) printToColor("Sealing " .. type .. " token " .. formatTokenCount(type), playerColor) end, 1) end }) return end end printToColor(type .. " token not found in bag", playerColor) end function releaseToken(type, playerColor, enemy) local chaosbag = chaosBagApi.findChaosBag() if chaosbag == nil then return end local tokens = sealedTokens[enemy.getGUID()] if tokens == nil or #tokens == 0 then return end for i, token in ipairs(tokens) do if token ~= nil and token.getName() == type then local guid = token.getGUID() chaosbag.putObject(token) for j, v in ipairs(tokensTaken[type]) do if v == guid then table.remove(tokensTaken[type], j) table.remove(tokens, i) printToColor("Releasing " .. type .. " token" .. formatTokenCount(type), playerColor) return end end end end printToColor(type .. " token not sealed on " .. enemy.getName(), playerColor) end end) __bundle_register("accessories/TokenArrangerApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local TokenArrangerApi = {} -- local function to call the token arranger, if it is on the table ---@param functionName String Name of the function to cal ---@param argument Variant Parameter to pass local function callIfExistent(functionName, argument) local tokenArranger = getObjectsWithTag("TokenArranger")[1] if tokenArranger ~= nil then tokenArranger.call(functionName, argument) end end -- updates the token modifiers with the provided data ---@param tokenData Table Contains the chaos token metadata TokenArrangerApi.onTokenDataChanged = function(fullData) callIfExistent("onTokenDataChanged", fullData) end -- deletes already laid out tokens TokenArrangerApi.deleteCopiedTokens = function() callIfExistent("deleteCopiedTokens") end -- updates the laid out tokens TokenArrangerApi.layout = function() Wait.time(function() callIfExistent("layout") end, 0.1) end return TokenArrangerApi end end) __bundle_register("chaosbag/ChaosBagApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local ChaosBagApi = {} -- respawns the chaos bag with a new state of tokens ---@param tokenList Table List of chaos token ids ChaosBagApi.setChaosBagState = function(tokenList) return Global.call("setChaosBagState", tokenList) end -- returns a Table List of chaos token ids in the current chaos bag -- requires copying the data into a new table because TTS is weird about handling table return values in Global ChaosBagApi.getChaosBagState = function() local chaosBagContentsCatcher = Global.call("getChaosBagState") local chaosBagContents = {} for _, v in ipairs(chaosBagContentsCatcher) do table.insert(chaosBagContents, v) end return chaosBagContents end -- checks scripting zone for chaos bag (also called by a lot of objects!) ChaosBagApi.findChaosBag = function() return Global.call("findChaosBag") end -- returns a table of object references to the tokens in play (does not include sealed tokens!) ChaosBagApi.getTokensInPlay = function() return Global.getTable("chaosTokens") end -- returns all sealed tokens on cards to the chaos bag ChaosBagApi.releaseAllSealedTokens = function(playerColor) return Global.call("releaseAllSealedTokens", playerColor) end -- returns all drawn tokens to the chaos bag ChaosBagApi.returnChaosTokens = function(playerColor) return Global.call("returnChaosTokens", playerColor) end -- removes the specified chaos token from the chaos bag ---@param id String ID of the chaos token ChaosBagApi.removeChaosToken = function(id) return Global.call("removeChaosToken", id) end -- spawns the specified chaos token and puts it into the chaos bag ---@param id String ID of the chaos token ChaosBagApi.spawnChaosToken = function(id) return Global.call("spawnChaosToken", id) end -- Checks to see if the chaos bag can be manipulated. If a player is searching the bag when tokens -- are drawn or replaced a TTS bug can cause those tokens to vanish. Any functions which change the -- contents of the bag should check this method before doing so. -- This method will broadcast a message to all players if the bag is being searched. ---@return Boolean. True if the bag is manipulated, false if it should be blocked. ChaosBagApi.canTouchChaosTokens = function() return Global.call("canTouchChaosTokens") end -- called by playermats (by the "Draw chaos token" button) ChaosBagApi.drawChaosToken = function(mat, tokenOffset, isRightClick) return Global.call("drawChaosToken", {mat, tokenOffset, isRightClick}) end -- returns a Table List of chaos token ids in the current chaos bag -- requires copying the data into a new table because TTS is weird about handling table return values in Global ChaosBagApi.getIdUrlMap = function() return Global.getTable("ID_URL_MAP") end return ChaosBagApi end end) return __bundle_require("__root")