-- 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("playermat/PlaymatApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local PlaymatApi = {} local guidReferenceApi = require("core/GUIDReferenceApi") -- Convenience function to look up a mat's object by color, or get all mats. ---@param matColor String Color of the playmat - White, Orange, Green, Red or All ---@return array Table Single-element if only single playmat 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 playmat ---@param startPos Table Starting position to get the closest mat from PlaymatApi.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 playmat ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.getPlayerColor = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("playerColor") end end -- Returns the color of the playmat that owns the playercolor's hand ---@param handColor String Color of the playmat PlaymatApi.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 -- Returns if there is the card "Dream-Enhancing Serum" on the requested playmat ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.isDES = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("isDES") end end -- Performs a search of the deck area of the requested playmat and returns the result as table ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.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 playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.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 playmat ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.getDiscardPosition = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("returnGlobalDiscardPosition") end end -- Transforms a local position into a global position ---@param localPos Table Local position to be transformed ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.transformLocalPosition = function(localPos, matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.positionToWorld(localPos) end end -- Returns the rotation of the requested playmat ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.returnRotation = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getRotation() end end -- Triggers the Upkeep for the requested playmat ---@param matColor String Color of the playmat - White, Orange, Green, Red or All ---@param playerColor String Color of the calling player (for messages) PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("doUpkeepFromHotkey", playerColor) end end -- Handles discarding for the requested playmat for the provided list of objects ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param objList Table List of objects to discard PlaymatApi.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 playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.returnInvestigatorId = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do return mat.getVar("activeInvestigatorId") end end -- Sets the requested playmat'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 playmat - White, Orange, Green, Red or All PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("setLimitSnapsByType", matchCardTypes) end end -- Sets the requested playmat'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 playmat - White, Orange, Green, Red or All PlaymatApi.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 playmat ---@param showCounter Boolean Whether the clickable counter should be present or not ---@param matColor String Color of the playmat - White, Orange, Green, Red or All PlaymatApi.clickableClues = function(showCounter, matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("clickableClues", showCounter) end end -- Removes all clues (to the trash for tokens and counters set to 0) for the requested playmat ---@param matColor String Color of the playmat - White, Orange, Green, Red or All PlaymatApi.removeClues = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("removeClues") end end -- Reports the clue count for the requested playmat ---@param useClickableCounters Boolean Controls which type of counter is getting checked PlaymatApi.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 playmat - 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 PlaymatApi.updateCounter = function(matColor, type, newValue, modifier) for _, mat in pairs(getMatForColor(matColor)) do mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier }) end end -- returns the resource counter amount ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param type String Counter to target PlaymatApi.getCounterValue = function(matColor, type) for _, mat in pairs(getMatForColor(matColor)) do return mat.call("getCounterValue", type) end end -- resets the specified skill tracker to "1, 1, 1, 1" ---@param matColor String Color of the playmat - White, Orange, Green, Red or All PlaymatApi.resetSkillTracker = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("resetSkillTracker") end end -- finds all objects on the playmat and associated set aside zone and returns a table ---@param matColor String Color of the playmat - White, Orange, Green, Red or All ---@param filter Function Optional filter function (return true for desired objects) PlaymatApi.searchAroundPlaymat = 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 playmat - White, Orange, Green, Red or All PlaymatApi.doDiscardOne = function(matColor) for _, mat in pairs(getMatForColor(matColor)) do mat.call("doDiscardOne") end end -- Triggers the metadata sync for all playmats PlaymatApi.syncAllCustomizableCards = function() for _, mat in pairs(getMatForColor("All")) do mat.call("syncAllCustomizableCards") end end return PlaymatApi end end) __bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules) do local GUIDReferenceApi = {} local function getGuidHandler() return getObjectFromGUID("123456") end -- returns all matching objects as a table with references ---@param owner String Parent object for this search ---@param type String Type of object to search for 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 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 GUIDReferenceApi.getObjectsByOwner = function(owner) return getGuidHandler().call("getObjectsByOwner", owner) end return GUIDReferenceApi end end) __bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules) require("playercards/customizable/RunicAxeUpgradeSheetTaboo") end) __bundle_register("playercards/customizable/RunicAxeUpgradeSheetTaboo", function(require, _LOADED, __bundle_register, __bundle_modules) -- Customizable Cards: Runic Axe (Taboo) -- Color information for buttons boxSize = 38 -- static values xInitial = -0.935 xOffset = 0.0705 customizations = { [1] = { checkboxes = { posZ = -0.92, count = 1, } }, [2] = { checkboxes = { posZ = -0.715, count = 1, } }, [3] = { checkboxes = { posZ = -0.415, count = 1, } }, [4] = { checkboxes = { posZ = -0.018, count = 2, } }, [5] = { checkboxes = { posZ = 0.265, count = 1, }, }, [6] = { checkboxes = { posZ = 0.66, count = 3, } }, [7] = { checkboxes = { posZ = 0.86, count = 3, }, }, [8] = { checkboxes = { posZ = 1.065, count = 4, }, }, } require("playercards/customizable/UpgradeSheetLibrary") end) __bundle_register("playercards/customizable/UpgradeSheetLibrary", function(require, _LOADED, __bundle_register, __bundle_modules) -- Common code for handling customizable card upgrade sheets -- Define UI elements in the base card file, then include this -- UI element definition is an array of tables, each with this structure. A row may include -- checkboxes (number defined by count), a text field, both, or neither (if the row has custom -- handling, as Living Ink does) -- { -- checkboxes = { -- posZ = -0.71, -- count = 1, -- }, -- textField = { -- position = { 0.005, 0.25, -0.58 }, -- width = 875 -- } -- } -- Fields should also be defined for xInitial (left edge of the checkboxes) and xOffset (amount to -- shift X from one box to the next) as well as boxSize (checkboxes) and inputFontSize. -- -- selectedUpgrades holds the state of checkboxes and text input, each element being: -- selectedUpgrades[row] = { xp = #, text = "" } local playmatApi = require("playermat/PlaymatApi") -- Y position for UI elements. Visibility of checkboxes moves the checkbox inside the card object -- when not selected. local Y_VISIBLE = 0.25 local Y_INVISIBLE = -0.5 -- Used for Summoned Servitor and Living Ink local VECTOR_COLOR = { unselected = { 0.5, 0.5, 0.5, 0.75 }, mystic = { 0.597, 0.195, 0.796 } } -- These match with ArkhamDB's way of storing the data in the dropdown menu local SUMMONED_SERVITOR_SLOT_INDICES = { arcane = "1", ally = "0", none = "" } local rowCheckboxFirstIndex = { } local rowInputIndex = { } local selectedUpgrades = { } -- save state when going into bags / decks function onDestroy() self.script_state = onSave() end function onSave() return JSON.encode({ selections = selectedUpgrades }) end -- Startup procedure function onLoad(savedData) if savedData ~= "" then local loadedData = JSON.decode(savedData) if loadedData.selections ~= nil then selectedUpgrades = loadedData.selections end end selfId = getSelfId() maybeLoadLivingInkSkills() createUi() maybeUpdateLivingInkSkillDisplay() maybeUpdateServitorSlotDisplay() self.addContextMenuItem("Clear Selections", function() resetSelections() end) self.addContextMenuItem("Scale: 1x", function() self.setScale({ 1, 1, 1 }) end) self.addContextMenuItem("Scale: 2x", function() self.setScale({ 2, 1, 2 }) end) self.addContextMenuItem("Scale: 3x", function() self.setScale({ 3, 1, 3 }) end) end -- Grabs the ID from the metadata for special functions (Living Ink, Summoned Servitor) function getSelfId() local metadata = JSON.decode(self.getGMNotes()) return metadata.id end function isUpgradeActive(row) return customizations[row] ~= nil and customizations[row].checkboxes ~= nil and customizations[row].checkboxes.count ~= nil and customizations[row].checkboxes.count > 0 and selectedUpgrades[row] ~= nil and selectedUpgrades[row].xp ~= nil and selectedUpgrades[row].xp >= customizations[row].checkboxes.count end function resetSelections() selectedUpgrades = { } updateDisplay() end function createUi() if customizations == nil then return end for i = 1, #customizations do if customizations[i].checkboxes ~= nil then createRowCheckboxes(i) end if customizations[i].textField ~= nil then createRowTextField(i) end end maybeMakeLivingInkSkillSelectionButtons() maybeMakeServitorSlotSelectionButtons() updateDisplay() end function createRowCheckboxes(rowIndex) local checkboxes = customizations[rowIndex].checkboxes rowCheckboxFirstIndex[rowIndex] = 0 local previousButtons = self.getButtons() if previousButtons ~= nil then rowCheckboxFirstIndex[rowIndex] = #previousButtons end for col = 1, checkboxes.count do local funcName = "checkboxRow" .. rowIndex .. "Col" .. col local func = function() clickCheckbox(rowIndex, col) end self.setVar(funcName, func) local checkboxPos = getCheckboxPosition(rowIndex, col) self.createButton({ click_function = funcName, function_owner = self, position = checkboxPos, height = boxSize * 10, width = boxSize * 10, font_size = 1000, scale = { 0.1, 0.1, 0.1 }, color = { 0, 0, 0 }, font_color = { 0, 0, 0 } }) end end function getCheckboxPosition(row, col) return { x = xInitial + col * xOffset, y = Y_VISIBLE, z = customizations[row].checkboxes.posZ } end function createRowTextField(rowIndex) local textField = customizations[rowIndex].textField rowInputIndex[rowIndex] = 0 local previousInputs = self.getInputs() if previousInputs ~= nil then rowInputIndex[rowIndex] = #previousInputs end local funcName = "textbox" .. rowIndex local func = function(_, _, val, sel) clickTextbox(rowIndex, val, sel) end self.setVar(funcName, func) self.createInput({ input_function = funcName, function_owner = self, label = "Click to type", alignment = 2, position = textField.position, scale = { 0.1, 0.1, 0.1 }, width = textField.width * 10, height = inputFontsize * 10 + 75, font_size = inputFontsize * 10.5, color = "White", value = "" }) end function updateDisplay() for i = 1, #customizations do updateRowDisplay(i) end maybeUpdateLivingInkSkillDisplay() maybeUpdateServitorSlotDisplay() end function updateRowDisplay(rowIndex) if customizations[rowIndex].checkboxes ~= nil then updateCheckboxes(rowIndex) end if customizations[rowIndex].textField ~= nil then updateTextField(rowIndex) end end function updateCheckboxes(rowIndex) local checkboxCount = customizations[rowIndex].checkboxes.count local selected = 0 if selectedUpgrades[rowIndex] ~= nil and selectedUpgrades[rowIndex].xp ~= nil then selected = selectedUpgrades[rowIndex].xp end local checkboxIndex = rowCheckboxFirstIndex[rowIndex] for col = 1, checkboxCount do local pos = getCheckboxPosition(rowIndex, col) if col <= selected then pos.y = Y_VISIBLE else pos.y = Y_INVISIBLE end self.editButton({ index = checkboxIndex, position = pos }) checkboxIndex = checkboxIndex + 1 end end function updateTextField(rowIndex) local inputIndex = rowInputIndex[rowIndex] if selectedUpgrades[rowIndex] ~= nil and selectedUpgrades[rowIndex].text ~= nil then self.editInput({ index = inputIndex, value = " " .. selectedUpgrades[rowIndex].text }) end end function clickCheckbox(row, col, buttonIndex) if selectedUpgrades[row] == nil then selectedUpgrades[row] = { } selectedUpgrades[row].xp = 0 end if selectedUpgrades[row].xp == col then selectedUpgrades[row].xp = col - 1 else selectedUpgrades[row].xp = col end updateCheckboxes(row) playmatApi.syncAllCustomizableCards() end -- Updates saved value for given text box when it loses focus function clickTextbox(rowIndex, value, selected) if selected == false then if selectedUpgrades[rowIndex] == nil then selectedUpgrades[rowIndex] = { } end selectedUpgrades[rowIndex].text = value:gsub("^%s*(.-)%s*$", "%1") -- Editing isn't actually done yet, and will block the update. Wait a frame so it's finished Wait.frames(function() updateRowDisplay(rowIndex) end, 1) end end --------------------------------------------------------- -- Living Ink related functions --------------------------------------------------------- -- Builds the list of boolean skill selections from the Row 1 text field function maybeLoadLivingInkSkills() if selfId ~= "09079-c" then return end selectedSkills = { willpower = false, intellect = false, combat = false, agility = false } if selectedUpgrades[1] ~= nil and selectedUpgrades[1].text ~= nil then for skill in string.gmatch(selectedUpgrades[1].text, "([^,]+)") do selectedSkills[skill] = true end end end function clickSkill(skillname) selectedSkills[skillname] = not selectedSkills[skillname] maybeUpdateLivingInkSkillDisplay() updateSelectedLivingInkSkillText() end -- Creates the invisible buttons overlaying the skill icons function maybeMakeLivingInkSkillSelectionButtons() if selfId ~= "09079-c" then return end local buttonData = { function_owner = self, position = { y = 0.2 }, height = 130, width = 130, color = { 0, 0, 0, 0 }, } for skillname, _ in pairs(selectedSkills) do local funcName = "clickSkill" .. skillname self.setVar(funcName, function() clickSkill(skillname) end) buttonData.click_function = funcName buttonData.position.x = -1 * SKILL_ICON_POSITIONS[skillname].x buttonData.position.z = SKILL_ICON_POSITIONS[skillname].z self.createButton(buttonData) end end -- Builds a comma-delimited string of skills and places it in the Row 1 text field function updateSelectedLivingInkSkillText() local skillString = "" if selectedSkills.willpower then skillString = skillString .. "willpower" .. "," end if selectedSkills.intellect then skillString = skillString .. "intellect" .. "," end if selectedSkills.combat then skillString = skillString .. "combat" .. "," end if selectedSkills.agility then skillString = skillString .. "agility" .. "," end if selectedUpgrades[1] == nil then selectedUpgrades[1] = { } end selectedUpgrades[1].text = skillString end -- Refresh the vector circles indicating a skill is selected. Since we can only have one table of -- vectors set, have to refresh all 4 at once function maybeUpdateLivingInkSkillDisplay() if selfId ~= "09079-c" then return end local circles = {} for skill, isSelected in pairs(selectedSkills) do if isSelected then local circle = getCircleVector(SKILL_ICON_POSITIONS[skill]) if circle ~= nil then table.insert(circles, circle) end end end self.setVectorLines(circles) end function getCircleVector(center) local diameter = Vector(0, 0, 0.1) local pointOfOrigin = Vector(center.x, Y_VISIBLE, center.z) local vec local vecList = {} local arcStep = 5 for i = 0, 360, arcStep do diameter:rotateOver('y', arcStep) vec = pointOfOrigin + diameter vec.y = pointOfOrigin.y table.insert(vecList, vec) end return { points = vecList, color = VECTOR_COLOR.mystic, thickness = 0.02, } end --------------------------------------------------------- -- Summoned Servitor related functions --------------------------------------------------------- -- Creates the invisible buttons overlaying the slot words function maybeMakeServitorSlotSelectionButtons() if selfId ~= "09080-c" then return end local buttonData = { click_function = "clickArcane", function_owner = self, position = { x = -1 * SLOT_ICON_POSITIONS.arcane.x, y = 0.2, z = SLOT_ICON_POSITIONS.arcane.z }, height = 130, width = 130, color = { 0, 0, 0, 0 }, } self.createButton(buttonData) buttonData.click_function = "clickAlly" buttonData.position.x = -1 * SLOT_ICON_POSITIONS.ally.x self.createButton(buttonData) end -- toggles the clicked slot function clickArcane() if selectedUpgrades[6] == nil then selectedUpgrades[6] = { } end if selectedUpgrades[6].text == SUMMONED_SERVITOR_SLOT_INDICES.arcane then selectedUpgrades[6].text = SUMMONED_SERVITOR_SLOT_INDICES.none else selectedUpgrades[6].text = SUMMONED_SERVITOR_SLOT_INDICES.arcane end maybeUpdateServitorSlotDisplay() end -- toggles the clicked slot function clickAlly() if selectedUpgrades[6] == nil then selectedUpgrades[6] = { } end if selectedUpgrades[6].text == SUMMONED_SERVITOR_SLOT_INDICES.ally then selectedUpgrades[6].text = SUMMONED_SERVITOR_SLOT_INDICES.none else selectedUpgrades[6].text = SUMMONED_SERVITOR_SLOT_INDICES.ally end maybeUpdateServitorSlotDisplay() end -- Refresh the vector circles indicating a slot is selected. function maybeUpdateServitorSlotDisplay() if selfId ~= "09080-c" then return end local center = SLOT_ICON_POSITIONS["arcane"] local arcaneVecList = { Vector(center.x + 0.12, Y_VISIBLE, center.z + 0.05), Vector(center.x - 0.12, Y_VISIBLE, center.z + 0.05), Vector(center.x - 0.12, Y_VISIBLE, center.z - 0.05), Vector(center.x + 0.12, Y_VISIBLE, center.z - 0.05), Vector(center.x + 0.12, Y_VISIBLE, center.z + 0.05), } center = SLOT_ICON_POSITIONS["ally"] local allyVecList = { Vector(center.x + 0.07, Y_VISIBLE, center.z + 0.05), Vector(center.x - 0.07, Y_VISIBLE, center.z + 0.05), Vector(center.x - 0.07, Y_VISIBLE, center.z - 0.05), Vector(center.x + 0.07, Y_VISIBLE, center.z - 0.05), Vector(center.x + 0.07, Y_VISIBLE, center.z + 0.05), } local arcaneVecColor = VECTOR_COLOR.unselected local allyVecColor = VECTOR_COLOR.unselected if selectedUpgrades[6] ~= nil and selectedUpgrades[6].text == SUMMONED_SERVITOR_SLOT_INDICES.arcane then arcaneVecColor = VECTOR_COLOR.mystic elseif selectedUpgrades[6] ~= nil and selectedUpgrades[6].text == SUMMONED_SERVITOR_SLOT_INDICES.ally then allyVecColor = VECTOR_COLOR.mystic end self.setVectorLines({ { points = arcaneVecList, color = arcaneVecColor, thickness = 0.02, }, { points = allyVecList, color = allyVecColor, thickness = 0.02, } }) end end) return __bundle_require("__root")