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.
This commit is contained in:
parent
ca63ab14c2
commit
c33d0386b7
@ -31,14 +31,26 @@ 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}
|
||||
|
||||
-- Coordinates to begin laying out cards. These vary based on the cards that are being placed
|
||||
-- ---------- 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(58.384, 1.36, 92.4),
|
||||
investigator = Vector(60, 1.36, 86),
|
||||
cycle = Vector(48, 1.36, 92.4),
|
||||
other = Vector(56, 1.36, 86),
|
||||
summonedServitor = Vector(55.5, 1.36, 60.2),
|
||||
randomWeakness = Vector(55, 1.36, 75)
|
||||
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
|
||||
@ -47,14 +59,23 @@ 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(-11, 0, 0)
|
||||
local INVESTIGATOR_POSITION_SHIFT_COL = Vector(0, 0, -6)
|
||||
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(-2.55, 0, 0)
|
||||
local INVESTIGATOR_SIGNATURE_OFFSET = Vector(-5.75, 0, 0)
|
||||
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 = {
|
||||
@ -361,6 +382,35 @@ function updateStarterModeButtons()
|
||||
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)
|
||||
@ -371,7 +421,7 @@ end
|
||||
---@param groupName String. Name of the group to spawn, matching a key in InvestigatorPanelData
|
||||
function spawnInvestigatorGroup(groupName)
|
||||
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
Wait.frames(function()
|
||||
if starterMode then
|
||||
spawnStarters(groupName)
|
||||
@ -385,32 +435,47 @@ end
|
||||
-- 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)
|
||||
local position = Vector(START_POSITIONS.investigator)
|
||||
local col = 1
|
||||
local row = 1
|
||||
if INVESTIGATOR_GROUPS[groupName] == nil then
|
||||
printToAll("No " .. groupName .. " data yet")
|
||||
return
|
||||
end
|
||||
for _, investigatorName in ipairs(INVESTIGATOR_GROUPS[groupName]) do
|
||||
|
||||
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(INVESTIGATOR_POSITION_SHIFT_COL)
|
||||
position:add(investigatorPositionShiftCol)
|
||||
col = col + 1
|
||||
if col > INVESTIGATOR_MAX_COLS then
|
||||
col = 1
|
||||
position = Vector(START_POSITIONS.investigator)
|
||||
position:add(Vector(
|
||||
INVESTIGATOR_POSITION_SHIFT_ROW.x * row,
|
||||
INVESTIGATOR_POSITION_SHIFT_ROW.z * row,
|
||||
INVESTIGATOR_POSITION_SHIFT_ROW.z * row))
|
||||
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
|
||||
@ -418,12 +483,12 @@ end
|
||||
--- 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(INVESTIGATOR_SIGNATURE_OFFSET)
|
||||
local sigPos = Vector(position):add(investigatorSignatureOffset)
|
||||
local spawns = buildCommonSpawnSpec(investigatorName, investigatorData, position)
|
||||
table.insert(spawns, {
|
||||
name = investigatorName.."signatures",
|
||||
cards = investigatorData.signatures,
|
||||
globalPos = sigPos,
|
||||
globalPos = self.positionToWorld(sigPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
})
|
||||
|
||||
@ -441,18 +506,18 @@ end
|
||||
---@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(INVESTIGATOR_CARD_OFFSET)
|
||||
local cardPos = Vector(position):add(investigatorCardOffset)
|
||||
return {
|
||||
{
|
||||
name = investigatorName.."minicards",
|
||||
cards = oneCardOnly and { investigatorData.minicards[1] } or investigatorData.minicards,
|
||||
globalPos = position,
|
||||
globalPos = self.positionToWorld(position),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
},
|
||||
{
|
||||
name = investigatorName.."cards",
|
||||
cards = oneCardOnly and { investigatorData.cards[1] } or investigatorData.cards,
|
||||
globalPos = cardPos,
|
||||
globalPos = self.positionToWorld(cardPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
},
|
||||
}
|
||||
@ -462,21 +527,18 @@ end
|
||||
-- investigators in the given group.
|
||||
---@param groupName String. Name of the group to spawn, matching a key in InvestigatorPanelData
|
||||
function spawnStarters(groupName)
|
||||
local position = Vector(START_POSITIONS.investigator)
|
||||
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(INVESTIGATOR_POSITION_SHIFT_COL)
|
||||
position:add(investigatorPositionShiftCol)
|
||||
col = col + 1
|
||||
if col > INVESTIGATOR_MAX_COLS then
|
||||
col = 1
|
||||
position = Vector(START_POSITIONS.investigator)
|
||||
position:add(Vector(
|
||||
INVESTIGATOR_POSITION_SHIFT_ROW.x * row,
|
||||
INVESTIGATOR_POSITION_SHIFT_ROW.z * row,
|
||||
INVESTIGATOR_POSITION_SHIFT_ROW.z * row))
|
||||
row = row + 1
|
||||
position = getInvestigatorRowStartPos(investigatorCount, row)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -489,7 +551,7 @@ function spawnStarterDeck(investigatorName, investigatorData, position)
|
||||
buildCommonSpawnSpec(investigatorName, INVESTIGATORS[investigatorName], position, true)) do
|
||||
spawnBag.spawn(spawnSpec)
|
||||
end
|
||||
local deckPos = Vector(position):add(INVESTIGATOR_SIGNATURE_OFFSET)
|
||||
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
|
||||
@ -500,7 +562,7 @@ function spawnStarterDeck(investigatorName, investigatorData, position)
|
||||
spawnBag.spawn({
|
||||
name = investigatorName.."starter",
|
||||
cards = cardIdList,
|
||||
globalPos = deckPos,
|
||||
globalPos = self.positionToWorld(deckPos),
|
||||
rotation = FACE_DOWN_ROTATION
|
||||
})
|
||||
end)
|
||||
@ -509,7 +571,7 @@ end
|
||||
---@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)
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
Wait.frames(function() placeClassCards(cardClass, isUpgraded) end, 2)
|
||||
end
|
||||
|
||||
@ -538,34 +600,34 @@ function placeClassCards(cardClass, isUpgraded)
|
||||
table.insert(assetList, cardId)
|
||||
end
|
||||
end
|
||||
local groupPos = Vector(START_POSITIONS.classCards)
|
||||
local groupPos = Vector(startPositions.classCards)
|
||||
if #skillList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = skillList,
|
||||
globalPos = groupPos,
|
||||
globalPos = self.positionToWorld(groupPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#skillList / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
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 = groupPos,
|
||||
globalPos = self.positionToWorld(groupPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#eventList / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
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 = groupPos,
|
||||
globalPos = self.positionToWorld(groupPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
@ -576,7 +638,7 @@ 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)
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
spawnInvestigators(cycle)
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
|
||||
local indexReady = allCardsBag.call("isIndexReady")
|
||||
@ -592,7 +654,7 @@ function spawnCycle(cycle)
|
||||
spawnBag.spawn({
|
||||
name = "cycle"..cycle,
|
||||
cards = copiedList,
|
||||
globalPos = START_POSITIONS.cycle,
|
||||
globalPos = self.positionToWorld(startPositions.cycle),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
@ -600,11 +662,11 @@ function spawnCycle(cycle)
|
||||
end
|
||||
|
||||
function spawnBonded()
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
spawnBag.spawn({
|
||||
name = "bonded",
|
||||
cards = BONDED_CARD_LIST,
|
||||
globalPos = START_POSITIONS.classCards,
|
||||
globalPos = self.positionToWorld(startPositions.classCards),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
@ -612,11 +674,11 @@ function spawnBonded()
|
||||
end
|
||||
|
||||
function spawnUpgradeSheets()
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
spawnBag.spawn({
|
||||
name = "upgradeSheets",
|
||||
cards = UPGRADE_SHEET_LIST,
|
||||
globalPos = START_POSITIONS.classCards,
|
||||
globalPos = self.positionToWorld(startPositions.classCards),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
@ -624,14 +686,14 @@ function spawnUpgradeSheets()
|
||||
spawnBag.spawn({
|
||||
name = "servitor",
|
||||
cards = { "09080-m" },
|
||||
globalPos = START_POSITIONS.summonedServitor,
|
||||
globalPos = self.positionToWorld(startPositions.summonedServitor),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
})
|
||||
end
|
||||
|
||||
-- Clears the current cards, and places all basic weaknesses on the table.
|
||||
function spawnWeaknesses()
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
|
||||
local indexReady = allCardsBag.call("isIndexReady")
|
||||
if (not indexReady) then
|
||||
@ -649,29 +711,29 @@ function spawnWeaknesses()
|
||||
table.insert(otherWeaknessList, id)
|
||||
end
|
||||
end
|
||||
local groupPos = Vector(START_POSITIONS.classCards)
|
||||
local groupPos = Vector(startPositions.classCards)
|
||||
spawnBag.spawn({
|
||||
name = "basicWeaknesses",
|
||||
cards = basicWeaknessList,
|
||||
globalPos = groupPos,
|
||||
globalPos = self.positionToWorld(groupPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#basicWeaknessList / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
groupPos.z = groupPos.z + math.ceil(#basicWeaknessList / 20) * cardRowOffset + cardGroupOffset
|
||||
spawnBag.spawn({
|
||||
name = "evolvedWeaknesses",
|
||||
cards = EVOLVED_WEAKNESSES,
|
||||
globalPos = groupPos,
|
||||
globalPos = self.positionToWorld(groupPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#EVOLVED_WEAKNESSES / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
groupPos.z = groupPos.z + math.ceil(#EVOLVED_WEAKNESSES / 20) * cardRowOffset + cardGroupOffset
|
||||
spawnBag.spawn({
|
||||
name = "otherWeaknesses",
|
||||
cards = otherWeaknessList,
|
||||
globalPos = groupPos,
|
||||
globalPos = self.positionToWorld(groupPos),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
@ -679,7 +741,7 @@ function spawnWeaknesses()
|
||||
end
|
||||
|
||||
function spawnRandomWeakness()
|
||||
spawnBag.recall(true)
|
||||
prepareToPlaceCards()
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
|
||||
local weaknessId = allCardsBag.call("getRandomWeaknessId")
|
||||
if (weaknessId == nil) then
|
||||
@ -689,7 +751,7 @@ function spawnRandomWeakness()
|
||||
spawnBag.spawn({
|
||||
name = "randomWeakness",
|
||||
cards = { weaknessId },
|
||||
globalPos = START_POSITIONS.randomWeakness,
|
||||
globalPos = self.positionToWorld(startPositions.randomWeakness),
|
||||
rotation = FACE_UP_ROTATION,
|
||||
})
|
||||
end
|
||||
|
@ -52,13 +52,24 @@ Spawner.spawnCardSpread = function(cardList, startPos, maxCols, rot, sort, callb
|
||||
end
|
||||
|
||||
local position = { x = startPos.x, y = startPos.y, z = startPos.z }
|
||||
-- Special handle the first row if we have less than a full single row, but only if there's a
|
||||
-- reasonable max column count. Single-row spreads will send a large value for maxCols
|
||||
if maxCols < 100 and #cardList < maxCols then
|
||||
position.z = startPos.z + ((maxCols - #cardList) / 2 * SPREAD_Z_SHIFT)
|
||||
end
|
||||
local cardsInRow = 0
|
||||
local rows = 0
|
||||
for _, card in ipairs(cardList) do
|
||||
Spawner.spawn({ card }, position, rot, callback)
|
||||
position.z = position.z + SPREAD_Z_SHIFT
|
||||
cardsInRow = cardsInRow + 1
|
||||
if cardsInRow >= maxCols then
|
||||
position.z = startPos.z
|
||||
rows = rows + 1
|
||||
local cardsForRow = #cardList - rows * maxCols
|
||||
if cardsForRow > maxCols then
|
||||
cardsForRow = maxCols
|
||||
end
|
||||
position.z = startPos.z + ((maxCols - cardsForRow) / 2 * SPREAD_Z_SHIFT)
|
||||
position.x = position.x + SPREAD_X_SHIFT
|
||||
cardsInRow = 0
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user