-- 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("accessories/CampaignImporterExporter") end) __bundle_register("accessories/CampaignImporterExporter", function(require, _LOADED, __bundle_register, __bundle_modules) local blessCurseApi = require("chaosbag/BlessCurseManagerApi") local chaosBagApi = require("chaosbag/ChaosBagApi") local deckImporterApi = require("arkhamdb/DeckImporterApi") local guidReferenceApi = require("core/GUIDReferenceApi") local optionPanelApi = require("core/OptionPanelApi") local playAreaApi = require("core/PlayAreaApi") local playermatApi = require("playermat/PlayermatApi") -- base data for token creation local campaignTokenData = { Name = "Custom_Model_Bag", Transform = { posX = -21.25, posY = 1.68, posZ = 55.59, rotX = 0, rotY = 270, rotZ = 0, scaleX = 2, scaleY = 2, scaleZ = 2 }, Description = "SCED Importer Token", Tags = { "ImporterToken" }, CustomMesh = { MeshURL = "http://cloud-3.steamusercontent.com/ugc/943949966265929204/A38BB5D72419E6298385556D931877C0A1A55C17/", DiffuseURL = "http://cloud-3.steamusercontent.com/ugc/254843371583188147/920981125E37B5CEB6C400E3FD353A2C428DA969/", ColliderURL = "http://cloud-3.steamusercontent.com/ugc/943949966265929204/A38BB5D72419E6298385556D931877C0A1A55C17/", Convex = true, MaterialIndex = 2, TypeIndex = 6, CustomShader = { SpecularColor = { r = 0.72, g = 0.51, b = 0.34 }, SpecularIntensity = 0.4, SpecularSharpness = 7.0, FresnelStrength = 0.0 } } } function onLoad() self.createButton({ click_function = "createCampaignToken", function_owner = self, label = "Export", tooltip = "Create a campaign save token!", position = { x = -1, y = 0.21, z = 0 }, font_size = 400, width = 1400, height = 600, scale = { 0.5, 1, 0.5 } }) end function onObjectLeaveContainer(container) if container.hasTag("ImporterToken") then broadcastToAll("Removing objects from the Save Coin bag will break functionality. Please return the removed objects.", "Yellow") end end function onObjectEnterContainer(container) if container.hasTag("ImporterToken") then broadcastToAll("Adding objects to the Save Coin bag will break functionality. Please remove the objects.", "Yellow") end end --------------------------------------------------------- -- main import functions (split up to allow for Wait conditions) --------------------------------------------------------- function onCollisionEnter(info) if info.collision_object.hasTag("ImporterToken") then importFromToken(info.collision_object) end end -- identifies import token, determines campaign box and downloads it (if needed) function importFromToken(coin) broadcastToAll("Campaign Import Initiated") local importData = JSON.decode(coin.getGMNotes()) local campaignBox = getObjectFromGUID(importData["box"]) if not campaignBox then broadcastToAll("Campaign Box not present on table!", "Red") return end if campaignBox.type == "Generic" then campaignBox.call("buttonClick_download") end Wait.condition( function() campaignBox = getObjectFromGUID(importData["box"]) if #campaignBox.getObjects() > 0 then placeCampaignFromToken(importData, coin) else restoreCampaignData(importData, coin) end end, function() campaignBox = getObjectFromGUID(importData["box"]) if campaignBox == nil then return false else return campaignBox.type == "Bag" end end, 2, function() broadcastToAll("Error loading campaign box") end ) end -- after box has been downloaded, places content on table function placeCampaignFromToken(importData, coin) getObjectFromGUID(importData["box"]).call("buttonClick_place") Wait.condition( function() restoreCampaignData(importData, coin) end, function() return findUniqueObjectWithTag("CampaignLog") ~= nil end, 2, function() broadcastToAll("Error placing campaign box") end ) end -- after content is placed on table, conducts all the other import operations function restoreCampaignData(importData, coin) -- go over internal items and respawn them (only storing campaign log and additional player cards) for _, objData in ipairs(coin.getData().ContainedObjects) do objData.Locked = true local spawnData = { data = objData } -- maybe restore position of item and destroy duplicate if objData.Nickname == "Additional Player Cards" then local additionalIndex = guidReferenceApi.getObjectByOwnerAndType("Mythos", "AdditionalPlayerCardsBag") spawnData.position = additionalIndex.getPosition() additionalIndex.destruct() else local campaignLog = findUniqueObjectWithTag("CampaignLog") if campaignLog then spawnData.position = campaignLog.getPosition() campaignLog.destruct() end end spawnObjectData(spawnData) end chaosBagApi.setChaosBagState(importData["bag"]) -- populate trauma values if importData["trauma"] then setTrauma(importData["trauma"]) end -- populate ArkhamDB deck IDs if importData["decks"] then deckImporterApi.setUiState(importData["decks"]) end -- maybe set campaign guide page (unless it was on the first page) if importData["guide"] and importData["guide"] ~= 0 then local campaignGuide = findUniqueObjectWithTag("CampaignGuide") if campaignGuide then Wait.condition( -- Called after the condition function returns true function() printToAll("Campaign Guide import successful!") end, -- Condition function that is called continuously until it returns true or timeout is reached function() return campaignGuide.Book.setPage(importData["guide"]) end, -- Amount of time in seconds until the Wait times out 2, -- Called if the Wait times out function() printToAll("Campaign Guide import failed!") end ) end end Wait.time(function() optionPanelApi.loadSettings(importData["options"]) end, 0.5) -- destroy Tour Starter token local tourStarter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TourStarter") if tourStarter then tourStarter.destruct() end -- restore PlayArea image and player count playAreaApi.updateSurface(importData["playarea"]) playAreaApi.setInvestigatorCount(importData["clueCount"]) -- restore playermat slots if importData["slotData"] then for matColor, slotData in pairs(importData["slotData"]) do playermatApi.loadSlotData(matColor, slotData) end end coin.destruct() broadcastToAll("Campaign successfully imported!", "Green") end -- creates a campaign token with save data encoded into GM Notes based on the current state of the table function createCampaignToken(_, playerColor, _) local campaignData = {} -- need to reset the contained objects to support multiple exports campaignTokenData.ContainedObjects = {} -- find active campaign local campaignBox for _, obj in ipairs(getObjectsWithTag("CampaignBox")) do if obj.type == "Bag" and #obj.getObjects() == 0 then if not campaignBox then campaignBox = obj else broadcastToAll("Multiple empty campaign boxes detected; delete all but one.", "Red") return end end end if not campaignBox then broadcastToAll("Campaign box with all placed objects not found!", "Red") return end -- clean up chaos tokens (needs to happen before saving chaos bag state) blessCurseApi.removeAll(playerColor) chaosBagApi.releaseAllSealedTokens(playerColor) -- main data collection campaignData.box = campaignBox.getGUID() campaignData.bag = chaosBagApi.getChaosBagState() campaignData.decks = deckImporterApi.getUiState() campaignData.clueCount = playAreaApi.getInvestigatorCount() campaignData.playarea = playAreaApi.getSurface() campaignData.options = optionPanelApi.getOptions() -- save campaign log if present local campaignLog = findUniqueObjectWithTag("CampaignLog") if campaignLog then local logData = campaignLog.getData() logData.Locked = false table.insert(campaignTokenData.ContainedObjects, logData) -- maybe also extract the trauma values local trauma = campaignLog.getVar("returnTrauma") if trauma then printToAll("Trauma values found in campaign log!", "Green") campaignData.trauma = {} for _, val in ipairs(campaignLog.call("returnTrauma")) do table.insert(campaignData.trauma, val) end else printToAll("Trauma values could not be found in campaign log!", "Yellow") end end -- store campaign guide page if present local campaignGuide = findUniqueObjectWithTag("CampaignGuide") if campaignGuide then campaignData.guide = campaignGuide.Book.getPage() end -- store the additional index if there are any cards in it local additionalIndex = guidReferenceApi.getObjectByOwnerAndType("Mythos", "AdditionalPlayerCardsBag") if additionalIndex and #additionalIndex.getObjects() > 0 then local indexData = additionalIndex.getData() indexData.Locked = false table.insert(campaignTokenData.ContainedObjects, indexData) end -- get the slot symbol data for each playermat (use GUIDReferenceApi to only get this for existing playermats) campaignData.slotData = {} for matColor, _ in pairs(guidReferenceApi.getObjectsByType("Playermat")) do local slotData = playermatApi.getSlotData(matColor) campaignData.slotData[matColor] = slotData end -- finish the data for the campaign token campaignTokenData.GMNotes = JSON.encode(campaignData) campaignTokenData.Nickname = campaignBox.getName() .. os.date(" %b %d") .. " Save" spawnObjectData({ data = campaignTokenData }) broadcastToAll("Campaign successfully exported! Save coin object to import on a different save.", "Green") end --------------------------------------------------------- -- helper functions --------------------------------------------------------- function findUniqueObjectWithTag(tag) local objects = getObjectsWithTag(tag) if not objects then return end if #objects == 1 then return objects[1] elseif #objects == 0 then broadcastToAll("No " .. tag .. " detected; ensure it has the correct tag.", "Red") else broadcastToAll("More than one " .. tag .. " detected; delete all but one.", "Red") end end function setTrauma(trauma) for i, matColor in ipairs({ "White", "Orange", "Green", "Red" }) do playermatApi.updateCounter(matColor, "DamageCounter", trauma[i]) playermatApi.updateCounter(matColor, "HorrorCounter", trauma[i + 4]) end end end) __bundle_register("arkhamdb/DeckImporterApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local DeckImporterApi = {} local guidReferenceApi = require("core/GUIDReferenceApi") local function getDeckImporter() return guidReferenceApi.getObjectByOwnerAndType("Mythos", "DeckImporter") end ---@class uiStateTable ---@field redDeck string Deck ID to load for the red player ---@field orangeDeck string Deck ID to load for the orange player ---@field whiteDeck string Deck ID to load for the white player ---@field greenDeck string Deck ID to load for the green player ---@field privateDeck boolean True to load a private deck, false to load a public deck ---@field loadNewest boolean True if the most upgraded version of the deck should be loaded ---@field investigators boolean True if investigator cards should be spawned -- Returns a table with the full state of the UI, including options and deck IDs. -- This can be used to persist via onSave(), or provide values for a load operation ---@return uiStateTable uiStateTable Contains data about the current UI state DeckImporterApi.getUiState = function() local passthroughTable = {} for k,v in pairs(getDeckImporter().call("getUiState")) do passthroughTable[k] = v end return passthroughTable end -- Updates the state of the UI based on the provided table. Any values not provided will be left the same. ---@return uiStateTable uiStateTable Contains data about the current UI state DeckImporterApi.setUiState = function(uiStateTable) return getDeckImporter().call("setUiState", uiStateTable) end return DeckImporterApi 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) 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.call("getChaosTokensinPlay") end -- returns all sealed tokens on cards to the chaos bag ---@param playerColor string Color of the player to show the broadcast to ChaosBagApi.releaseAllSealedTokens = function(playerColor) Global.call("releaseAllSealedTokens", playerColor) end -- returns all drawn tokens to the chaos bag ChaosBagApi.returnChaosTokens = function() Global.call("returnChaosTokens") end -- removes the specified chaos token from the chaos bag ---@param id string ID of the chaos token ChaosBagApi.removeChaosToken = function(id) Global.call("removeChaosToken", id) end -- returns a chaos token to the bag and calls all relevant functions ---@param token tts__Object Chaos token to return ---@param fromBag boolean whether or not the token to return was in the middle of being drawn (true) or elsewhere (false) ChaosBagApi.returnChaosTokenToBag = function(token, fromBag) Global.call("returnChaosTokenToBag", { token = token, fromBag = fromBag }) 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) 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 any: True if the bag is manipulated, false if it should be blocked. ChaosBagApi.canTouchChaosTokens = function() return Global.call("canTouchChaosTokens") end -- draws a chaos token to a playermat ---@param mat tts__Object Playermat that triggered this ---@param drawAdditional boolean Controls whether additional tokens should be drawn ---@param tokenType? string Name of token (e.g. "Bless") to be drawn from the bag ---@param guidToBeResolved? string GUID of the sealed token to be resolved instead of drawing a token from the bag ---@param takeParameters? table Position and rotation of the location where the new token should be drawn to, usually to replace a returned token ---@return tts__Object: Object reference to the token that was drawn ChaosBagApi.drawChaosToken = function(mat, drawAdditional, tokenType, guidToBeResolved, takeParameters) return Global.call("drawChaosToken", { mat = mat, drawAdditional = drawAdditional, tokenType = tokenType, guidToBeResolved = guidToBeResolved, takeParameters = takeParameters }) 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) __bundle_register("core/PlayAreaApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local PlayAreaApi = {} local guidReferenceApi = require("core/GUIDReferenceApi") local function getPlayArea() return guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayArea") end local function getInvestigatorCounter() return guidReferenceApi.getObjectByOwnerAndType("Mythos", "InvestigatorCounter") end -- Returns the current value of the investigator counter from the playermat ---@return number: Number of investigators currently set on the counter PlayAreaApi.getInvestigatorCount = function() return getInvestigatorCounter().getVar("val") end -- Updates the current value of the investigator counter from the playermat ---@param count number Number of investigators to set on the counter PlayAreaApi.setInvestigatorCount = function(count) getInvestigatorCounter().call("updateVal", count) end -- Move all contents on the play area (cards, tokens, etc) one slot in the given direction. Certain -- fixed objects will be ignored, as will anything the player has tagged with 'displacement_excluded' ---@param playerColor string Color of the player requesting the shift for messages PlayAreaApi.shiftContentsUp = function(playerColor) getPlayArea().call("shiftContentsUp", playerColor) end PlayAreaApi.shiftContentsDown = function(playerColor) getPlayArea().call("shiftContentsDown", playerColor) end PlayAreaApi.shiftContentsLeft = function(playerColor) getPlayArea().call("shiftContentsLeft", playerColor) end PlayAreaApi.shiftContentsRight = function(playerColor) getPlayArea().call("shiftContentsRight", playerColor) end ---@param state boolean This controls whether location connections should be drawn PlayAreaApi.setConnectionDrawState = function(state) getPlayArea().call("setConnectionDrawState", state) end ---@param color string Connection color to be used for location connections PlayAreaApi.setConnectionColor = function(color) getPlayArea().call("setConnectionColor", color) end -- Event to be called when the current scenario has changed ---@param scenarioName string Name of the new scenario PlayAreaApi.onScenarioChanged = function(scenarioName) getPlayArea().call("onScenarioChanged", scenarioName) end -- Sets this playermat's snap points to limit snapping to locations or not. -- If matchTypes is false, snap points will be reset to snap all cards. ---@param matchCardTypes boolean Whether snap points should only snap for the matching card types PlayAreaApi.setLimitSnapsByType = function(matchCardTypes) getPlayArea().call("setLimitSnapsByType", matchCardTypes) end -- Receiver for the Global tryObjectEnterContainer event. Used to clear vector lines from dragged -- cards before they're destroyed by entering the container PlayAreaApi.tryObjectEnterContainer = function(container, object) getPlayArea().call("tryObjectEnterContainer", { container = container, object = object }) end -- Counts the VP on locations in the play area PlayAreaApi.countVP = function() return getPlayArea().call("countVP") end -- Highlights all locations in the play area without metadata ---@param state boolean True if highlighting should be enabled PlayAreaApi.highlightMissingData = function(state) return getPlayArea().call("highlightMissingData", state) end -- Highlights all locations in the play area with VP ---@param state boolean True if highlighting should be enabled PlayAreaApi.highlightCountedVP = function(state) return getPlayArea().call("countVP", state) end -- Checks if an object is in the play area (returns true or false) PlayAreaApi.isInPlayArea = function(object) return getPlayArea().call("isInPlayArea", object) end -- Returns the current surface of the play area PlayAreaApi.getSurface = function() return getPlayArea().getCustomObject().image end -- Updates the surface of the play area PlayAreaApi.updateSurface = function(url) return getPlayArea().call("updateSurface", url) end -- Returns a deep copy of the currently tracked locations PlayAreaApi.getTrackedLocations = function() local t = {} for k, v in pairs(getPlayArea().call("getTrackedLocations", {})) do t[k] = v end return t end -- Called by Custom Data Helpers to push their location data into the Data Helper. This adds the -- data to the local token manager instance. ---@param args table Single-value array holding the GUID of the Custom Data Helper making the call PlayAreaApi.updateLocations = function(args) getPlayArea().call("updateLocations", args) end PlayAreaApi.getCustomDataHelper = function() return getPlayArea().getVar("customDataHelper") end return PlayAreaApi end end) __bundle_register("playermat/PlayermatApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local PlayermatApi = {} local guidReferenceApi = require("core/GUIDReferenceApi") local searchLib = require("util/SearchLib") local localInvestigatorPosition = { x = -1.17, y = 1, z = -0.01 } -- Convenience function to look up a mat's object by color, or get all mats. ---@param matColor string Color of the playermat - White, Orange, Green, Red or All ---@return table: Single-element if only single playermat is requested local function getMatForColor(matColor) if matColor == "All" then return guidReferenceApi.getObjectsByType("Playermat") else return { matColor = guidReferenceApi.getObjectByOwnerAndType(matColor, "Playermat") } end end -- Returns the color of the closest playermat ---@param startPos table Starting position to get the closest mat from PlayermatApi.getMatColorByPosition = function(startPos) local result, smallestDistance for matColor, mat in pairs(getMatForColor("All")) do local distance = Vector.between(startPos, mat.getPosition()):magnitude() if smallestDistance == nil or distance < smallestDistance then smallestDistance = distance result = matColor end end return result end -- Returns the color of the player's hand that is seated next to the playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.getPlayerColor = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("playerColor") end end -- Returns the color of the playermat that owns the playercolor's hand ---@param handColor string Color of the playermat PlayermatApi.getMatColor = function(handColor) for matColor, mat in pairs(getMatForColor("All")) do local playerColor = mat.getVar("playerColor") if playerColor == handColor then return matColor end end end -- Instructs a playermat to check for DES ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.checkForDES = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("checkForDES") end end -- Returns if there is the card "Dream-Enhancing Serum" on the requested playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") ---@return boolean: whether DES is present on the playermat PlayermatApi.hasDES = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("hasDES") end end -- gets the slot data for the playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.getSlotData = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getTable("slotData") end end -- sets the slot data for the playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") ---@param newSlotData table New slot data for the playermat PlayermatApi.loadSlotData = function(matColor, newSlotData) for _, mat in pairs(getMatForColor(matColor)) do mat.setTable("slotData", newSlotData) mat.call("redrawSlotSymbols") return end end -- Performs a search of the deck area of the requested playermat and returns the result as table ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.getDeckAreaObjects = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("getDeckAreaObjects") end end -- Flips the top card of the deck (useful after deck manipulation for Norman Withers) ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.flipTopCardFromDeck = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("flipTopCardFromDeck") end end -- Returns the position of the discard pile of the requested playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.getDiscardPosition = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("returnGlobalDiscardPosition") end end -- Returns the position of the draw pile of the requested playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.getDrawPosition = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("returnGlobalDrawPosition") end end -- Transforms a local position into a global position ---@param localPos table Local position to be transformed ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.transformLocalPosition = function(localPos, matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.positionToWorld(localPos) end end -- Returns the rotation of the requested playermat ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.returnRotation = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getRotation() end end -- Returns a table with spawn data (position and rotation) for a helper object ---@param matColor string Color of the playermat - White, Orange, Green, Red or All ---@param helperName string Name of the helper object PlayermatApi.getHelperSpawnData = function(matColor, helperName) local resultTable = {} local localPositionTable = { ["Hand Helper"] = {0.05, 0, -1.182}, ["Search Assistant"] = {-0.3, 0, -1.182} } for color, mat in pairs(getMatForColor(matColor)) do resultTable[color] = { position = mat.positionToWorld(localPositionTable[helperName]), rotation = mat.getRotation() } end return resultTable end -- Triggers the Upkeep for the requested playermat ---@param matColor string Color of the playermat - White, Orange, Green, Red or All ---@param playerColor string Color of the calling player (for messages) PlayermatApi.doUpkeepFromHotkey = function(matColor, playerColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("doUpkeepFromHotkey", playerColor) end end -- Handles discarding for the requested playermat for the provided list of objects ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") ---@param objList table List of objects to discard PlayermatApi.discardListOfObjects = function(matColor, objList) for _, mat in pairs(getMatForColor(matColor)) do mat.call("discardListOfObjects", objList) end end -- Returns the active investigator id ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.returnInvestigatorId = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("activeInvestigatorId") end end -- Returns the class of the active investigator ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") PlayermatApi.returnInvestigatorClass = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("activeInvestigatorClass") end end -- Returns the position for encounter card drawing ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") ---@param stack boolean If true, returns the leftmost position instead of the first empty from the right PlayermatApi.getEncounterCardDrawPosition = function(matColor, stack) for _, mat in pairs(getMatForColor(matColor)) do return Vector(mat.call("getEncounterCardDrawPosition", stack)) end end -- Sets the requested playermat's snap points to limit snapping to matching card types or not. If -- matchTypes is true, the main card slot snap points will only snap assets, while the -- investigator area point will only snap Investigators. If matchTypes is false, snap points will -- be reset to snap all cards. ---@param matchCardTypes boolean Whether snap points should only snap for the matching card types ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.setLimitSnapsByType = function(matchCardTypes, matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("setLimitSnapsByType", matchCardTypes) end end -- Sets the requested playermat's draw 1 button to visible ---@param isDrawButtonVisible boolean Whether the draw 1 button should be visible or not ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.showDrawButton = function(isDrawButtonVisible, matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("showDrawButton", isDrawButtonVisible) end end -- Shows or hides the clickable clue counter for the requested playermat ---@param showCounter boolean Whether the clickable counter should be present or not ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.clickableClues = function(showCounter, matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("clickableClues", showCounter) end end -- Toggles the use of class textures for the requested playermat ---@param state boolean Whether the class texture should be used or not ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.useClassTexture = function(state, matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("useClassTexture", state) end end -- Removes all clues (to the trash for tokens and counters set to 0) for the requested playermat ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.removeClues = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("removeClues") end end -- Reports the clue count for the requested playermat ---@param useClickableCounters boolean Controls which type of counter is getting checked PlayermatApi.getClueCount = function(useClickableCounters, matColor) local count = 0 for _, mat in pairs(getMatForColor(matColor)) do count = count + mat.call("getClueCount", useClickableCounters) end return count end -- Updates the specified owned counter ---@param matColor string Color of the playermat - White, Orange, Green, Red or All ---@param type string Counter to target ---@param newValue number Value to set the counter to ---@param modifier number If newValue is not provided, the existing value will be adjusted by this modifier PlayermatApi.updateCounter = function(matColor, type, newValue, modifier) for _, mat in pairs(getMatForColor(matColor)) do mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier }) end end -- Triggers the draw function for the specified playermat ---@param matColor string Color of the playermat - White, Orange, Green, Red or All ---@param number number Amount of cards to draw PlayermatApi.drawCardsWithReshuffle = function(matColor, number) for _, mat in pairs(getMatForColor(matColor)) do mat.call("drawCardsWithReshuffle", number) end end -- Returns the resource counter amount ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All") ---@param type string Counter to target PlayermatApi.getCounterValue = function(matColor, type) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("getCounterValue", type) end end -- Returns a list of mat colors that have an investigator placed PlayermatApi.getUsedMatColors = function() local usedColors = {} for matColor, mat in pairs(getMatForColor("All")) do local searchPos = mat.positionToWorld(localInvestigatorPosition) local searchResult = searchLib.atPosition(searchPos, "isCardOrDeck") if #searchResult > 0 then table.insert(usedColors, matColor) end end return usedColors end -- Returns investigator name ---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") PlayermatApi.getInvestigatorName = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do local searchPos = mat.positionToWorld(localInvestigatorPosition) local searchResult = searchLib.atPosition(searchPos, "isCardOrDeck") if #searchResult == 1 then return searchResult[1].getName() end end return "" end -- Resets the specified skill tracker to "1, 1, 1, 1" ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.resetSkillTracker = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("resetSkillTracker") end end -- Redraws the XML for the slot symbols based on the slotData table ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.redrawSlotSymbols = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("redrawSlotSymbols") end end -- Finds all objects on the playermat and associated set aside zone and returns a table ---@param matColor string Color of the playermat - White, Orange, Green, Red or All ---@param filter string Name of the filte function (see util/SearchLib) PlayermatApi.searchAroundPlayermat = function(matColor, filter) local objList = {} for _, mat in pairs(getMatForColor(matColor)) do for _, obj in ipairs(mat.call("searchAroundSelf", filter)) do table.insert(objList, obj) end end return objList end -- Discard a non-hidden card from the corresponding player's hand ---@param matColor string Color of the playermat - White, Orange, Green, Red or All PlayermatApi.doDiscardOne = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("doDiscardOne") end end -- Triggers the metadata sync for all playermats PlayermatApi.syncAllCustomizableCards = function() for _, mat in pairs(getMatForColor("All")) do mat.call("syncAllCustomizableCards") end end return PlayermatApi end end) __bundle_register("chaosbag/BlessCurseManagerApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local BlessCurseManagerApi = {} local guidReferenceApi = require("core/GUIDReferenceApi") local function getManager() return guidReferenceApi.getObjectByOwnerAndType("Mythos", "BlessCurseManager") end -- removes all taken tokens and resets the counts BlessCurseManagerApi.removeTakenTokensAndReset = function() local BlessCurseManager = getManager() Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Bless") end, 0.05) Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Curse") end, 0.10) Wait.time(function() BlessCurseManager.call("doReset", "White") end, 0.15) end -- updates the internal count (called by cards that seal bless/curse tokens) ---@param type string Type of chaos token ("Bless" or "Curse") ---@param guid string GUID of the token BlessCurseManagerApi.sealedToken = function(type, guid) getManager().call("sealedToken", { type = type, guid = guid }) end -- updates the internal count (called by cards that seal bless/curse tokens) ---@param type string Type of chaos token ("Bless" or "Curse") ---@param guid string GUID of the token ---@param fromBag? boolean Whether or not token was just drawn from the chaos bag BlessCurseManagerApi.releasedToken = function(type, guid, fromBag) getManager().call("releasedToken", { type = type, guid = guid, fromBag = fromBag }) end -- updates the internal count (called by cards that seal bless/curse tokens) ---@param type string Type of chaos token ("Bless" or "Curse") ---@param guid string GUID of the token BlessCurseManagerApi.returnedToken = function(type, guid) getManager().call("returnedToken", { type = type, guid = guid }) end -- broadcasts the current status for bless/curse tokens ---@param playerColor string Color of the player to show the broadcast to BlessCurseManagerApi.broadcastStatus = function(playerColor) getManager().call("broadcastStatus", playerColor) end -- removes all bless / curse tokens from the chaos bag and play ---@param playerColor string Color of the player to show the broadcast to BlessCurseManagerApi.removeAll = function(playerColor) getManager().call("doRemove", playerColor) end -- adds bless / curse sealing to the hovered card ---@param playerColor string Color of the player to show the broadcast to ---@param hoveredObject tts__Object Hovered object BlessCurseManagerApi.addBlurseSealingMenu = function(playerColor, hoveredObject) getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject }) end -- adds bless / curse to the chaos bag ---@param type string Type of chaos token ("Bless" or "Curse") BlessCurseManagerApi.addToken = function(type) getManager().call("addToken", type) end -- removes bless / curse from the chaos bag ---@param type string Type of chaos token ("Bless" or "Curse") BlessCurseManagerApi.removeToken = function(type) getManager().call("removeToken", type) end BlessCurseManagerApi.getBlessCurseInBag = function() return getManager().call("getBlessCurseInBag", {}) end return BlessCurseManagerApi end end) __bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local GUIDReferenceApi = {} local function getGuidHandler() return getObjectFromGUID("123456") end -- Returns the matching object ---@param owner string Parent object for this search ---@param type string Type of object to search for ---@return any: Object reference to the matching object GUIDReferenceApi.getObjectByOwnerAndType = function(owner, type) return getGuidHandler().call("getObjectByOwnerAndType", { owner = owner, type = type }) end -- Returns all matching objects as a table with references ---@param type string Type of object to search for ---@return table: List of object references to matching objects GUIDReferenceApi.getObjectsByType = function(type) return getGuidHandler().call("getObjectsByType", type) end -- Returns all matching objects as a table with references ---@param owner string Parent object for this search ---@return table: List of object references to matching objects GUIDReferenceApi.getObjectsByOwner = function(owner) return getGuidHandler().call("getObjectsByOwner", owner) end -- Sends new information to the reference handler to edit the main index ---@param owner string Parent of the object ---@param type string Type of the object ---@param guid string GUID of the object GUIDReferenceApi.editIndex = function(owner, type, guid) return getGuidHandler().call("editIndex", { owner = owner, type = type, guid = guid }) end -- Returns the owner of an object or the object it's located on ---@param object tts__GameObject Object for this search ---@return string: Parent of the object or object it's located on GUIDReferenceApi.getOwnerOfObject = function(object) return getGuidHandler().call("getOwnerOfObject", object) end return GUIDReferenceApi end end) __bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local OptionPanelApi = {} -- loads saved options ---@param options table Set a new state for the option table OptionPanelApi.loadSettings = function(options) return Global.call("loadSettings", options) end ---@return any: Table of option panel state OptionPanelApi.getOptions = function() return Global.getTable("optionPanel") end return OptionPanelApi end end) __bundle_register("util/SearchLib", function(require, _LOADED, __bundle_register, __bundle_modules) do local SearchLib = {} local filterFunctions = { isCard = function(x) return x.type == "Card" end, isDeck = function(x) return x.type == "Deck" end, isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end, isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end, isTileOrToken = function(x) return x.type == "Tile" end, isUniversalToken = function(x) return x.getMemo() == "universalActionAbility" end, } -- performs the actual search and returns a filtered list of object references ---@param pos tts__Vector Global position ---@param rot? tts__Vector Global rotation ---@param size table Size ---@param filter? string Name of the filter function ---@param direction? table Direction (positive is up) ---@param maxDistance? number Distance for the cast local function returnSearchResult(pos, rot, size, filter, direction, maxDistance) local filterFunc if filter then filterFunc = filterFunctions[filter] end local searchResult = Physics.cast({ origin = pos, direction = direction or { 0, 1, 0 }, orientation = rot or { 0, 0, 0 }, type = 3, size = size, max_distance = maxDistance or 0 }) -- filter the result for matching objects local objList = {} for _, v in ipairs(searchResult) do if not filter or filterFunc(v.hit_object) then table.insert(objList, v.hit_object) end end return objList end -- searches the specified area SearchLib.inArea = function(pos, rot, size, filter) return returnSearchResult(pos, rot, size, filter) end -- searches the area on an object SearchLib.onObject = function(obj, filter) local pos = obj.getPosition() local size = obj.getBounds().size:setAt("y", 1) return returnSearchResult(pos, _, size, filter) end -- searches the specified position (a single point) SearchLib.atPosition = function(pos, filter) local size = { 0.1, 2, 0.1 } return returnSearchResult(pos, _, size, filter) end -- searches below the specified position (downwards until y = 0) SearchLib.belowPosition = function(pos, filter) local size = { 0.1, 2, 0.1 } local direction = { 0, -1, 0 } local maxDistance = pos.y return returnSearchResult(pos, _, size, filter, direction, maxDistance) end return SearchLib end end) return __bundle_require("__root")