ah_sce_unpacked/unpacked/Bag All Player Cards 15bb07/CardCustom Living Ink Upgrade Sheet 19a05b.ttslua
2024-01-06 21:32:07 -05:00

788 lines
24 KiB
Plaintext

-- 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("playercards/customizable/LivingInkUpgradeSheet")
end)
__bundle_register("playercards/customizable/LivingInkUpgradeSheet", function(require, _LOADED, __bundle_register, __bundle_modules)
-- Customizable Cards: Living Ink
-- Size information for buttons
boxSize = 40
-- static values
xInitial = -0.935
xOffset = 0.075
-- Locations of the skill selectors
SKILL_ICON_POSITIONS = {
willpower = { x = 0.085, z = -0.88 },
intellect = { x = -0.183, z = -0.88 },
combat = { x = -0.473, z = -0.88 },
agility = { x = -0.74, z = -0.88 },
}
customizations = {
[1] = { }, -- Empty placeholder for skill selection row, handled by custom skill display
[2] = {
checkboxes = {
posZ = -0.69,
count = 1,
}
},
[3] = {
checkboxes = {
posZ = -0.355,
count = 1,
}
},
[4] = {
checkboxes = {
posZ = 0.0855,
count = 2,
}
},
[5] = {
checkboxes = {
posZ = 0.425,
count = 2,
}
},
[6] = {
checkboxes = {
posZ = 0.555,
count = 3,
},
},
[7] = {
checkboxes = {
posZ = 0.685,
count = 3,
}
},
[8] = {
checkboxes = {
posZ = 1.02,
count = 3,
},
},
}
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)
__bundle_register("playermat/PlaymatApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local PlaymatApi = { }
local internal = { }
local MAT_IDS = {
White = "8b081b",
Orange = "bd0ff4",
Green = "383d8b",
Red = "0840d5"
}
local CLUE_COUNTER_GUIDS = {
White = "37be78",
Orange = "1769ed",
Green = "032300",
Red = "d86b7c"
}
local CLUE_CLICKER_GUIDS = {
White = "db85d6",
Orange = "3f22e5",
Green = "891403",
Red = "4111de"
}
-- Returns the color of the by position requested playermat as string
---@param startPos Table Position of the search, table get's roughly cut into 4 quarters to assign a playermat
PlaymatApi.getMatColorByPosition = function(startPos)
if startPos.x < -42 then
if startPos.z > 0 then
return "White"
else
return "Orange"
end
else
if startPos.z > 0 then
return "Green"
else
return "Red"
end
end
end
-- Returns the color of the player's hand that is seated next to the playermat
---@param matColor String Color of the playermat
PlaymatApi.getPlayerColor = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.getVar("playerColor")
end
-- Returns the color of the playermat that owns the playercolor's hand
---@param handColor String Color of the playermat
PlaymatApi.getMatColor = function(handColor)
local matColors = {"White", "Orange", "Green", "Red"}
for i, mat in ipairs(internal.getMatForColor("All")) do
local color = mat.getVar("playerColor")
if color == handColor then return matColors[i] end
end
return "NOT_FOUND"
end
-- Returns the result of a cast in the specificed playermat's area
---@param matColor String Color of the playermat
PlaymatApi.searchPlaymat = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.call("searchAroundSelf")
end
-- Returns if there is the card "Dream-Enhancing Serum" on the requested playermat
---@param matColor String Color of the playermat
PlaymatApi.isDES = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.getVar("isDES")
end
-- Returns the draw deck of the requested playmat
---@param matColor String Color of the playermat
PlaymatApi.getDrawDeck = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
mat.call("getDrawDiscardDecks")
return mat.getVar("drawDeck")
end
-- Returns the position of the discard pile of the requested playmat
---@param matColor String Color of the playermat
PlaymatApi.getDiscardPosition = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.call("returnGlobalDiscardPosition")
end
-- Transforms a local position into a global position
---@param localPos Table Local position to be transformed
---@param matColor String Color of the playermat
PlaymatApi.transformLocalPosition = function(localPos, matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.positionToWorld(localPos)
end
-- Returns the rotation of the requested playmat
---@param matColor String Color of the playermat
PlaymatApi.returnRotation = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.getRotation()
end
-- Triggers the Upkeep for the requested playmat
---@param matColor String Color of the playermat
---@param playerColor String Color of the calling player (for messages)
PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.call("doUpkeepFromHotkey", playerColor)
end
-- Returns the active investigator id
---@param matColor String Color of the playermat
PlaymatApi.returnInvestigatorId = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.getVar("activeInvestigatorId")
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 for one of the active player colors - White, Orange, Green, Red. Also
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor)
for _, mat in ipairs(internal.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 for one of the active player colors - White, Orange, Green, Red. Also
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.showDrawButton = function(isDrawButtonVisible, matColor)
for _, mat in ipairs(internal.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 for one of the active player colors - White, Orange, Green, Red. Also
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.clickableClues = function(showCounter, matColor)
for _, mat in ipairs(internal.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 playermat
---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.removeClues = function(matColor)
for _, mat in ipairs(internal.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
PlaymatApi.getClueCount = function(useClickableCounters, matColor)
local count = 0
for _, mat in ipairs(internal.getMatForColor(matColor)) do
count = count + tonumber(mat.call("getClueCount", useClickableCounters))
end
return count
end
-- Adds the specified amount of resources to the requested playermat's resource counter
PlaymatApi.gainResources = function(amount, matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do
mat.call("gainResources", amount)
end
end
-- Returns the resource counter amount for the requested playermat
PlaymatApi.getResourceCount = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.call("getResourceCount")
end
-- Discard a non-hidden card from the corresponding player's hand
PlaymatApi.doDiscardOne = function(matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do
mat.call("doDiscardOne")
end
end
PlaymatApi.syncAllCustomizableCards = function()
for _, mat in ipairs(internal.getMatForColor("All")) do
mat.call("syncAllCustomizableCards")
end
end
PlaymatApi.updateClueClicker = function(playerColor, val)
return getObjectFromGUID(CLUE_CLICKER_GUIDS[playerColor]).call("updateVal", val)
end
-- Convenience function to look up a mat's object by color, or get all mats.
---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also
-- accepts "All" as a special value which will return all four mats.
---@return: Array of playermat objects. If a single mat is requested, will return a single-element
-- array to simplify processing by consumers.
internal.getMatForColor = function(matColor)
local targetMatGuid = MAT_IDS[matColor]
if targetMatGuid != nil then
return { getObjectFromGUID(targetMatGuid) }
end
if matColor == "All" then
return {
getObjectFromGUID(MAT_IDS.White),
getObjectFromGUID(MAT_IDS.Orange),
getObjectFromGUID(MAT_IDS.Green),
getObjectFromGUID(MAT_IDS.Red),
}
end
end
return PlaymatApi
end
end)
return __bundle_require("__root")