SCED/src/playercards/PlayerCardPanel.ttslua
Buhallin c33d0386b7
Update Unified Player Card panel to use relative positioning
Because of the mix of global and relative positioning this ended up being very complex.  Tried to make sure the comments were thorough, but open to any improvement suggestions.
2023-01-06 16:20:09 -08:00

758 lines
26 KiB
Plaintext

require("playercards/PlayerCardPanelData")
local spawnBag = require("playercards/spawnbag/SpawnBag")
local arkhamDb = require("arkhamdb/ArkhamDb")
-- Size and position information for the three rows of class buttons
local CIRCLE_BUTTON_SIZE = 250
local CLASS_BUTTONS_X_OFFSET = 0.1325
local INVESTIGATOR_ROW_START = Vector(0.125, 0.1, -0.447)
local LEVEL_ZERO_ROW_START = Vector(0.125, 0.1, -0.007)
local UPGRADED_ROW_START = Vector(0.125, 0.1, 0.333)
-- Size and position information for the two blocks of other buttons
local MISC_BUTTONS_X_OFFSET = 0.155
local WEAKNESS_ROW_START = Vector(0.157, 0.1, 0.666)
local OTHER_ROW_START = Vector(0.605, 0.1, 0.666)
-- Size and position information for the Cycle (box) buttons
local CYCLE_BUTTON_SIZE = 468
local CYCLE_BUTTON_START = Vector(-0.716, 0.1, -0.39)
local CYCLE_COLUMN_COUNT = 3
local CYCLE_BUTTONS_X_OFFSET = 0.267
local CYCLE_BUTTONS_Z_OFFSET = 0.2665
local ALL_CARDS_BAG_GUID = "15bb07"
local STARTER_DECK_MODE_SELECTED_COLOR = { 0.2, 0.2, 0.2, 0.8 }
local TRANSPARENT = { 0, 0, 0, 0 }
local STARTER_DECK_MODE_STARTERS = "starters"
local STARTER_DECK_MODE_CARDS_ONLY = "cards"
local FACE_UP_ROTATION = { x = 0, y = 270, z = 0}
local FACE_DOWN_ROTATION = { x = 0, y = 270, z = 180}
-- ---------- IMPORTANT ----------
-- Coordinates defined below are in global dimensions relative to the panel - DO NOT USE THESE
-- DIRECTLY. Call scalePositions() before use, and reference the variables below
-- Layout width for a single card, in global coordinate space
local CARD_WIDTH = 2.3
-- Coordinates to begin laying out cards. These vary based on the cards that are being placed by
-- considering the width of the cards, number of cards, and desired spread intervals.
-- IMPORTANT! Because of the mix of global card sizes and relative-to-scale positions, the X and Y
-- coordinates on these provide global disances while the Z is local.
local START_POSITIONS = {
classCards = Vector(CARD_WIDTH * 9.5, 2, 1.4),
investigator = Vector(6 * 2.5, 2, 1.3),
cycle = Vector(CARD_WIDTH * 9.5, 2, 2.4),
other = Vector(CARD_WIDTH * 9.5, 2, 1.4),
randomWeakness = Vector(0, 2, 1.4),
-- Because the card spread is handled by the SpawnBag, we don't know (programatically) where this
-- should be placed. If more customizable cards are added it will need to be moved.
summonedServitor = Vector(CARD_WIDTH * -6.5, 2, 1.7),
}
-- Shifts to move rows of cards, and groups of rows, as different groupings are laid out
local CARD_ROW_OFFSET = 3.7
local CARD_GROUP_OFFSET = 2
-- Position offsets for investigator decks in investigator mode, defines the spacing for how the
-- rows and columns are laid out
local INVESTIGATOR_POSITION_SHIFT_ROW = Vector(0, 0, 11)
local INVESTIGATOR_POSITION_SHIFT_COL = Vector(-6, 0, 0)
local INVESTIGATOR_MAX_COLS = 6
-- Positions relative to the minicard to place other stacks. Both signature card piles and starter
-- decks use SIGNATURE_OFFSET
local INVESTIGATOR_CARD_OFFSET = Vector(0, 0, 2.55)
local INVESTIGATOR_SIGNATURE_OFFSET = Vector(0, 0, 5.75)
-- USE THESE! Positions and offset shifts accounting for the scale of the panel
local startPositions
local cardRowOffset
local cardGroupOffset
local investigatorPositionShiftRow
local investigatorPositionShiftCol
local investigatorCardOffset
local investigatorSignatureOffset
local CLASS_LIST = { "Guardian", "Seeker", "Rogue", "Mystic", "Survivor", "Neutral" }
local CYCLE_LIST = {
"Core",
"The Dunwich Legacy",
"The Path to Carcosa",
"The Forgotten Age",
"The Circle Undone",
"The Dream-Eaters",
"The Innsmouth Conspiracy",
"Edge of the Earth",
"The Scarlet Keys",
"Investigator Packs"
}
local excludedNonBasicWeaknesses
local starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY
local helpVisibleToPlayers = { }
function onSave()
local saveState = {
spawnBagState = spawnBag.getStateForSave(),
}
return JSON.encode(saveState)
end
function onLoad(savedData)
arkhamDb.initialize()
if (savedData ~= nil) then
local saveState = JSON.decode(savedData) or { }
if (saveState.spawnBagState ~= nil) then
spawnBag.loadFromSave(saveState.spawnBagState)
end
end
buildExcludedWeaknessList()
createButtons()
end
-- Build a list of non-basic weaknesses which should be excluded from the last weakness set,
-- including all signature cards and evolved weaknesses.
function buildExcludedWeaknessList()
excludedNonBasicWeaknesses = { }
for _, investigator in pairs(INVESTIGATORS) do
for _, signatureId in ipairs(investigator.signatures) do
excludedNonBasicWeaknesses[signatureId] = true
end
end
for _, weaknessId in ipairs(EVOLVED_WEAKNESSES) do
excludedNonBasicWeaknesses[weaknessId] = true
end
end
function createButtons()
createHelpButton()
createInvestigatorButtons()
createLevelZeroButtons()
createUpgradedButtons()
createWeaknessButtons()
createOtherButtons()
createCycleButtons()
createClearButton()
-- Create investigator mode buttons last so the indexes are set when we need to update them
createInvestigatorModeButtons()
end
function createHelpButton()
self.createButton({
function_owner = self,
click_function = "toggleHelp",
position = Vector(0.845, 0.1, -0.855),
rotation = Vector(0, 0, 0),
height = 180,
width = 180,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
})
end
function createInvestigatorButtons()
local invButtonParams = {
function_owner = self,
rotation = Vector(0, 0, 0),
height = CIRCLE_BUTTON_SIZE,
width = CIRCLE_BUTTON_SIZE,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
}
local buttonPos = INVESTIGATOR_ROW_START:copy()
for _, class in ipairs(CLASS_LIST) do
invButtonParams.click_function = "spawnInvestigators" .. class
invButtonParams.position = buttonPos
self.createButton(invButtonParams)
buttonPos.x = buttonPos.x + CLASS_BUTTONS_X_OFFSET
self.setVar(invButtonParams.click_function, function(_, _, _) spawnInvestigatorGroup(class) end)
end
end
function createLevelZeroButtons()
local l0ButtonParams = {
function_owner = self,
rotation = Vector(0, 0, 0),
height = CIRCLE_BUTTON_SIZE,
width = CIRCLE_BUTTON_SIZE,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
}
local buttonPos = LEVEL_ZERO_ROW_START:copy()
for _, class in ipairs(CLASS_LIST) do
l0ButtonParams.click_function = "spawnBasic" .. class
l0ButtonParams.position = buttonPos
self.createButton(l0ButtonParams)
buttonPos.x = buttonPos.x + CLASS_BUTTONS_X_OFFSET
self.setVar(l0ButtonParams.click_function, function(_, _, _) spawnClassCards(class, false) end)
end
end
function createUpgradedButtons()
local upgradedButtonParams = {
function_owner = self,
rotation = Vector(0, 0, 0),
height = CIRCLE_BUTTON_SIZE,
width = CIRCLE_BUTTON_SIZE,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
}
local buttonPos = UPGRADED_ROW_START:copy()
for _, class in ipairs(CLASS_LIST) do
upgradedButtonParams.click_function = "spawnUpgraded" .. class
upgradedButtonParams.position = buttonPos
self.createButton(upgradedButtonParams)
buttonPos.x = buttonPos.x + CLASS_BUTTONS_X_OFFSET
self.setVar(upgradedButtonParams.click_function, function(_, _, _) spawnClassCards(class, true) end)
end
end
function createWeaknessButtons()
local weaknessButtonParams = {
function_owner = self,
rotation = Vector(0, 0, 0),
height = CIRCLE_BUTTON_SIZE,
width = CIRCLE_BUTTON_SIZE,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
}
local buttonPos = WEAKNESS_ROW_START:copy()
weaknessButtonParams.click_function = "spawnWeaknesses"
weaknessButtonParams.tooltip = "All Weaknesses"
weaknessButtonParams.position = buttonPos
self.createButton(weaknessButtonParams)
buttonPos.x = buttonPos.x + MISC_BUTTONS_X_OFFSET
weaknessButtonParams.click_function = "spawnRandomWeakness"
weaknessButtonParams.tooltip = "Random Basic Weakness"
weaknessButtonParams.position = buttonPos
self.createButton(weaknessButtonParams)
end
function createOtherButtons()
local otherButtonParams = {
function_owner = self,
rotation = Vector(0, 0, 0),
height = CIRCLE_BUTTON_SIZE,
width = CIRCLE_BUTTON_SIZE,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
}
local buttonPos = OTHER_ROW_START:copy()
otherButtonParams.click_function = "spawnBonded"
otherButtonParams.tooltip = "Bonded Cards"
otherButtonParams.position = buttonPos
self.createButton(otherButtonParams)
buttonPos.x = buttonPos.x + MISC_BUTTONS_X_OFFSET
otherButtonParams.click_function = "spawnUpgradeSheets"
otherButtonParams.tooltip = "Customization Upgrade Sheets"
otherButtonParams.position = buttonPos
self.createButton(otherButtonParams)
end
function createCycleButtons()
local cycleButtonParams = {
function_owner = self,
rotation = Vector(0, 0, 0),
height = CYCLE_BUTTON_SIZE,
width = CYCLE_BUTTON_SIZE,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
}
local buttonPos = CYCLE_BUTTON_START:copy()
local rowCount = 0
local colCount = 0
for _, cycle in ipairs(CYCLE_LIST) do
cycleButtonParams.click_function = "spawnCycle" .. cycle
cycleButtonParams.position = buttonPos
cycleButtonParams.tooltip = cycle
self.createButton(cycleButtonParams)
self.setVar(cycleButtonParams.click_function, function(_, _, _) spawnCycle(cycle) end)
colCount = colCount + 1
-- If we've reached the end of a row, shift down and back to the first column
if colCount >= CYCLE_COLUMN_COUNT then
buttonPos = CYCLE_BUTTON_START:copy()
rowCount = rowCount + 1
colCount = 0
buttonPos.z = buttonPos.z + CYCLE_BUTTONS_Z_OFFSET * rowCount
if rowCount == 3 then
-- Account for centered button on the final row
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
end
else
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
end
end
end
function createClearButton()
self.createButton({
function_owner = self,
click_function = "deleteAll",
position = Vector(0, 0.1, 0.852),
rotation = Vector(0, 0, 0),
height = 170,
width = 750,
scale = Vector(0.25, 1, 0.25),
color = TRANSPARENT,
})
end
function createInvestigatorModeButtons()
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
self.createButton({
function_owner = self,
click_function = "setCardsOnlyMode",
position = Vector(0.251, 0.1, -0.322),
rotation = Vector(0, 0, 0),
height = 170,
width = 760,
scale = Vector(0.25, 1, 0.25),
color = starterMode and TRANSPARENT or STARTER_DECK_MODE_SELECTED_COLOR
})
self.createButton({
function_owner = self,
click_function = "setStarterDeckMode",
position = Vector(0.66, 0.1, -0.322),
rotation = Vector(0, 0, 0),
height = 170,
width = 760,
scale = Vector(0.25, 1, 0.25),
color = starterMode and STARTER_DECK_MODE_SELECTED_COLOR or TRANSPARENT
})
local checkX = starterMode and 0.52 or 0.11
self.createButton({
function_owner = self,
label = "✓",
click_function = "doNothing",
position = Vector(checkX, 0.11, -0.317),
rotation = Vector(0, 0, 0),
height = 0,
width = 0,
scale = Vector(0.3, 1, 0.3),
font_color = { 0, 0, 0 },
color = { 1, 1, 1 }
})
end
function toggleHelp(_, playerColor, _)
if helpVisibleToPlayers[playerColor] then
helpVisibleToPlayers[playerColor] = nil
else
helpVisibleToPlayers[playerColor] = true
end
updateHelpVisibility()
end
function updateHelpVisibility()
local visibility = ""
for player, _ in pairs(helpVisibleToPlayers) do
if string.len(visibility) > 0 then
visibility = visibility .. "|" .. player
else
visibility = player
end
end
self.UI.setAttribute("helpText", "visibility", visibility)
self.UI.setAttribute("helpPanel", "visibility", visibility)
self.UI.setAttribute("helpPanel", "active", string.len(visibility) > 0)
end
function setStarterDeckMode()
starterDeckMode = STARTER_DECK_MODE_STARTERS
updateStarterModeButtons()
end
function setCardsOnlyMode()
starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY
updateStarterModeButtons()
end
function updateStarterModeButtons()
local buttonCount = #self.getButtons()
-- Buttons are 0-indexed, so the last three are -1, -2, and -3 from the size
self.removeButton(buttonCount - 1)
self.removeButton(buttonCount - 2)
self.removeButton(buttonCount - 3)
createInvestigatorModeButtons()
end
-- Clears the table and updates positions based on scale. Should be called before ANY card
-- placement
function prepareToPlaceCards()
deleteAll()
scalePositions()
end
-- Updates the positions based on the current object scale to ensure the relative layout functions
-- properly at different scales.
function scalePositions()
-- Assume scaling is consistent in X and Z dimensions
local scale = 1 / self.getScale().x
startPositions = { }
for key, pos in pairs(START_POSITIONS) do
-- Because a scaled object means a different global size, using global distance for Z results in
-- the cards being closer or farther depending on the scale. Leave the Z values and only scale
-- X and Y
startPositions[key] = Vector(pos)
startPositions[key].x = startPositions[key].x * scale
startPositions[key].y = startPositions[key].y * scale
end
cardRowOffset = CARD_ROW_OFFSET * scale
cardGroupOffset = CARD_GROUP_OFFSET * scale
investigatorPositionShiftRow = Vector(INVESTIGATOR_POSITION_SHIFT_ROW):scale(scale)
investigatorPositionShiftCol = Vector(INVESTIGATOR_POSITION_SHIFT_COL):scale(scale)
investigatorCardOffset = Vector(INVESTIGATOR_CARD_OFFSET):scale(scale)
investigatorSignatureOffset = Vector(INVESTIGATOR_SIGNATURE_OFFSET):scale(scale)
end
-- Deletes all cards currently placed on the table
function deleteAll()
spawnBag.recall(true)
end
-- Spawn an investigator group, based on the current UI setting for either investigators or starter
-- decks.
---@param groupName String. Name of the group to spawn, matching a key in InvestigatorPanelData
function spawnInvestigatorGroup(groupName)
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
prepareToPlaceCards()
Wait.frames(function()
if starterMode then
spawnStarters(groupName)
else
spawnInvestigators(groupName)
end
end, 2)
end
-- Spawn cards for all investigators in the given group. This creates piles for all defined
-- investigator cards and minicards as well as the signature cards.
---@param groupName String. Name of the group to spawn, matching a key in InvestigatorPanelData
function spawnInvestigators(groupName)
if INVESTIGATOR_GROUPS[groupName] == nil then
printToAll("No " .. groupName .. " data yet")
return
end
local col = 1
local row = 1
local investigatorCount = #INVESTIGATOR_GROUPS[groupName]
local position = getInvestigatorRowStartPos(investigatorCount, row)
for i, investigatorName in ipairs(INVESTIGATOR_GROUPS[groupName]) do
for _, spawnSpec in ipairs(buildInvestigatorSpawnSpec(
investigatorName, INVESTIGATORS[investigatorName], position, false)) do
spawnBag.spawn(spawnSpec)
end
position:add(investigatorPositionShiftCol)
col = col + 1
if col > INVESTIGATOR_MAX_COLS then
col = 1
row = row + 1
position = getInvestigatorRowStartPos(investigatorCount, row)
end
end
end
function getInvestigatorRowStartPos(investigatorCount, row)
local rowStart = Vector(startPositions.investigator)
rowStart:add(Vector(
investigatorPositionShiftRow.x * (row - 1),
investigatorPositionShiftRow.y * (row - 1),
investigatorPositionShiftRow.z * (row - 1)))
local investigatorsInRow =
math.min(investigatorCount - INVESTIGATOR_MAX_COLS * (row - 1), INVESTIGATOR_MAX_COLS)
rowStart:add(Vector(
investigatorPositionShiftCol.x * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2,
investigatorPositionShiftCol.y * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2,
investigatorPositionShiftCol.z * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2))
return rowStart
end
-- Creates the spawn spec for the investigator's signature cards.
---@param investigatorName String. Name of the investigator, matching a key in
--- InvestigatorPanelData
---@param investigatorData Table. Spawn definition for the investigator, retrieved from
--- INVESTIGATORS
---@param position Vector. Where to spawn the minicard; investigagor cards will be placed below
function buildInvestigatorSpawnSpec(investigatorName, investigatorData, position)
local sigPos = Vector(position):add(investigatorSignatureOffset)
local spawns = buildCommonSpawnSpec(investigatorName, investigatorData, position)
table.insert(spawns, {
name = investigatorName.."signatures",
cards = investigatorData.signatures,
globalPos = self.positionToWorld(sigPos),
rotation = FACE_UP_ROTATION,
})
return spawns
end
-- Builds the spawn specs for minicards and investigator cards. These are common enough to be
-- shared, and will only differ in whether they spawn the full stack of possible investigator and
-- minicards, or only the first of each.
---@param investigatorName String. Name of the investigator, matching a key in
--- InvestigatorPanelData
---@param investigatorData Table. Spawn definition for the investigator, retrieved from
--- INVESTIGATORS
---@param position Vector. Where to spawn the minicard; investigagor cards will be placed below
---@param oneCardOnly Boolean. If true, will spawn only the first card in the investigator card
--- and minicard lists. Otherwise, spawn them all in a deck
function buildCommonSpawnSpec(investigatorName, investigatorData, position, oneCardOnly)
local cardPos = Vector(position):add(investigatorCardOffset)
return {
{
name = investigatorName.."minicards",
cards = oneCardOnly and { investigatorData.minicards[1] } or investigatorData.minicards,
globalPos = self.positionToWorld(position),
rotation = FACE_UP_ROTATION,
},
{
name = investigatorName.."cards",
cards = oneCardOnly and { investigatorData.cards[1] } or investigatorData.cards,
globalPos = self.positionToWorld(cardPos),
rotation = FACE_UP_ROTATION,
},
}
end
-- Spawns all starter decks (single minicard and investigator card, plus the starter deck) for
-- investigators in the given group.
---@param groupName String. Name of the group to spawn, matching a key in InvestigatorPanelData
function spawnStarters(groupName)
local col = 1
local row = 1
local investigatorCount = #INVESTIGATOR_GROUPS[groupName]
local position = getInvestigatorRowStartPos(investigatorCount, row)
for _, investigatorName in ipairs(INVESTIGATOR_GROUPS[groupName]) do
spawnStarterDeck(investigatorName, INVESTIGATORS[investigatorName], position)
position:add(investigatorPositionShiftCol)
col = col + 1
if col > INVESTIGATOR_MAX_COLS then
col = 1
row = row + 1
position = getInvestigatorRowStartPos(investigatorCount, row)
end
end
end
-- Spawns the defined starter deck for the given investigator's.
---@param investigatorName String. Name of the investigator, matching a key in
--- InvestigatorPanelData
function spawnStarterDeck(investigatorName, investigatorData, position)
for _, spawnSpec in ipairs(
buildCommonSpawnSpec(investigatorName, INVESTIGATORS[investigatorName], position, true)) do
spawnBag.spawn(spawnSpec)
end
local deckPos = Vector(position):add(investigatorSignatureOffset)
arkhamDb.getDecklist("None", investigatorData.starterDeck, true, false, false, function(slots)
local cardIdList = { }
for id, count in pairs(slots) do
for i = 1, count do
table.insert(cardIdList, id)
end
end
spawnBag.spawn({
name = investigatorName.."starter",
cards = cardIdList,
globalPos = self.positionToWorld(deckPos),
rotation = FACE_DOWN_ROTATION
})
end)
end
-- Clears the currently placed cards, then places cards for the given class and level spread
---@param cardClass String. Class to place ("Guardian", "Seeker", etc)
---@param isUpgraded Boolean. If true, spawn the Level 1-5 cards. Otherwise, Level 0.
function spawnClassCards(cardClass, isUpgraded)
prepareToPlaceCards()
Wait.frames(function() placeClassCards(cardClass, isUpgraded) end, 2)
end
-- Spawn the class cards.
---@param cardClass String. Class to place ("Guardian", "Seeker", etc)
---@param isUpgraded Boolean. If true, spawn the Level 1-5 cards. Otherwise, Level 0.
function placeClassCards(cardClass, isUpgraded)
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
local indexReady = allCardsBag.call("isIndexReady")
if (not indexReady) then
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return
end
local cardIdList = allCardsBag.call("getCardsByClassAndLevel", {class = cardClass, upgraded = isUpgraded})
local skillList = { }
local eventList = { }
local assetList = { }
for _, cardId in ipairs(cardIdList) do
local cardMetadata = allCardsBag.call("getCardById", { id = cardId }).metadata
if (cardMetadata.type == "Skill") then
table.insert(skillList, cardId)
elseif (cardMetadata.type == "Event") then
table.insert(eventList, cardId)
elseif (cardMetadata.type == "Asset") then
table.insert(assetList, cardId)
end
end
local groupPos = Vector(startPositions.classCards)
if #skillList > 0 then
spawnBag.spawn({
name = cardClass .. (isUpgraded and "upgraded" or "basic"),
cards = skillList,
globalPos = self.positionToWorld(groupPos),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
groupPos.z = groupPos.z + math.ceil(#skillList / 20) * cardRowOffset + cardGroupOffset
end
if #eventList > 0 then
spawnBag.spawn({
name = cardClass .. "event" .. (isUpgraded and "upgraded" or "basic"),
cards = eventList,
globalPos = self.positionToWorld(groupPos),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
groupPos.z = groupPos.z + math.ceil(#eventList / 20) * cardRowOffset + cardGroupOffset
end
if #assetList > 0 then
spawnBag.spawn({
name = cardClass .. "asset" .. (isUpgraded and "upgraded" or "basic"),
cards = assetList,
globalPos = self.positionToWorld(groupPos),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
end
end
-- Spawns the investigator sets and all cards for the given cycle
---@param cycle String Name of a cycle, should match the standard used in card metadata
function spawnCycle(cycle)
prepareToPlaceCards()
spawnInvestigators(cycle)
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
local indexReady = allCardsBag.call("isIndexReady")
if (not indexReady) then
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return
end
local cycleCardList = allCardsBag.call("getCardsByCycle", cycle)
local copiedList = { }
for i, id in ipairs(cycleCardList) do
copiedList[i] = id
end
spawnBag.spawn({
name = "cycle"..cycle,
cards = copiedList,
globalPos = self.positionToWorld(startPositions.cycle),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
end
function spawnBonded()
prepareToPlaceCards()
spawnBag.spawn({
name = "bonded",
cards = BONDED_CARD_LIST,
globalPos = self.positionToWorld(startPositions.classCards),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
end
function spawnUpgradeSheets()
prepareToPlaceCards()
spawnBag.spawn({
name = "upgradeSheets",
cards = UPGRADE_SHEET_LIST,
globalPos = self.positionToWorld(startPositions.classCards),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
spawnBag.spawn({
name = "servitor",
cards = { "09080-m" },
globalPos = self.positionToWorld(startPositions.summonedServitor),
rotation = FACE_UP_ROTATION,
})
end
-- Clears the current cards, and places all basic weaknesses on the table.
function spawnWeaknesses()
prepareToPlaceCards()
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
local indexReady = allCardsBag.call("isIndexReady")
if (not indexReady) then
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return
end
local weaknessIdList = allCardsBag.call("getUniqueWeaknesses")
local basicWeaknessList = { }
local otherWeaknessList = { }
for i, id in ipairs(weaknessIdList) do
local cardMetadata = allCardsBag.call("getCardById", { id = id }).metadata
if cardMetadata.basicWeaknessCount ~= nil and cardMetadata.basicWeaknessCount > 0 then
table.insert(basicWeaknessList, id)
elseif excludedNonBasicWeaknesses[id] == nil then
table.insert(otherWeaknessList, id)
end
end
local groupPos = Vector(startPositions.classCards)
spawnBag.spawn({
name = "basicWeaknesses",
cards = basicWeaknessList,
globalPos = self.positionToWorld(groupPos),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
groupPos.z = groupPos.z + math.ceil(#basicWeaknessList / 20) * cardRowOffset + cardGroupOffset
spawnBag.spawn({
name = "evolvedWeaknesses",
cards = EVOLVED_WEAKNESSES,
globalPos = self.positionToWorld(groupPos),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
groupPos.z = groupPos.z + math.ceil(#EVOLVED_WEAKNESSES / 20) * cardRowOffset + cardGroupOffset
spawnBag.spawn({
name = "otherWeaknesses",
cards = otherWeaknessList,
globalPos = self.positionToWorld(groupPos),
rotation = FACE_UP_ROTATION,
spread = true,
spreadCols = 20
})
end
function spawnRandomWeakness()
prepareToPlaceCards()
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
local weaknessId = allCardsBag.call("getRandomWeaknessId")
if (weaknessId == nil) then
broadcastToAll("All basic weaknesses are in play!", {0.9, 0.2, 0.2})
return
end
spawnBag.spawn({
name = "randomWeakness",
cards = { weaknessId },
globalPos = self.positionToWorld(startPositions.randomWeakness),
rotation = FACE_UP_ROTATION,
})
end