Merge pull request #106 from argonui/investigator-panel
Create a unified panel for groups of player cards.
This commit is contained in:
commit
456d210b6e
@ -67,13 +67,13 @@ do
|
||||
|
||||
local deck = Request.start(deckUri, function(status)
|
||||
if string.find(status.text, "<!DOCTYPE html>") then
|
||||
printToAll("Private deck ID " .. deckId .. " is not shared", playerColor)
|
||||
internal.maybePrint("Private deck ID " .. deckId .. " is not shared", playerColor)
|
||||
return false, table.concat({ "Private deck ", deckId, " is not shared" })
|
||||
end
|
||||
local json = JSON.decode(status.text)
|
||||
|
||||
if not json then
|
||||
printToAll("Deck ID " .. deckId .. " not found", playerColor)
|
||||
internal.maybePrint("Deck ID " .. deckId .. " not found", playerColor)
|
||||
return false, "Deck not found!"
|
||||
end
|
||||
|
||||
@ -101,9 +101,9 @@ do
|
||||
if (adbCardInfo.xp ~= nil and adbCardInfo.xp > 0) then
|
||||
cardName = cardName .. " (" .. adbCardInfo.xp .. ")"
|
||||
end
|
||||
printToAll("Card not found: " .. cardName .. ", ArkhamDB ID " .. cardId, playerColor)
|
||||
internal.maybePrint("Card not found: " .. cardName .. ", ArkhamDB ID " .. cardId, playerColor)
|
||||
else
|
||||
printToAll("Card not found in ArkhamDB, ID " .. cardId, playerColor)
|
||||
internal.maybePrint("Card not found in ArkhamDB, ID " .. cardId, playerColor)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@ -132,7 +132,7 @@ do
|
||||
return
|
||||
end
|
||||
|
||||
printToAll(table.concat({ "Found decklist: ", deck.name }), playerColor)
|
||||
internal.maybePrint(table.concat({ "Found decklist: ", deck.name }), playerColor)
|
||||
|
||||
log(table.concat({ "-", deck.name, "-" }))
|
||||
for k, v in pairs(deck) do
|
||||
@ -185,7 +185,7 @@ do
|
||||
local weaknessId = allCardsBag.call("getRandomWeaknessId")
|
||||
slots[weaknessId] = 1
|
||||
slots[RANDOM_WEAKNESS_ID] = nil
|
||||
printToAll("Random basic weakness added to deck", playerColor)
|
||||
internal.maybePrint("Random basic weakness added to deck", playerColor)
|
||||
end
|
||||
end
|
||||
|
||||
@ -249,7 +249,7 @@ do
|
||||
if investigatorCount ~= nil then
|
||||
slots["09006"] = investigatorCount
|
||||
else
|
||||
printToAll("Something went wrong with the load, adding 4 copies of On the Mend", playerColor)
|
||||
internal.maybePrint("Something went wrong with the load, adding 4 copies of On the Mend", playerColor)
|
||||
slots["09006"] = 4
|
||||
end
|
||||
end
|
||||
@ -294,7 +294,7 @@ do
|
||||
local tabooCard = allCardsBag.call("getCardById", { id = cardId .. "-t" })
|
||||
if tabooCard == nil then
|
||||
local basicCard = allCardsBag.call("getCardById", { id = cardId })
|
||||
printToAll("Taboo version for " .. basicCard.data.Nickname .. " is not available. Using standard version", playerColor)
|
||||
internal.maybePrint("Taboo version for " .. basicCard.data.Nickname .. " is not available. Using standard version", playerColor)
|
||||
else
|
||||
slots[cardId .. "-t"] = slots[cardId]
|
||||
slots[cardId] = nil
|
||||
@ -304,6 +304,12 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
internal.maybePrint = function(message, playerColor)
|
||||
if playerColor ~= "None" then
|
||||
printToAll(message, playerColor)
|
||||
end
|
||||
end
|
||||
|
||||
-- Gets the ArkhamDB config info from the configuration object.
|
||||
---@return Table. Configuration data
|
||||
internal.getConfiguration = function()
|
||||
@ -411,7 +417,7 @@ do
|
||||
on_success(results, table.unpack(parameters))
|
||||
elseif on_error == nil then
|
||||
for _, request in ipairs(errors) do
|
||||
printToAll(table.concat({ "[ERROR]", request.uri, ":", request.error_message }))
|
||||
internal.maybePrint(table.concat({ "[ERROR]", request.uri, ":", request.error_message }))
|
||||
end
|
||||
else
|
||||
on_error(requests, table.unpack(parameters))
|
||||
|
@ -129,6 +129,7 @@ function buildSupplementalIndexes()
|
||||
table.insert(basicWeaknessList, cardMetadata.id)
|
||||
end
|
||||
end
|
||||
table.sort(basicWeaknessList, cardComparator)
|
||||
|
||||
-- Add the card to the appropriate class and level indexes
|
||||
local isGuardian = false
|
||||
@ -298,6 +299,10 @@ function buildAvailableWeaknesses()
|
||||
return availableWeaknesses
|
||||
end
|
||||
|
||||
function getBasicWeaknesses()
|
||||
return basicWeaknessList
|
||||
end
|
||||
|
||||
-- Helper function that adds one to the table entry for the number of weaknesses in play
|
||||
function incrementWeaknessCount(table, cardMetadata)
|
||||
if (isBasicWeakness(cardMetadata)) then
|
||||
|
509
src/playercards/PlayerCardPanel.ttslua
Normal file
509
src/playercards/PlayerCardPanel.ttslua
Normal file
@ -0,0 +1,509 @@
|
||||
require("playercards/PlayerCardPanelData")
|
||||
local spawnBag = require("playercards/spawnbag/SpawnBag")
|
||||
local arkhamDb = require("arkhamdb/ArkhamDb")
|
||||
|
||||
-- TODO: Update when the real UI image is in place
|
||||
local BUTTON_WIDTH = 150
|
||||
local BUTTON_HEIGHT = 550
|
||||
|
||||
local ALL_CARDS_BAG_GUID = "15bb07"
|
||||
|
||||
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 to match the reserved areas of the
|
||||
-- table. Cards will lay out horizontally, then create additional rows
|
||||
local START_POSITIONS = {
|
||||
skill = Vector(58.384, 1.36, 92.4),
|
||||
event = Vector(53.229, 1.36, 92.4),
|
||||
asset = Vector(40.960, 1.36, 92.4),
|
||||
investigator = Vector(60, 1.36, 80)
|
||||
}
|
||||
|
||||
-- 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_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 spawnStarterDecks = false
|
||||
|
||||
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
|
||||
|
||||
self.createButton({
|
||||
label="Guardian", click_function="spawnInvestigatorsGuardian", function_owner=self,
|
||||
position={-0.3,0.2,-0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Seeker", click_function="spawnInvestigatorsSeeker", function_owner=self,
|
||||
position={0,0.2,-0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Mystic", click_function="spawnInvestigatorsMystic", function_owner=self,
|
||||
position={0.3,0.2,-0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Rogue", click_function="spawnInvestigatorsRogue", function_owner=self,
|
||||
position={-0.3,0.2,-0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Survivor", click_function="spawnSurvivor", function_owner=self,
|
||||
position={0,0.2,-0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Neutral", click_function="spawnNeutral", function_owner=self,
|
||||
position={0.3,0.2,-0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
|
||||
self.createButton({
|
||||
label="Core", click_function="spawnCore", function_owner=self,
|
||||
position={-0.3,0.2,-0.2}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Dunwich", click_function="spawnDunwich", function_owner=self,
|
||||
position={0,0.2,-0.2}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Carcosa", click_function="spawnCarcosa", function_owner=self,
|
||||
position={0.3,0.2,-0.2}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Forgotten Age", click_function="spawnForgottenAge", function_owner=self,
|
||||
position={-0.3,0.2,-0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Circle Undone", click_function="spawnCircleUndone", function_owner=self,
|
||||
position={0,0.2,-0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Dream Eaters", click_function="spawnDreamEaters", function_owner=self,
|
||||
position={0.3,0.2,-0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Innsmouth", click_function="spawnInnsmouth", function_owner=self,
|
||||
position={-0.3,0.2,0}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="EotE", click_function="spawnEotE", function_owner=self,
|
||||
position={0,0.2,0}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Scarlet Keys", click_function="spawnScarletKeys", function_owner=self,
|
||||
position={0.3,0.2,0}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="InvPacks", click_function="spawnInvestigatorDecks", function_owner=self,
|
||||
position={-0.3,0.2,0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Investigators", click_function="setInvestigators", function_owner=self,
|
||||
position={-0.15,0.2,-0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Starters", click_function="setStarters", function_owner=self,
|
||||
position={0.15,0.2,-0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
|
||||
self.createButton({
|
||||
label="L0 Guardian", click_function="spawnBasicGuardian", function_owner=self,
|
||||
position={-0.15,0.2,0.3}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Guardian", click_function="spawnUpgradedGuardian", function_owner=self,
|
||||
position={0.15,0.2,0.3}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Seeker", click_function="spawnBasicSeeker", function_owner=self,
|
||||
position={-0.15,0.2,0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Seeker", click_function="spawnUpgradedSeeker", function_owner=self,
|
||||
position={0.15,0.2,0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Mystic", click_function="spawnBasicMystic", function_owner=self,
|
||||
position={-0.15,0.2,0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Mystic", click_function="spawnUpgradedGuardian", function_owner=self,
|
||||
position={0.15,0.2,0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Rogue", click_function="spawnBasicRogue", function_owner=self,
|
||||
position={-0.15,0.2,0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Rogue", click_function="spawnUpgradedRogue", function_owner=self,
|
||||
position={0.15,0.2,0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Survivor", click_function="spawnBasicSurvivor", function_owner=self,
|
||||
position={-0.15,0.2,0.7}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Survivor", click_function="spawnUpgradedSurvivor", function_owner=self,
|
||||
position={0.15,0.2,0.7}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Neutral", click_function="spawnBasicNeutral", function_owner=self,
|
||||
position={-0.15,0.2,0.8}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Neutral", click_function="spawnUpgradedNeutral", function_owner=self,
|
||||
position={0.15,0.2,0.8}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Clear", click_function="deleteAll", function_owner=self,
|
||||
position={0.5,0.2,0.9}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Weaknesses", click_function="spawnWeaknesses", function_owner=self,
|
||||
position={-0.5,0.2,0.9}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
local classList = { "Guardian", "Seeker", "Mystic", "Rogue", "Survivor", "Neutral" }
|
||||
for _, className in ipairs(classList) do
|
||||
local funcName = "spawnInvestigators"..className
|
||||
self.setVar(funcName, function(_, _, _) spawnGroup(className) end)
|
||||
funcName = "spawnBasic"..className
|
||||
self.setVar(funcName, function(_, _, _) spawnClassCards(className, false) end)
|
||||
funcName = "spawnUpgraded"..className
|
||||
self.setVar(funcName, function(_, _, _) spawnClassCards(className, true) end)
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Replace these with something less manual once the full data in in place so we know what
|
||||
-- keys to use
|
||||
function placeCore()
|
||||
spawnGroup("Core")
|
||||
end
|
||||
|
||||
function placeDunwich()
|
||||
spawnGroup("Dunwich")
|
||||
end
|
||||
|
||||
function placeCarcosa()
|
||||
spawnGroup("Carcosa")
|
||||
end
|
||||
|
||||
function placeForgottenAge()
|
||||
spawnGroup("ForgottenAge")
|
||||
end
|
||||
|
||||
function placeCircleUndone()
|
||||
spawnGroup("CircleUndone")
|
||||
end
|
||||
|
||||
function placeDreamEaters()
|
||||
spawnGroup("DreamEaters")
|
||||
end
|
||||
|
||||
function placeInnsmouth()
|
||||
spawnGroup("Innsmouth")
|
||||
end
|
||||
|
||||
function placeEotE()
|
||||
spawnGroup("EotE")
|
||||
end
|
||||
|
||||
function placeScarletKeys()
|
||||
spawnGroup("ScarletKeys")
|
||||
end
|
||||
|
||||
function placeInvestigatorDecks()
|
||||
spawnGroup("InvestigatorDecks")
|
||||
end
|
||||
|
||||
-- UI handler to put the investigator spawn in investigator mode.
|
||||
function setInvestigators()
|
||||
spawnStarterDecks = false
|
||||
printToAll("Spawning investigator piles")
|
||||
end
|
||||
|
||||
-- UI handler to put the investigator spawn in starter deck mode.
|
||||
function setStarters()
|
||||
spawnStarterDecks = true
|
||||
printToAll("Spawning starter decks")
|
||||
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 spawnGroup(groupName)
|
||||
spawnBag.recall(true)
|
||||
Wait.frames(function()
|
||||
if spawnStarterDecks 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)
|
||||
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
|
||||
for _, spawnSpec in ipairs(buildInvestigatorSpawnSpec(
|
||||
investigatorName, INVESTIGATORS[investigatorName], position, false)) do
|
||||
spawnBag.spawn(spawnSpec)
|
||||
end
|
||||
position:add(INVESTIGATOR_POSITION_SHIFT_COL)
|
||||
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
|
||||
end
|
||||
end
|
||||
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(INVESTIGATOR_SIGNATURE_OFFSET)
|
||||
local spawns = buildCommonSpawnSpec(investigatorName, investigatorData, position)
|
||||
table.insert(spawns, {
|
||||
name = investigatorName.."signatures",
|
||||
cards = investigatorData.signatures,
|
||||
globalPos = 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(INVESTIGATOR_CARD_OFFSET)
|
||||
return {
|
||||
{
|
||||
name = investigatorName.."minicards",
|
||||
cards = oneCardOnly and investigatorData.minicards[1] or investigatorData.minicards,
|
||||
globalPos = position,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
},
|
||||
{
|
||||
name = investigatorName.."cards",
|
||||
cards = oneCardOnly and investigatorData.cards[1] or investigatorData.cards,
|
||||
globalPos = 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 position = Vector(START_POSITIONS.investigator)
|
||||
local col = 1
|
||||
local row = 1
|
||||
for _, investigatorName in ipairs(INVESTIGATOR_GROUPS[groupName]) do
|
||||
spawnStarterDeck(investigatorName, INVESTIGATORS[investigatorName], position)
|
||||
position:add(INVESTIGATOR_POSITION_SHIFT_COL)
|
||||
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
|
||||
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(INVESTIGATOR_SIGNATURE_OFFSET)
|
||||
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 = 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)
|
||||
spawnBag.recall(true)
|
||||
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
|
||||
if #skillList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = skillList,
|
||||
globalPos = START_POSITIONS.skill,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
||||
if #eventList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. "event" .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = eventList,
|
||||
globalPos = START_POSITIONS.event,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
||||
if #assetList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. "asset" .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = assetList,
|
||||
globalPos = START_POSITIONS.asset,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Clears the current cards, and places all basic weaknesses on the table.
|
||||
function spawnWeaknesses()
|
||||
spawnBag.recall(fast)
|
||||
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("getBasicWeaknesses")
|
||||
local copiedList = { }
|
||||
for i, id in ipairs(weaknessIdList) do
|
||||
copiedList[i] = id
|
||||
end
|
||||
spawnBag.spawn({
|
||||
name = "weaknesses",
|
||||
cards = copiedList,
|
||||
globalPos = START_POSITIONS.asset,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
44
src/playercards/PlayerCardPanelData.ttslua
Normal file
44
src/playercards/PlayerCardPanelData.ttslua
Normal file
@ -0,0 +1,44 @@
|
||||
------------------ START INVESTIGATOR DATA DEFINITION ------------------
|
||||
INVESTIGATOR_GROUPS = {
|
||||
Guardian = {
|
||||
"Roland Banks",
|
||||
},
|
||||
Seeker = {
|
||||
"Daisy Walker",
|
||||
},
|
||||
Core = {
|
||||
"Roland Banks",
|
||||
"Daisy Walker",
|
||||
"R2",
|
||||
"D2",
|
||||
"R3",
|
||||
"D3",
|
||||
"R4",
|
||||
"D4",
|
||||
"R5",
|
||||
"D5",
|
||||
},
|
||||
}
|
||||
|
||||
INVESTIGATORS = { }
|
||||
INVESTIGATORS["Roland Banks"] = {
|
||||
cards = { "01001", "01001-promo", "01001-p", "01001-pf", "01001-pb", },
|
||||
minicards = { "01001-m", "01001-promo-m", },
|
||||
signatures = { "01006", "01007", "90030", "90031", },
|
||||
starterDeck = "1462",
|
||||
}
|
||||
INVESTIGATORS["Daisy Walker"] = {
|
||||
cards = { "01002", "01002-p", "01002-pf", "01002-pb", },
|
||||
minicards = { "01002-m", },
|
||||
signatures = { "01008", "01009", "90002", "90003" },
|
||||
starterDeck = "42652",
|
||||
}
|
||||
------------------ END INVESTIGATOR DATA DEFINITION ------------------
|
||||
INVESTIGATORS["R2"] = INVESTIGATORS["Roland Banks"]
|
||||
INVESTIGATORS["R3"] = INVESTIGATORS["Roland Banks"]
|
||||
INVESTIGATORS["R4"] = INVESTIGATORS["Roland Banks"]
|
||||
INVESTIGATORS["R5"] = INVESTIGATORS["Roland Banks"]
|
||||
INVESTIGATORS["D2"] = INVESTIGATORS["Daisy Walker"]
|
||||
INVESTIGATORS["D3"] = INVESTIGATORS["Daisy Walker"]
|
||||
INVESTIGATORS["D4"] = INVESTIGATORS["Daisy Walker"]
|
||||
INVESTIGATORS["D5"] = INVESTIGATORS["Daisy Walker"]
|
@ -3,6 +3,7 @@
|
||||
-- Note that the table rotation is weird, and the X axis is vertical while the
|
||||
-- Z axis is horizontal
|
||||
local SPREAD_Z_SHIFT = -2.3
|
||||
local SPREAD_X_SHIFT = -3.66
|
||||
|
||||
Spawner = { }
|
||||
|
||||
@ -45,15 +46,22 @@ Spawner.spawnCards = function(cardList, pos, rot, sort, callback)
|
||||
Spawner.spawn(miniCards, position, rot, callback)
|
||||
end
|
||||
|
||||
Spawner.spawnCardSpread = function(cardList, startPos, rot, sort, callback)
|
||||
Spawner.spawnCardSpread = function(cardList, startPos, maxCols, rot, sort, callback)
|
||||
if (sort) then
|
||||
table.sort(cardList, Spawner.cardComparator)
|
||||
end
|
||||
|
||||
local position = { x = startPos.x, y = startPos.y, z = startPos.z }
|
||||
local cardsInRow = 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
|
||||
position.x = position.x + SPREAD_X_SHIFT
|
||||
cardsInRow = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -14,13 +14,17 @@ require("playercards/PlayerCardSpawner")
|
||||
-- a valid Vector with x, y, and z defined, e.g. { x = 5, y = 1, z = 15 }
|
||||
-- rotation: Rotation for the spawned objects. X=180 should be used for face down items. As with
|
||||
-- globalPos, this should be a valid Vector with x, y, and z defined
|
||||
-- spread: Optional. If present and true, cards will be spawned next to each other in a spread
|
||||
-- moving to the right. globalPos will define the location of the first card, each after that
|
||||
-- will be moved a predefined distance
|
||||
-- spread: Optional Boolean. If present and true, cards will be spawned next to each other in a
|
||||
-- spread moving to the right. globalPos will define the location of the first card, each
|
||||
-- after that will be moved a predefined distance
|
||||
-- spreadCols: Optional integer. If spread is true, specifies the maximum columns cards will be
|
||||
-- laid out in before starting a new row. If spread is true but spreadCols is not set, all
|
||||
-- cards will be in a single row (however long that may be)
|
||||
-- }
|
||||
-- See BondedBag.ttslua for an example
|
||||
do
|
||||
local SpawnBag = { }
|
||||
local internal = { }
|
||||
|
||||
-- To assist debugging, will draw a box around the recall zone when it's set up
|
||||
local SHOW_RECALL_ZONE = false
|
||||
@ -87,7 +91,8 @@ do
|
||||
end
|
||||
local cardsToSpawn = { }
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_GUID)
|
||||
for _, cardId in ipairs(spawnSpec.cards) do
|
||||
local cardList = spawnSpec.cards
|
||||
for _, cardId in ipairs(cardList) do
|
||||
local cardData = allCardsBag.call("getCardById", { id = cardId })
|
||||
if (cardData ~= nil) then
|
||||
table.insert(cardsToSpawn, cardData)
|
||||
@ -96,27 +101,27 @@ do
|
||||
end
|
||||
end
|
||||
if (spawnSpec.spread) then
|
||||
Spawner.spawnCardSpread(cardsToSpawn, spawnSpec.globalPos, spawnSpec.rotation, false, recordPlacedObject)
|
||||
Spawner.spawnCardSpread(cardsToSpawn, spawnSpec.globalPos, spawnSpec.spreadCols or 9999, spawnSpec.rotation, false, internal.recordPlacedObject)
|
||||
else
|
||||
Spawner.spawnCards(cardsToSpawn, spawnSpec.globalPos, spawnSpec.rotation, false, recordPlacedObject)
|
||||
-- TTS decks come out in reverse order of the cards, reverse the list so the input order stays
|
||||
-- This only applies for decks; spreads are spawned by us in the order given
|
||||
if spawnSpec.rotation.z != 180 then
|
||||
cardsToSpawn = internal.reverseList(cardsToSpawn)
|
||||
end
|
||||
Spawner.spawnCards(cardsToSpawn, spawnSpec.globalPos, spawnSpec.rotation, false, internal.recordPlacedObject)
|
||||
end
|
||||
placedSpecs[spawnSpec.name] = true
|
||||
end
|
||||
|
||||
-- Recalls all spawned objects to the bag, and clears the placedObjectGuids list
|
||||
SpawnBag.recall = function()
|
||||
local trash = spawnObjectData({data = RECALL_BAG, position = self.getPosition()})
|
||||
for guid, _ in pairs(placedObjectGuids) do
|
||||
local obj = getObjectFromGUID(guid)
|
||||
if (obj ~= nil) then
|
||||
if (isInRecallZone(obj)) then
|
||||
trash.putObject(obj)
|
||||
end
|
||||
placedObjectGuids[guid] = nil
|
||||
end
|
||||
---@param fast Boolean. If true, cards will be deleted directly without faking the bag recall.
|
||||
SpawnBag.recall = function(fast)
|
||||
if fast then
|
||||
internal.deleteSpawned()
|
||||
else
|
||||
internal.recallSpawned()
|
||||
end
|
||||
|
||||
trash.destruct()
|
||||
-- We've recalled everything we can, some cards may have been moved out of the
|
||||
-- card area. Just reset at this point.
|
||||
placedSpecs = { }
|
||||
@ -124,16 +129,46 @@ do
|
||||
recallZone = nil
|
||||
end
|
||||
|
||||
-- Deleted all spawned cards.
|
||||
internal.deleteSpawned = function()
|
||||
for guid, _ in pairs(placedObjectGuids) do
|
||||
local obj = getObjectFromGUID(guid)
|
||||
if (obj ~= nil) then
|
||||
if (internal.isInRecallZone(obj)) then
|
||||
obj.destruct()
|
||||
end
|
||||
placedObjectGuids[guid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Recalls spawned cards with a fake bag that replicates the memory bag recall style.
|
||||
internal.recallSpawned = function()
|
||||
local trash = spawnObjectData({data = RECALL_BAG, position = self.getPosition()})
|
||||
for guid, _ in pairs(placedObjectGuids) do
|
||||
local obj = getObjectFromGUID(guid)
|
||||
if (obj ~= nil) then
|
||||
if (internal.isInRecallZone(obj)) then
|
||||
trash.putObject(obj)
|
||||
end
|
||||
placedObjectGuids[guid] = nil
|
||||
end
|
||||
end
|
||||
|
||||
trash.destruct()
|
||||
end
|
||||
|
||||
|
||||
-- Callback for when an object has been spawned. Tracks the object for later recall and updates the
|
||||
-- recall zone.
|
||||
function recordPlacedObject(spawned)
|
||||
internal.recordPlacedObject = function(spawned)
|
||||
placedObjectGuids[spawned.getGUID()] = true
|
||||
expandRecallZone(spawned)
|
||||
internal.expandRecallZone(spawned)
|
||||
end
|
||||
|
||||
-- Expands the current recall zone based on the position of the given object. The recall zone will
|
||||
-- be maintained as the bounding box of the extreme object positions, plus a small amount of buffer
|
||||
function expandRecallZone(spawnedCard)
|
||||
internal.expandRecallZone = function(spawnedCard)
|
||||
local pos = spawnedCard.getPosition()
|
||||
if (recallZone == nil) then
|
||||
-- First card out of the bag, initialize surrounding that
|
||||
@ -189,7 +224,7 @@ do
|
||||
|
||||
-- Checks to see if the given object is in the current recall zone. If there isn't a recall zone,
|
||||
-- will return true so that everything can be easily cleaned up.
|
||||
function isInRecallZone(obj)
|
||||
internal.isInRecallZone = function(obj)
|
||||
if (recallZone == nil) then
|
||||
return true
|
||||
end
|
||||
@ -198,5 +233,14 @@ do
|
||||
and pos.z < recallZone.upperLeft.z and pos.z > recallZone.lowerRight.z)
|
||||
end
|
||||
|
||||
internal.reverseList = function(list)
|
||||
local reversed = { }
|
||||
for i = 1, #list do
|
||||
reversed[i] = list[#list - i + 1]
|
||||
end
|
||||
|
||||
return reversed
|
||||
end
|
||||
|
||||
return SpawnBag
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user