-- 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 campaignTokenData = { GUID = "51b1c9", Name = "Custom_Model", Transform = { posX = -21.25, posY = 1.68, posZ = 55.59, rotX = 0, rotY = 270, rotZ = 0, scaleX = 2, scaleY = 2, scaleZ = 2 }, Nickname = "Arkham Coin", Description = "SCED Importer Token", GMNotes = "", Tags = { "ImporterToken" }, CustomMesh = { MeshURL = "http://cloud-3.steamusercontent.com/ugc/943949966265929204/A38BB5D72419E6298385556D931877C0A1A55C17/", DiffuseURL = "http://cloud-3.steamusercontent.com/ugc/254843371583188147/920981125E37B5CEB6C400E3FD353A2C428DA969/", NormalURL = "", ColliderURL = "http://cloud-3.steamusercontent.com/ugc/943949966265929204/A38BB5D72419E6298385556D931877C0A1A55C17/", Convex = true, MaterialIndex = 2, TypeIndex = 0, CustomShader = { SpecularColor = { r = 0.7222887, g = 0.507659256, b = 0.339915335 }, SpecularIntensity = 0.4, SpecularSharpness = 7.0, FresnelStrength = 0.0 }, CastShadows = true } } -- counter GUIDS (4x damage and 4x horror) local DAMAGE_HORROR_GUIDS = { "eb08d6"; "e64eec"; "1f5a0a"; "591a45"; "468e88"; "0257d9"; "7b5729"; "beb964"; } local chaosBagApi = require("chaosbag/ChaosBagApi") local playAreaApi = require("core/PlayAreaApi") local deckImporterApi = require("arkhamdb/DeckImporterApi") local optionPanelApi = require("core/OptionPanelApi") local blessCurseApi = require("chaosbag/BlessCurseManagerApi") local TOUR_GUID = "0e5aa8" local campaignBoxGUID function onLoad(save_state) campaignBoxGUID = "" self.createButton({ click_function = "findCampaignFromToken", function_owner = self, label = "Import", tooltip = "Load in a campaign save from a token!\n\n(Token can be anywhere on the table, but ensure there is only 1!)", position = {x=-1, y=0.2, z=0}, font_size = 400, width = 1400, height = 600, scale = {0.5, 1, 0.5}, }) self.createButton({ click_function = "createCampaignToken", function_owner = self, label = "Export", tooltip = "Create a campaign save token!\n\n(Ensure all chaos tokens have been unsealed!)", position = {x=1, y=0.2, z=0}, font_size = 400, width = 1400, height = 600, scale = {0.5, 1, 0.5}, }) end -- The main import functions. Due to timing concerns, has to be split up into several separate methods to allow for Wait conditions -- Identifies import token, determines campaign box and downloads it (if needed) function findCampaignFromToken(_, _, _) local coin = nil local coinObjects = getObjectsWithTag("ImporterToken") if #coinObjects == 0 then broadcastToAll("Could not find importer token", Color.Red) elseif #coinObjects > 1 then broadcastToAll("More than 1 importer token found. Please delete all but 1 importer token", Color.Yellow) else coin = coinObjects[1] local importData = JSON.decode(coin.getGMNotes()) campaignBoxGUID = importData["box"] local campaignBox = getObjectFromGUID(campaignBoxGUID) if campaignBox.type == "Generic" then campaignBox.call("buttonClick_download") end Wait.condition( function() if #campaignBox.getObjects() > 0 then placeCampaignFromToken(importData) else createCampaignFromToken(importData) end end, function() local obj = getObjectFromGUID(campaignBoxGUID) if obj == nil then return false else return obj.type == "Bag" and obj.getLuaScript() ~= "" end end, 2, function() broadcastToAll("Error loading campaign box") end ) end end -- After box has been downloaded, places content on table function placeCampaignFromToken(importData) getObjectFromGUID(campaignBoxGUID).call("buttonClick_place") Wait.condition( function() createCampaignFromToken(importData) end, function() return findCampaignLog() ~= nil end, 2, function() broadcastToAll("Error placing campaign box") end ) end -- After content is placed on table, conducts all the other import operations function createCampaignFromToken(importData) findCampaignLog().destruct() --create campaign log spawnObjectData({data = importData["log"]}) --create chaos bag chaosBagApi.setChaosBagState(importData["bag"]) --populate trauma values if importData["trauma"] then updateCounters(importData["trauma"]) end --populate ArkhamDB deck IDs if importData["decks"] then deckImporterApi.setUiState(importData["decks"]) end --set investigator count playAreaApi.setInvestigatorCount(importData["clueCount"]) --set campaign guide page local guide = findCampaignGuide() if guide then Wait.condition( -- Called after the condition function returns true function() log("Campaign Guide import successful!") end, -- Condition function that is called continiously until returs true or timeout is reached function() guide.Book.setPage(importData["guide"]) return guide.Book.getPage() == importData["guide"] end, -- Amount of time in seconds until the Wait times out 1, -- Called if the Wait times out function() log("Campaign Guide import failed!") end ) end Wait.time( function() optionPanelApi.loadSettings(importData["options"]) end, 0.5 ) getObjectFromGUID(TOUR_GUID).destruct() playAreaApi.updateSurface(importData["playmat"]) broadcastToAll("Campaign successfully imported!", Color.Green) end -- Creates a campaign token with save data encoded into GM Notes based on the current state of the table function createCampaignToken(_, playerColor, _) -- clean up chaos tokens blessCurseApi.removeAll(playerColor) chaosBagApi.releaseAllSealedTokens(playerColor) local campaignBoxGUID = "" -- find active campaign for _, obj in ipairs(getObjectsWithTag("CampaignBox")) do if obj.type == "Bag" and #obj.getObjects() == 0 then if campaignBoxGUID ~= "" then broadcastToAll("Multiple empty campaign box detected; delete all but one.", Color.Red) return end campaignBoxGUID = obj.getGUID() end end if campaignBoxGUID == "" then broadcastToAll("Campaign box with all placed objects not found!", Color.Red) return end local campaignLog = findCampaignLog() if campaignLog == nil then broadcastToAll("Campaign log not found!", Color.Red) return end local traumaValues = nil local counterData = campaignLog.getVar("ref_buttonData") if counterData ~= nil then traumaValues = {} printToAll("Trauma values found in campaign log!", "Green") for i = 1, 10, 3 do traumaValues[1 + (i - 1) / 3] = counterData.counter[i].value traumaValues[5 + (i - 1) / 3] = counterData.counter[i + 1].value end else printToAll("Trauma values could not be found in campaign log!", "Yellow") printToAll("Default values for health and sanity loaded.", "Yellow") end local campaignGuide = findCampaignGuide() if campaignGuide == nil then broadcastToAll("Campaign guide not found!", Color.Red) return end local campaignGuidePage = campaignGuide.Book.getPage() local campaignData = { box = campaignBoxGUID, log = campaignLog.getData(), bag = chaosBagApi.getChaosBagState(), trauma = traumaValues, decks = deckImporterApi.getUiState(), clueCount = playAreaApi.getInvestigatorCount(), guide = campaignGuidePage, options = optionPanelApi.getOptions(), playmat = playAreaApi.getSurface() } campaignTokenData.GMNotes = JSON.encode(campaignData) campaignTokenData.Nickname = os.date("%b %d ") .. getObjectFromGUID(campaignBoxGUID).getName() .. " Save" spawnObjectData({ data = campaignTokenData, position = {-21.25, 1.68, 55.59} }) broadcastToAll("Campaign successfully exported! Save coin object to import on a fresh save", Color.Green) end -- helper functions function findCampaignLog() local campaignLog = getObjectsWithTag("CampaignLog") if campaignLog then if #campaignLog == 1 then return campaignLog[1] else broadcastToAll("More than 1 campaign log detected; delete all but one.", Color.Red) return nil end else return nil end end function findCampaignGuide() local campaignGuide = getObjectsWithTag("CampaignGuide") if campaignGuide then if #campaignGuide == 1 then return campaignGuide[1] else broadcastToAll("More than 1 campaign guide detected; delete all but one.", Color.Red) return nil end else return nil end end function updateCounters(tableOfNewValues) if tonumber(tableOfNewValues) then local value = tableOfNewValues tableOfNewValues = {} for i = 1, #DAMAGE_HORROR_GUIDS do table.insert(tableOfNewValues, value) end end for i, guid in ipairs(DAMAGE_HORROR_GUIDS) do local TOKEN = getObjectFromGUID(guid) if TOKEN ~= nil then TOKEN.call("updateVal", tableOfNewValues[i]) else printToAll(": No. " .. i .. " could not be found.", "Yellow") end end end end) __bundle_register("arkhamdb/DeckImporterApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local DeckImporterApi = {} local DECK_IMPORTER_GUID = "a28140" -- 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 -- Table values: -- redDeck: Deck ID to load for the red player -- orangeDeck: Deck ID to load for the orange player -- whiteDeck: Deck ID to load for the white player -- greenDeck: Deck ID to load for the green player -- private: True to load a private deck, false to load a public deck -- loadNewest: True if the most upgraded version of the deck should be loaded -- investigators: True if investigator cards should be spawned DeckImporterApi.getUiState = function() local passthroughTable = {} for k,v in pairs(getObjectFromGUID(DECK_IMPORTER_GUID).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. -- @param uiStateTable Table of values to update on importer -- Table values: -- redDeck: Deck ID to load for the red player -- orangeDeck: Deck ID to load for the orange player -- whiteDeck: Deck ID to load for the white player -- greenDeck: Deck ID to load for the green player -- private: True to load a private deck, false to load a public deck -- loadNewest: True if the most upgraded version of the deck should be loaded -- investigators: True if investigator cards should be spawned DeckImporterApi.setUiState = function(uiStateTable) return getObjectFromGUID(DECK_IMPORTER_GUID).call("setUiState", uiStateTable) end return DeckImporterApi end end) __bundle_register("chaosbag/BlessCurseManagerApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local BlessCurseManagerApi = {} local MANAGER_GUID = "5933fb" -- removes all taken tokens and resets the counts BlessCurseManagerApi.removeTakenTokensAndReset = function() local BlessCurseManager = getObjectFromGUID(MANAGER_GUID) 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) BlessCurseManagerApi.sealedToken = function(type, guid) getObjectFromGUID(MANAGER_GUID).call("sealedToken", { type = type, guid = guid }) end -- updates the internal count (called by cards that seal bless/curse tokens) BlessCurseManagerApi.releasedToken = function(type, guid) getObjectFromGUID(MANAGER_GUID).call("releasedToken", { 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) getObjectFromGUID(MANAGER_GUID).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) getObjectFromGUID(MANAGER_GUID).call("doRemove", playerColor) end -- adds Wendy's menu to the hovered card (allows sealing of tokens) ---@param color String Color of the player to show the broadcast to BlessCurseManagerApi.addWendysMenu = function(playerColor, hoveredObject) getObjectFromGUID(MANAGER_GUID).call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject }) end return BlessCurseManagerApi 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 all sealed tokens on cards to the chaos bag ChaosBagApi.releaseAllSealedTokens = function(playerColor) return Global.call("releaseAllSealedTokens", playerColor) end return ChaosBagApi end end) __bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local OptionPanelApi = {} -- loads saved options ---@param options Table New options table OptionPanelApi.loadSettings = function(options) return Global.call("loadSettings", options) end -- returns option panel table OptionPanelApi.getOptions = function() return Global.getTable("optionPanel") end return OptionPanelApi end end) __bundle_register("core/PlayAreaApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local PlayAreaApi = { } local PLAY_AREA_GUID = "721ba2" local IMAGE_SWAPPER = "b7b45b" -- Returns the current value of the investigator counter from the playmat ---@return Integer. Number of investigators currently set on the counter PlayAreaApi.getInvestigatorCount = function() return getObjectFromGUID(PLAY_AREA_GUID).call("getInvestigatorCount") end -- Updates the current value of the investigator counter from the playmat ---@param count Number of investigators to set on the counter PlayAreaApi.setInvestigatorCount = function(count) return getObjectFromGUID(PLAY_AREA_GUID).call("setInvestigatorCount", 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 Color of the player requesting the shift. Used solely to send an error --- message in the unlikely case that the scripting zone has been deleted PlayAreaApi.shiftContentsUp = function(playerColor) return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsUp", playerColor) end PlayAreaApi.shiftContentsDown = function(playerColor) return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsDown", playerColor) end PlayAreaApi.shiftContentsLeft = function(playerColor) return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsLeft", playerColor) end PlayAreaApi.shiftContentsRight = function(playerColor) return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsRight", playerColor) end -- Reset the play area's tracking of which cards have had tokens spawned. PlayAreaApi.resetSpawnedCards = function() return getObjectFromGUID(PLAY_AREA_GUID).call("resetSpawnedCards") end -- Event to be called when the current scenario has changed. ---@param scenarioName Name of the new scenario PlayAreaApi.onScenarioChanged = function(scenarioName) getObjectFromGUID(PLAY_AREA_GUID).call("onScenarioChanged", scenarioName) end -- Sets this playmat's snap points to limit snapping to locations or not. -- If matchTypes is false, snap points will be reset to snap all cards. ---@param matchTypes Boolean Whether snap points should only snap for the matching card types. PlayAreaApi.setLimitSnapsByType = function(matchCardTypes) getObjectFromGUID(PLAY_AREA_GUID).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) getObjectFromGUID(PLAY_AREA_GUID).call("tryObjectEnterContainer", { container = container, object = object }) end -- counts the VP on locations in the play area PlayAreaApi.countVP = function() return getObjectFromGUID(PLAY_AREA_GUID).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 getObjectFromGUID(PLAY_AREA_GUID).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 getObjectFromGUID(PLAY_AREA_GUID).call("highlightCountedVP", state) end -- Checks if an object is in the play area (returns true or false) PlayAreaApi.isInPlayArea = function(object) return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object) end PlayAreaApi.getSurface = function() return getObjectFromGUID(PLAY_AREA_GUID).getCustomObject().image end PlayAreaApi.updateSurface = function(url) return getObjectFromGUID(IMAGE_SWAPPER).call("updateSurface", url) end return PlayAreaApi end end) return __bundle_require("__root")