From afff593874d9814cd8e1e1f860e1bb94358725bd Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Fri, 24 May 2024 21:54:29 +0200 Subject: [PATCH 1/6] foundation for custom cards --- src/arkhamdb/DeckImporterUi.ttslua | 12 +- src/playercards/AllCardsBag.ttslua | 21 ++-- src/playercards/AllCardsBagApi.ttslua | 22 ++-- src/playercards/PlayerCardPanel.ttslua | 157 ++++++++++++------------- src/playercards/SpawnBag.ttslua | 130 ++++++++++---------- xml/playercards/PlayerCardPanel.xml | 23 ++-- 6 files changed, 174 insertions(+), 191 deletions(-) diff --git a/src/arkhamdb/DeckImporterUi.ttslua b/src/arkhamdb/DeckImporterUi.ttslua index 5788446d..524907c0 100644 --- a/src/arkhamdb/DeckImporterUi.ttslua +++ b/src/arkhamdb/DeckImporterUi.ttslua @@ -150,11 +150,8 @@ end -- Event handlers for deck ID change function redDeckChanged(_, _, inputValue) redDeckId = inputValue end - function orangeDeckChanged(_, _, inputValue) orangeDeckId = inputValue end - function whiteDeckChanged(_, _, inputValue) whiteDeckId = inputValue end - function greenDeckChanged(_, _, inputValue) greenDeckId = inputValue end -- Event handlers for toggle buttons @@ -174,14 +171,7 @@ function loadInvestigatorsChanged() end function loadDecks() - -- testLoadLotsOfDecks() - -- Method in DeckImporterMain, visible due to inclusion - - local indexReady = allCardsBagApi.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 + if not allCardsBagApi.isIndexReady() then return end if (redDeckId ~= nil and redDeckId ~= "") then buildDeck("Red", redDeckId) end diff --git a/src/playercards/AllCardsBag.ttslua b/src/playercards/AllCardsBag.ttslua index c141564b..2355b2f4 100644 --- a/src/playercards/AllCardsBag.ttslua +++ b/src/playercards/AllCardsBag.ttslua @@ -18,7 +18,7 @@ end -- called once indexing is complete it means the hotfix bag has been added -- later, and we should rebuild the index to integrate the hotfix bag. function rebuildIndexForHotfix() - if (indexingDone) then + if indexingDone then startIndexBuild() end end @@ -210,6 +210,9 @@ function cardComparator(id1, id2) end function isIndexReady() + if not indexingDone then + broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) + end return indexingDone end @@ -221,10 +224,7 @@ end -- cardData: TTS object data, suitable for spawning the card -- cardMetadata: Table of parsed metadata function getCardById(params) - if (not indexingDone) then - broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) - return { } - end + if not isIndexReady() then return {} end return cardIdIndex[params.id] end @@ -237,10 +237,8 @@ end -- cardData: TTS object data, suitable for spawning the card -- cardMetadata: Table of parsed metadata function getCardsByClassAndLevel(params) - if (not indexingDone) then - broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) - return { } - end + if not isIndexReady() then return {} end + local upgradeKey if (params.upgraded) then upgradeKey = "-upgrade" @@ -251,10 +249,7 @@ function getCardsByClassAndLevel(params) end function getCardsByCycle(cycleName) - if (not indexingDone) then - broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) - return { } - end + if not isIndexReady() then return {} end return cycleIndex[string.lower(cycleName)] end diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index 350f2f8d..c7baf29f 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -6,6 +6,14 @@ do return guidReferenceApi.getObjectByOwnerAndType("Mythos", "AllCardsBag") end + local function returnCopyOfList(data) + local copiedList = {} + for _, id in ipairs(data) do + table.insert(copiedList, id) + end + return copiedList + end + -- Returns a specific card from the bag, based on ArkhamDB ID ---@param id table String ID of the card to retrieve ---@return table table @@ -14,7 +22,7 @@ do -- cardData: TTS object data, suitable for spawning the card -- cardMetadata: Table of parsed metadata AllCardsBagApi.getCardById = function(id) - return getAllCardsBag().call("getCardById", {id = id}) + return getAllCardsBag().call("getCardById", { id = id }) end -- Gets a random basic weakness from the bag. Once a given ID has been returned @@ -36,7 +44,7 @@ do -- called once indexing is complete it means the hotfix bag has been added -- later, and we should rebuild the index to integrate the hotfix bag. AllCardsBagApi.rebuildIndexForHotfix = function() - return getAllCardsBag().call("rebuildIndexForHotfix") + getAllCardsBag().call("rebuildIndexForHotfix") end -- Searches the bag for cards which match the given name and returns a list. Note that this is @@ -44,7 +52,7 @@ do ---@param name string or string fragment to search for names ---@param exact boolean Whether the name match should be exact AllCardsBagApi.getCardsByName = function(name, exact) - return getAllCardsBag().call("getCardsByName", {name = name, exact = exact}) + return returnCopyOfList(getAllCardsBag().call("getCardsByName", { name = name, exact = exact })) end AllCardsBagApi.isBagPresent = function() @@ -59,16 +67,16 @@ do -- cardData: TTS object data, suitable for spawning the card -- cardMetadata: Table of parsed metadata AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded) - return getAllCardsBag().call("getCardsByClassAndLevel", {class = class, upgraded = upgraded}) + return returnCopyOfList(getAllCardsBag().call("getCardsByClassAndLevel", { class = class, upgraded = upgraded })) end AllCardsBagApi.getCardsByCycle = function(cycle) - return getAllCardsBag().call("getCardsByCycle", cycle) + return returnCopyOfList(getAllCardsBag().call("getCardsByCycle", cycle)) end AllCardsBagApi.getUniqueWeaknesses = function() - return getAllCardsBag().call("getUniqueWeaknesses") + return returnCopyOfList(getAllCardsBag().call("getUniqueWeaknesses")) end return AllCardsBagApi -end \ No newline at end of file +end diff --git a/src/playercards/PlayerCardPanel.ttslua b/src/playercards/PlayerCardPanel.ttslua index d1ae7311..f84bb340 100644 --- a/src/playercards/PlayerCardPanel.ttslua +++ b/src/playercards/PlayerCardPanel.ttslua @@ -26,22 +26,20 @@ local CYCLE_BUTTONS_Z_OFFSET = 0.2665 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} +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 +-- 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 +-- 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 +-- 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), @@ -50,7 +48,7 @@ local START_POSITIONS = { 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. + -- should be placed. If more customizable cards are added it will need to be moved. summonedServitor = Vector(CARD_WIDTH * -7.5, 2, 1.7) } @@ -64,12 +62,12 @@ 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 +-- 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 +-- USE THESE! Positions and offset shifts accounting for the scale of the panel local startPositions local cardRowOffset local cardGroupOffset @@ -78,7 +76,14 @@ local investigatorPositionShiftCol local investigatorCardOffset local investigatorSignatureOffset -local CLASS_LIST = { "Guardian", "Seeker", "Rogue", "Mystic", "Survivor", "Neutral" } +local CLASS_LIST = { + "Guardian", + "Seeker", + "Rogue", + "Mystic", + "Survivor", + "Neutral" +} local CYCLE_LIST = { "Core", "The Dunwich Legacy", @@ -94,9 +99,8 @@ local CYCLE_LIST = { } local excludedNonBasicWeaknesses - -local starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY -local helpVisibleToPlayers = { } +local spawnStarterDecks = false +local helpVisibleToPlayers = {} function onSave() return JSON.encode({ spawnBagState = spawnBag.getStateForSave() }) @@ -104,7 +108,7 @@ end function onLoad(savedData) if savedData and savedData ~= "" then - local saveState = JSON.decode(savedData) or { } + local saveState = JSON.decode(savedData) or {} if saveState.spawnBagState ~= nil then spawnBag.loadFromSave(saveState.spawnBagState) end @@ -117,7 +121,7 @@ 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 = { } + excludedNonBasicWeaknesses = {} for _, investigator in pairs(INVESTIGATORS) do for _, signatureId in ipairs(investigator.signatures) do excludedNonBasicWeaknesses[signatureId] = true @@ -274,9 +278,8 @@ function createCycleButtons() if rowCount == 3 then -- Account for two centered buttons on the final row buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET / 2 - --[[ Account for centered button on the final row - buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET - ]] + -- 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 @@ -297,8 +300,6 @@ function createClearButton() end function createInvestigatorModeButtons() - local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS - self.createButton({ function_owner = self, click_function = "setCardsOnlyMode", @@ -306,18 +307,18 @@ function createInvestigatorModeButtons() height = 170, width = 760, scale = Vector(0.25, 1, 0.25), - color = starterMode and TRANSPARENT or STARTER_DECK_MODE_SELECTED_COLOR + color = spawnStarterDecks and TRANSPARENT or STARTER_DECK_MODE_SELECTED_COLOR }) self.createButton({ function_owner = self, - click_function = "setStarterDeckMode", + click_function = "setspawnStarterDecks", position = Vector(0.66, 0.1, -0.322), height = 170, width = 760, scale = Vector(0.25, 1, 0.25), - color = starterMode and STARTER_DECK_MODE_SELECTED_COLOR or TRANSPARENT + color = spawnStarterDecks and STARTER_DECK_MODE_SELECTED_COLOR or TRANSPARENT }) - local checkX = starterMode and 0.52 or 0.11 + local checkX = spawnStarterDecks and 0.52 or 0.11 self.createButton({ function_owner = self, label = "✓", @@ -325,7 +326,8 @@ function createInvestigatorModeButtons() position = Vector(checkX, 0.11, -0.317), height = 0, width = 0, - scale = Vector(0.3, 1, 0.3), + font_size = 300, + scale = Vector(0.1, 1, 0.1), font_color = { 0, 0, 0 }, color = { 1, 1, 1 } }) @@ -354,13 +356,13 @@ function updateHelpVisibility() self.UI.setAttribute("helpPanel", "active", string.len(visibility) > 0) end -function setStarterDeckMode() - starterDeckMode = STARTER_DECK_MODE_STARTERS +function setspawnStarterDecks() + spawnStarterDecks = true updateStarterModeButtons() end function setCardsOnlyMode() - starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY + spawnStarterDecks = false updateStarterModeButtons() end @@ -384,7 +386,7 @@ end function scalePositions() -- Assume scaling is consistent in X and Z dimensions local scale = 1 / self.getScale().x - startPositions = { } + 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 @@ -405,14 +407,12 @@ function deleteAll() spawnBag.recall(true) end --- Spawn an investigator group, based on the current UI setting for either investigators or starter --- decks. +-- 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 + if spawnStarterDecks then spawnStarters(groupName) else spawnInvestigators(groupName) @@ -420,7 +420,7 @@ function spawnInvestigatorGroup(groupName) end, 2) end --- Spawn cards for all investigators in the given group. This creates piles for all defined +-- 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) @@ -451,14 +451,14 @@ 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))) + 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)) + investigatorPositionShiftCol.x * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2, + investigatorPositionShiftCol.y * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2, + investigatorPositionShiftCol.z * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2)) return rowStart end @@ -470,23 +470,23 @@ 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 - }) + 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 +-- 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 tts__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 +--- and minicard lists. Otherwise, spawn them all in a deck function buildCommonSpawnSpec(investigatorName, investigatorData, position, oneCardOnly) local cardPos = Vector(position):add(investigatorCardOffset) return { @@ -533,23 +533,24 @@ function spawnStarterDeck(investigatorName, investigatorData, position) end local deckPos = Vector(position):add(investigatorSignatureOffset) arkhamDb.getDecklist("None", investigatorData.starterDeck, true, false, false, function(slots) - local cardIdList = { } + 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", + 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. +---@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) @@ -557,18 +558,15 @@ 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. +---@param isUpgraded boolean If true, spawn the Level 1-5 cards. Otherwise, Level 0. function placeClassCards(cardClass, isUpgraded) - local indexReady = allCardsBagApi.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 + if not allCardsBagApi.isIndexReady() then return end + local cardIdList = allCardsBagApi.getCardsByClassAndLevel(cardClass, isUpgraded) - local skillList = { } - local eventList = { } - local assetList = { } + local skillList = {} + local eventList = {} + local assetList = {} for _, cardId in ipairs(cardIdList) do local cardMetadata = allCardsBagApi.getCardById(cardId).metadata if (cardMetadata.type == "Skill") then @@ -614,24 +612,22 @@ function placeClassCards(cardClass, isUpgraded) end end +-- called by the XML UI to spawn cards from fan-made camnpaigns +function spawnOtherCards() + spawnCycle("Other") +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) + if not allCardsBagApi.isIndexReady() then return end + prepareToPlaceCards() spawnInvestigators(cycle) - local indexReady = allCardsBagApi.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 = allCardsBagApi.getCardsByCycle(cycle) - local copiedList = { } - for i, id in ipairs(cycleCardList) do - copiedList[i] = id - end + spawnBag.spawn({ - name = "cycle"..cycle, - cards = copiedList, + name = "cycle" .. cycle, + cards = allCardsBagApi.getCardsByCycle(cycle), globalPos = self.positionToWorld(startPositions.cycle), rotation = FACE_UP_ROTATION, spread = true, @@ -671,16 +667,13 @@ end -- Clears the current cards, and places all basic weaknesses on the table. function spawnWeaknesses() + if not allCardsBagApi.isIndexReady() then return end + prepareToPlaceCards() - local indexReady = allCardsBagApi.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 = allCardsBagApi.getUniqueWeaknesses() - local basicWeaknessList = { } - local otherWeaknessList = { } - for i, id in ipairs(weaknessIdList) do + + local basicWeaknessList = {} + local otherWeaknessList = {} + for _, id in ipairs(allCardsBagApi.getUniqueWeaknesses()) do local cardMetadata = allCardsBagApi.getCardById(id).metadata if cardMetadata.basicWeaknessCount ~= nil and cardMetadata.basicWeaknessCount > 0 then table.insert(basicWeaknessList, id) @@ -721,7 +714,7 @@ function spawnRandomWeakness() prepareToPlaceCards() local weaknessId = allCardsBagApi.getRandomWeaknessId() if (weaknessId == nil) then - broadcastToAll("All basic weaknesses are in play!", {0.9, 0.2, 0.2}) + broadcastToAll("All basic weaknesses are in play!", { 0.9, 0.2, 0.2 }) return end spawnBag.spawn({ diff --git a/src/playercards/SpawnBag.ttslua b/src/playercards/SpawnBag.ttslua index 3bdcb80e..982d489c 100644 --- a/src/playercards/SpawnBag.ttslua +++ b/src/playercards/SpawnBag.ttslua @@ -1,32 +1,32 @@ require("playercards/PlayerCardSpawner") -- Allows spawning of defined lists of cards which will be created from the template in the All --- Player Cards bag. SpawnBag.spawn will create objects based on a table definition, while --- SpawnBag.recall will clean them all up. Recall will be limited to a small area around the --- spawned objects. Objects moved out of this area will not be cleaned up. +-- Player Cards bag. SpawnBag.spawn will create objects based on a table definition, while +-- SpawnBag.recall will clean them all up. Recall will be limited to a small area around the +-- spawned objects. Objects moved out of this area will not be cleaned up. -- -- SpawnSpec: Spawning requires a spawn specification with the following structure: -- { --- name: Name of this spawn content, used for internal tracking. Multiple specs can be spawned, +-- name: Name of this spawn content, used for internal tracking. Multiple specs can be spawned, -- but each requires a separate name -- cards: A list of card IDs to be spawned --- globalPos: Where the spawned objects should be placed, in global coordinates. This should be +-- globalPos: Where the spawned objects should be placed, in global coordinates. This should be -- 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 +-- 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 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 +-- 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 +-- 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 allCardsBagApi = require("playercards/AllCardsBagApi") - local SpawnBag = { } - local internal = { } + 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 @@ -36,8 +36,8 @@ do local RECALL_BUFFER_Z = 0.5 -- In order to mimic the behavior of the previous memory buttons we use a temporary bag when - -- recalling objects. This bag is tiny and transparent, and will be placed at the same location as - -- this object. Once all placed cards are recalled bag to this bag, it will be destroyed + -- recalling objects. This bag is tiny and transparent, and will be placed at the same location as + -- this object. Once all placed cards are recalled bag to this bag, it will be destroyed local RECALL_BAG = { Name = "Bag", Transform = { @@ -58,8 +58,8 @@ do } -- Tracks what has been placed by this "bag" so they can be recalled - local placedSpecs = { } - local placedObjectGuids = { } + local placedSpecs = {} + local placedObjectGuids = {} local recallZone = nil -- Loads a table of saved state, extracted during the parent object's onLoad @@ -81,21 +81,13 @@ do -- Places the given spawnSpec on the table. See comment at the start of the file for spawnSpec table data and examples SpawnBag.spawn = function(spawnSpec) -- Limit to one placement at a time - if (placedSpecs[spawnSpec.name]) then - return - end - if (spawnSpec == nil) then - -- TODO: error here - return - end - local cardsToSpawn = { } - local cardList = spawnSpec.cards - for _, cardId in ipairs(cardList) do + if placedSpecs[spawnSpec.name] or spawnSpec == nil then return end + + local cardsToSpawn = {} + for _, cardId in ipairs(spawnSpec.cards) do local cardData = allCardsBagApi.getCardById(cardId) - if (cardData ~= nil) then + if cardData ~= nil then table.insert(cardsToSpawn, cardData) - else - -- TODO: error here end end if (spawnSpec.spread) then @@ -120,14 +112,13 @@ do internal.recallSpawned() end - -- We've recalled everything we can, some cards may have been moved out of the - -- card area. Just reset at this point. - placedSpecs = { } - placedObjectGuids = { } + -- We've recalled everything we can, some cards may have been moved out of the card area. Just reset at this point. + placedSpecs = {} + placedObjectGuids = {} recallZone = nil end - -- Deleted all spawned cards. + -- Delete all spawned cards internal.deleteSpawned = function() for guid, _ in pairs(placedObjectGuids) do local obj = getObjectFromGUID(guid) @@ -140,9 +131,9 @@ do end end - -- Recalls spawned cards with a fake bag that replicates the memory bag recall style. + -- 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()}) + local trash = spawnObjectData({ data = RECALL_BAG, position = self.getPosition() }) for guid, _ in pairs(placedObjectGuids) do local obj = getObjectFromGUID(guid) if (obj ~= nil) then @@ -156,71 +147,70 @@ do trash.destruct() end - - -- Callback for when an object has been spawned. Tracks the object for later recall and updates the - -- recall zone. + -- Callback for when an object has been spawned. Tracks the object for later recall and updates the recall zone. internal.recordPlacedObject = function(spawned) placedObjectGuids[spawned.getGUID()] = true internal.expandRecallZone(spawned) end - -- Expands the current recall zone based on the position of the given object. The recall zone will + -- 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 internal.expandRecallZone = function(spawnedCard) local pos = spawnedCard.getPosition() if (recallZone == nil) then -- First card out of the bag, initialize surrounding that - recallZone = { } + recallZone = {} recallZone.upperLeft = { x = pos.x + RECALL_BUFFER_X, z = pos.z + RECALL_BUFFER_Z } recallZone.lowerRight = { x = pos.x - RECALL_BUFFER_X, z = pos.z - RECALL_BUFFER_Z } return - else - if (pos.x > recallZone.upperLeft.x) then - recallZone.upperLeft.x = pos.x + RECALL_BUFFER_X - end - if (pos.x < recallZone.lowerRight.x) then - recallZone.lowerRight.x = pos.x - RECALL_BUFFER_X - end - if (pos.z > recallZone.upperLeft.z) then - recallZone.upperLeft.z = pos.z + RECALL_BUFFER_Z - end - if (pos.z < recallZone.lowerRight.z) then - recallZone.lowerRight.z = pos.z - RECALL_BUFFER_Z - end end - if (SHOW_RECALL_ZONE) then + + if pos.x > recallZone.upperLeft.x then + recallZone.upperLeft.x = pos.x + RECALL_BUFFER_X + end + if pos.x < recallZone.lowerRight.x then + recallZone.lowerRight.x = pos.x - RECALL_BUFFER_X + end + if pos.z > recallZone.upperLeft.z then + recallZone.upperLeft.z = pos.z + RECALL_BUFFER_Z + end + if pos.z < recallZone.lowerRight.z then + recallZone.lowerRight.z = pos.z - RECALL_BUFFER_Z + end + + if SHOW_RECALL_ZONE then local y = 1.5 local thick = 0.05 Global.setVectorLines({ { - points = { {recallZone.upperLeft.x,y,recallZone.upperLeft.z}, {recallZone.upperLeft.x,y,recallZone.lowerRight.z} }, - color = {1,0,0}, + points = { { recallZone.upperLeft.x, y, recallZone.upperLeft.z }, { recallZone.upperLeft.x, y, recallZone.lowerRight.z } }, + color = { 1, 0, 0 }, thickness = thick, - rotation = {0,0,0} + rotation = { 0, 0, 0 } }, { - points = { {recallZone.upperLeft.x,y,recallZone.lowerRight.z}, {recallZone.lowerRight.x,y,recallZone.lowerRight.z} }, - color = {1,0,0}, + points = { { recallZone.upperLeft.x, y, recallZone.lowerRight.z }, { recallZone.lowerRight.x, y, recallZone.lowerRight.z } }, + color = { 1, 0, 0 }, thickness = thick, - rotation = {0,0,0} + rotation = { 0, 0, 0 } }, { - points = { {recallZone.lowerRight.x,y,recallZone.lowerRight.z}, {recallZone.lowerRight.x,y,recallZone.upperLeft.z} }, - color = {1,0,0}, + points = { { recallZone.lowerRight.x, y, recallZone.lowerRight.z }, { recallZone.lowerRight.x, y, recallZone.upperLeft.z } }, + color = { 1, 0, 0 }, thickness = thick, - rotation = {0,0,0} + rotation = { 0, 0, 0 } }, { - points = { {recallZone.lowerRight.x,y,recallZone.upperLeft.z}, {recallZone.upperLeft.x,y,recallZone.upperLeft.z} }, - color = {1,0,0}, + points = { { recallZone.lowerRight.x, y, recallZone.upperLeft.z }, { recallZone.upperLeft.x, y, recallZone.upperLeft.z } }, + color = { 1, 0, 0 }, thickness = thick, - rotation = {0,0,0} + rotation = { 0, 0, 0 } } }) end end - -- Checks to see if the given object is in the current recall zone. If there isn't a recall zone, + -- 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. internal.isInRecallZone = function(obj) if (recallZone == nil) then @@ -228,11 +218,11 @@ do end local pos = obj.getPosition() return (pos.x < recallZone.upperLeft.x and pos.x > recallZone.lowerRight.x - and pos.z < recallZone.upperLeft.z and pos.z > recallZone.lowerRight.z) + and pos.z < recallZone.upperLeft.z and pos.z > recallZone.lowerRight.z) end internal.reverseList = function(list) - local reversed = { } + local reversed = {} for i = 1, #list do reversed[i] = list[#list - i + 1] end diff --git a/xml/playercards/PlayerCardPanel.xml b/xml/playercards/PlayerCardPanel.xml index 17ecfb2c..d193129f 100644 --- a/xml/playercards/PlayerCardPanel.xml +++ b/xml/playercards/PlayerCardPanel.xml @@ -1,24 +1,31 @@ - - • Select a group to place cards • Copy the cards you want for your deck • Select a new group to clear the placed cards and see new ones • Clear to remove all cards + + + \ No newline at end of file From 5b38ea1c0200df9cfa83dd61ae12c4bfe3f6641e Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Sat, 25 May 2024 00:40:18 +0200 Subject: [PATCH 2/6] added sorting for custom cards --- src/playercards/AllCardsBag.ttslua | 111 +++++++++++++++++++------ src/playercards/AllCardsBagApi.ttslua | 4 +- src/playercards/PlayerCardPanel.ttslua | 26 +++++- 3 files changed, 111 insertions(+), 30 deletions(-) diff --git a/src/playercards/AllCardsBag.ttslua b/src/playercards/AllCardsBag.ttslua index 2355b2f4..95175ccf 100644 --- a/src/playercards/AllCardsBag.ttslua +++ b/src/playercards/AllCardsBag.ttslua @@ -12,9 +12,9 @@ function onLoad() Wait.frames(startIndexBuild, 30) end --- Called by Hotfix bags when they load. If we are still loading indexes, then +-- Called by Hotfix bags when they load. If we are still loading indexes, then -- the all cards and hotfix bags are being loaded together, and we can ignore --- this call as the hotfix will be included in the initial indexing. If it is +-- this call as the hotfix will be included in the initial indexing. If it is -- called once indexing is complete it means the hotfix bag has been added -- later, and we should rebuild the index to integrate the hotfix bag. function rebuildIndexForHotfix() @@ -170,12 +170,16 @@ function buildSupplementalIndexes() -- override cycle name for night of the zealot cycleName = cycleName:gsub("the night of the zealot", "core") - - if cycleIndex[cycleName] == nil then - cycleIndex[cycleName] = { } - end - table.insert(cycleIndex[cycleName], cardMetadata.id) + else + -- track cards without defined cycle (should only be fan-made cards) + cycleName = "other" end + + -- maybe initialize table + if cycleIndex[cycleName] == nil then + cycleIndex[cycleName] = { } + end + table.insert(cycleIndex[cycleName], cardMetadata.id) end end end @@ -220,7 +224,7 @@ end -- Params table: -- id: String ID of the card to retrieve -- Return: If the indexes are still being constructed, an empty table is --- returned. Otherwise, a single table with the following fields +-- returned. Otherwise, a single table with the following fields -- cardData: TTS object data, suitable for spawning the card -- cardMetadata: Table of parsed metadata function getCardById(params) @@ -240,21 +244,80 @@ function getCardsByClassAndLevel(params) if not isIndexReady() then return {} end local upgradeKey - if (params.upgraded) then + if params.upgraded then upgradeKey = "-upgrade" else upgradeKey = "-level0" end - return classAndLevelIndex[params.class..upgradeKey]; + return classAndLevelIndex[params.class..upgradeKey] end -function getCardsByCycle(cycleName) +function getCardsByCycle(params) if not isIndexReady() then return {} end - return cycleIndex[string.lower(cycleName)] + + if not params.sortByMetadata then + return cycleIndex[string.lower(params.cycle)] + end + + -- sort list by metadata (useful for custom cards without proper IDs) + local cardList = {} + for _, id in ipairs(cycleIndex[string.lower(params.cycle)]) do + table.insert(cardList, id) + end + + table.sort(cardList, metadataSortFunction) + return cardList end --- Searches the bag for cards which match the given name and returns a list. Note that this is --- an O(n) search without index support. It may be slow. +-- sorts cards by metadata: class, type, level, name and then description +function metadataSortFunction(id1, id2) + local card1 = cardIdIndex[id1] + local card2 = cardIdIndex[id2] + + -- extract class per card + local classValue1 = getClassValueFromString(card1.metadata.class) + local classValue2 = getClassValueFromString(card2.metadata.class) + + -- conversion tables to simplify type sorting + local typeConversion = { + Asset = 1, + Event = 2, + Skill = 3 + } + + if classValue1 ~= classValue2 then + return classValue1 < classValue2 + elseif typeConversion[card1.metadata.type] ~= typeConversion[card2.metadata.type] then + return typeConversion[card1.metadata.type] < typeConversion[card2.metadata.type] + elseif card1.metadata.level ~= card2.metadata.level then + return card1.metadata.level < card2.metadata.level + elseif card1.data.Nickname ~= card2.data.Nickname then + return card1.data.Nickname < card2.data.Nickname + else + return card1.data.Description < card2.data.Description + end +end + +-- helper function to calculate the class value for sorting from the "|" separated string +function getClassValueFromString(s) + local classValueList = { + Guardian = 1, + Seeker = 2, + Rogue = 3, + Mystic = 4, + Survivor = 5, + Neutral = 6 + } + local classValue = 0 + for str in s:gmatch("([^|]+)") do + -- this sorts multiclass cards + classValue = classValue * 10 + classValueList[str] + end + return classValue +end + +-- Searches the bag for cards which match the given name and returns a list. Note that this is +-- an O(n) search without index support. It may be slow. -- Parameter array must contain these fields to define the search: -- name String or string fragment to search for names -- exact Whether the name match should be exact @@ -276,14 +339,14 @@ function getCardsByName(params) return results end --- Gets a random basic weakness from the bag. Once a given ID has been returned +-- Gets a random basic weakness from the bag. Once a given ID has been returned -- it will be removed from the list and cannot be selected again until a reload -- occurs or the indexes are rebuilt, which will refresh the list to include all -- weaknesses. -- Return: String ID of the selected weakness. function getRandomWeaknessId() - local availableWeaknesses = buildAvailableWeaknesses() - if (#availableWeaknesses > 0) then + local availableWeaknesses = buildAvailableWeaknesses() + if #availableWeaknesses > 0 then return availableWeaknesses[math.random(#availableWeaknesses)] end end @@ -295,14 +358,12 @@ function buildAvailableWeaknesses() local weaknessesInPlay = { } local allObjects = getAllObjects() for _, object in ipairs(allObjects) do - if (object.name == "Deck") then + if object.type == "Deck" then for _, cardData in ipairs(object.getData().ContainedObjects) do - local cardMetadata = JSON.decode(cardData.GMNotes) - incrementWeaknessCount(weaknessesInPlay, cardMetadata) + incrementWeaknessCount(weaknessesInPlay, JSON.decode(cardData.GMNotes)) end - elseif (object.name == "Card") then - local cardMetadata = JSON.decode(object.getGMNotes()) - incrementWeaknessCount(weaknessesInPlay, cardMetadata) + elseif object.type == "Card" then + incrementWeaknessCount(weaknessesInPlay, JSON.decode(object.getGMNotes())) end end @@ -327,8 +388,8 @@ 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 - if (table[cardMetadata.id] == nil) then + if isBasicWeakness(cardMetadata) then + if table[cardMetadata.id] == nil then table[cardMetadata.id] = 1 else table[cardMetadata.id] = table[cardMetadata.id] + 1 diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index c7baf29f..2d6801d1 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -70,8 +70,8 @@ do return returnCopyOfList(getAllCardsBag().call("getCardsByClassAndLevel", { class = class, upgraded = upgraded })) end - AllCardsBagApi.getCardsByCycle = function(cycle) - return returnCopyOfList(getAllCardsBag().call("getCardsByCycle", cycle)) + AllCardsBagApi.getCardsByCycle = function(cycle, sortByMetadata) + return returnCopyOfList(getAllCardsBag().call("getCardsByCycle", { cycle = cycle, sortByMetadata = sortByMetadata })) end AllCardsBagApi.getUniqueWeaknesses = function() diff --git a/src/playercards/PlayerCardPanel.ttslua b/src/playercards/PlayerCardPanel.ttslua index f84bb340..90b2e1b6 100644 --- a/src/playercards/PlayerCardPanel.ttslua +++ b/src/playercards/PlayerCardPanel.ttslua @@ -425,7 +425,7 @@ end ---@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") + printToAll("No investigator data for " .. groupName .. " yet") return end @@ -434,7 +434,7 @@ function spawnInvestigators(groupName) local investigatorCount = #INVESTIGATOR_GROUPS[groupName] local position = getInvestigatorRowStartPos(investigatorCount, row) - for i, investigatorName in ipairs(INVESTIGATOR_GROUPS[groupName]) do + for _, investigatorName in ipairs(INVESTIGATOR_GROUPS[groupName]) do for _, spawnSpec in ipairs(buildInvestigatorSpawnSpec(investigatorName, INVESTIGATORS[investigatorName], position)) do spawnBag.spawn(spawnSpec) end @@ -625,9 +625,15 @@ function spawnCycle(cycle) prepareToPlaceCards() spawnInvestigators(cycle) + -- sort custom cards + local sortByMetadata = false + if cycle == "Other" then + sortByMetadata = true + end + spawnBag.spawn({ name = "cycle" .. cycle, - cards = allCardsBagApi.getCardsByCycle(cycle), + cards = allCardsBagApi.getCardsByCycle(cycle, sortByMetadata), globalPos = self.positionToWorld(startPositions.cycle), rotation = FACE_UP_ROTATION, spread = true, @@ -635,6 +641,20 @@ function spawnCycle(cycle) }) end +-- Comparison function used to sort the class card bag indexes. Sorts by card level, then name, then subname. +function cardComparator(id1, id2) + local card1 = cardIdIndex[id1] + local card2 = cardIdIndex[id2] + + if card1.metadata.level ~= card2.metadata.level then + return card1.metadata.level < card2.metadata.level + elseif card1.data.Nickname ~= card2.data.Nickname then + return card1.data.Nickname < card2.data.Nickname + else + return card1.data.Description < card2.data.Description + end +end + function spawnBonded() prepareToPlaceCards() spawnBag.spawn({ From eb63d0a532573204323accc0efc37a33eb9a76e4 Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Sat, 25 May 2024 00:52:01 +0200 Subject: [PATCH 3/6] added some documentation --- src/playercards/AllCardsBag.ttslua | 39 ++++++++++++++++----------- src/playercards/AllCardsBagApi.ttslua | 35 ++++++++++++++---------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/playercards/AllCardsBag.ttslua b/src/playercards/AllCardsBag.ttslua index 95175ccf..e555848d 100644 --- a/src/playercards/AllCardsBag.ttslua +++ b/src/playercards/AllCardsBag.ttslua @@ -131,6 +131,7 @@ function addCardToIndex(cardData) end end +-- Creates the supplemental indexes for classes, weaknesses etc. function buildSupplementalIndexes() for cardId, card in pairs(cardIdIndex) do local cardMetadata = card.metadata @@ -213,6 +214,7 @@ function cardComparator(id1, id2) end end +---@return boolean: If true, the bag is currently not indexing and ready to be accessed function isIndexReady() if not indexingDone then broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) @@ -221,25 +223,24 @@ function isIndexReady() end -- Returns a specific card from the bag, based on ArkhamDB ID --- Params table: --- id: String ID of the card to retrieve --- Return: If the indexes are still being constructed, an empty table is --- returned. Otherwise, a single table with the following fields --- cardData: TTS object data, suitable for spawning the card --- cardMetadata: Table of parsed metadata -function getCardById(params) +---@param id string ID of the card to retrieve +---@return table: If the indexes are still being constructed, returns an empty table. +-- Otherwise, a single table with the following fields +-- cardData: TTS object data, suitable for spawning the card +-- cardMetadata: Table of parsed metadata +function getCardById(id) if not isIndexReady() then return {} end - return cardIdIndex[params.id] + return cardIdIndex[id] end -- Returns a list of cards from the bag matching a class and level (0 or upgraded) --- Params table: --- class: String class to retrieve ("Guardian", "Seeker", etc) --- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0 --- Return: If the indexes are still being constructed, returns an empty table. --- Otherwise, a list of tables, each with the following fields --- cardData: TTS object data, suitable for spawning the card --- cardMetadata: Table of parsed metadata +---@param params table +-- class: String class to retrieve ("Guardian", "Seeker", etc) +-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0 +---@return table: If the indexes are still being constructed, returns an empty table. +-- Otherwise, a list of tables, each with the following fields +-- cardData: TTS object data, suitable for spawning the card +-- cardMetadata: Table of parsed metadata function getCardsByClassAndLevel(params) if not isIndexReady() then return {} end @@ -252,6 +253,14 @@ function getCardsByClassAndLevel(params) return classAndLevelIndex[params.class..upgradeKey] end +-- Returns a list of cards from the bag matching a cycle +---@param params table +-- cycle: String cycle to retrieve ("The Scarlet Keys" etc.) +-- sortByMetadata: true to sort the table by metadata instead of ID +---@return table: If the indexes are still being constructed, returns an empty table. +-- Otherwise, a list of tables, each with the following fields +-- cardData: TTS object data, suitable for spawning the card +-- cardMetadata: Table of parsed metadata function getCardsByCycle(params) if not isIndexReady() then return {} end diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index 2d6801d1..03bfabe3 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -6,6 +6,7 @@ do return guidReferenceApi.getObjectByOwnerAndType("Mythos", "AllCardsBag") end + -- internal function to create a copy of the table to avoid operating on variables owned by different objects local function returnCopyOfList(data) local copiedList = {} for _, id in ipairs(data) do @@ -15,17 +16,16 @@ do end -- Returns a specific card from the bag, based on ArkhamDB ID - ---@param id table String ID of the card to retrieve - ---@return table table - -- If the indexes are still being constructed, an empty table is - -- returned. Otherwise, a single table with the following fields + ---@param id string ID of the card to retrieve + ---@return table: If the indexes are still being constructed, returns an empty table. + -- Otherwise, a single table with the following fields -- cardData: TTS object data, suitable for spawning the card -- cardMetadata: Table of parsed metadata AllCardsBagApi.getCardById = function(id) - return getAllCardsBag().call("getCardById", { id = id }) + return getAllCardsBag().call("getCardById", id) end - -- Gets a random basic weakness from the bag. Once a given ID has been returned + -- Gets a random basic weakness from the bag. Once a given ID has been returned -- it will be removed from the list and cannot be selected again until a reload -- occurs or the indexes are rebuilt, which will refresh the list to include all -- weaknesses. @@ -38,17 +38,17 @@ do return getAllCardsBag().call("isIndexReady") end - -- Called by Hotfix bags when they load. If we are still loading indexes, then + -- Called by Hotfix bags when they load. If we are still loading indexes, then -- the all cards and hotfix bags are being loaded together, and we can ignore - -- this call as the hotfix will be included in the initial indexing. If it is + -- this call as the hotfix will be included in the initial indexing. If it is -- called once indexing is complete it means the hotfix bag has been added -- later, and we should rebuild the index to integrate the hotfix bag. AllCardsBagApi.rebuildIndexForHotfix = function() getAllCardsBag().call("rebuildIndexForHotfix") end - -- Searches the bag for cards which match the given name and returns a list. Note that this is - -- an O(n) search without index support. It may be slow. + -- Searches the bag for cards which match the given name and returns a list. Note that this is + -- an O(n) search without index support. It may be slow. ---@param name string or string fragment to search for names ---@param exact boolean Whether the name match should be exact AllCardsBagApi.getCardsByName = function(name, exact) @@ -61,15 +61,22 @@ do -- Returns a list of cards from the bag matching a class and level (0 or upgraded) ---@param class string class to retrieve ("Guardian", "Seeker", etc) - ---@param upgraded boolean true for upgraded cards (Level 1-5), false for Level 0 + ---@param upgraded boolean True for upgraded cards (Level 1-5), false for Level 0 ---@return table: If the indexes are still being constructed, returns an empty table. - -- Otherwise, a list of tables, each with the following fields - -- cardData: TTS object data, suitable for spawning the card - -- cardMetadata: Table of parsed metadata + -- Otherwise, a list of tables, each with the following fields + -- cardData: TTS object data, suitable for spawning the card + -- cardMetadata: Table of parsed metadata AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded) return returnCopyOfList(getAllCardsBag().call("getCardsByClassAndLevel", { class = class, upgraded = upgraded })) end + -- Returns a list of cards from the bag matching a cycle + ---@param cycle string Cycle to retrieve ("The Scarlet Keys" etc.) + ---@param sortByMetadata boolean If true, sorts the table by metadata instead of ID + ---@return table: If the indexes are still being constructed, returns an empty table. + -- Otherwise, a list of tables, each with the following fields + -- cardData: TTS object data, suitable for spawning the card + -- cardMetadata: Table of parsed metadata AllCardsBagApi.getCardsByCycle = function(cycle, sortByMetadata) return returnCopyOfList(getAllCardsBag().call("getCardsByCycle", { cycle = cycle, sortByMetadata = sortByMetadata })) end From acbd1ea9229fa3de09058bc63d8f461d7193adf6 Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Mon, 27 May 2024 15:20:55 +0200 Subject: [PATCH 4/6] added image for other cards --- objects/PlayerCards.2d30ee.json | 7 +++++++ xml/playercards/PlayerCardPanel.xml | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/objects/PlayerCards.2d30ee.json b/objects/PlayerCards.2d30ee.json index 8608be69..a623b186 100644 --- a/objects/PlayerCards.2d30ee.json +++ b/objects/PlayerCards.2d30ee.json @@ -22,6 +22,13 @@ "ImageURL": "http://cloud-3.steamusercontent.com/ugc/2342503777940937086/92256BDF101E6272AD1E3F5F0043D311DF708F03/", "WidthScale": 0 }, + "CustomUIAssets": [ + { + "Name": "OtherCards", + "Type": 0, + "URL": "http://cloud-3.steamusercontent.com/ugc/2446096169989812196/B5C491331EB348C261F561DC7A19968ECF9FC74A/" + } + ], "Description": "", "DragSelectable": true, "GMNotes": "", diff --git a/xml/playercards/PlayerCardPanel.xml b/xml/playercards/PlayerCardPanel.xml index d193129f..b1f900d1 100644 --- a/xml/playercards/PlayerCardPanel.xml +++ b/xml/playercards/PlayerCardPanel.xml @@ -26,6 +26,6 @@ height="200" width="200" scale="0.1 0.1 1" - color="#FFFFFF" onClick="spawnOtherCards"> + \ No newline at end of file From fbd70c0f74342be76270e5e1e88e31f2becdc58d Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Tue, 28 May 2024 01:02:36 +0200 Subject: [PATCH 5/6] updated XML --- objects/PlayerCards.2d30ee.json | 2 +- src/playercards/AllCardsBag.ttslua | 32 ++++++--- src/playercards/AllCardsBagApi.ttslua | 21 +++--- src/playercards/PlayerCardPanel.ttslua | 83 ++++++++++++++++++------ src/playercards/PlayerCardSpawner.ttslua | 10 +-- src/playercards/SpawnBag.ttslua | 8 +-- xml/playercards/PlayerCardPanel.xml | 31 --------- 7 files changed, 106 insertions(+), 81 deletions(-) delete mode 100644 xml/playercards/PlayerCardPanel.xml diff --git a/objects/PlayerCards.2d30ee.json b/objects/PlayerCards.2d30ee.json index a623b186..606c796d 100644 --- a/objects/PlayerCards.2d30ee.json +++ b/objects/PlayerCards.2d30ee.json @@ -60,5 +60,5 @@ "scaleZ": 10 }, "Value": 0, - "XmlUI": "\u003cInclude src=\"playercards/PlayerCardPanel.xml\"/\u003e" + "XmlUI": "" } diff --git a/src/playercards/AllCardsBag.ttslua b/src/playercards/AllCardsBag.ttslua index e555848d..bbfed2c2 100644 --- a/src/playercards/AllCardsBag.ttslua +++ b/src/playercards/AllCardsBag.ttslua @@ -1,3 +1,5 @@ +local guidReferenceApi = require("core/GUIDReferenceApi") + local cardIdIndex = { } local classAndLevelIndex = { } local basicWeaknessList = { } @@ -5,6 +7,7 @@ local uniqueWeaknessList = { } local cycleIndex = { } local indexingDone = false +local otherCardsDetected = false function onLoad() self.addContextMenuItem("Rebuild Index", startIndexBuild) @@ -63,6 +66,7 @@ end function buildIndex() local cardCount = 0 indexingDone = false + otherCardsDetected = false -- process the allcardsbag itself for _, cardData in ipairs(self.getData().ContainedObjects) do @@ -108,6 +112,7 @@ function buildIndex() end buildSupplementalIndexes() + updatePlayerCardPanel() indexingDone = true return 1 end @@ -174,6 +179,7 @@ function buildSupplementalIndexes() else -- track cards without defined cycle (should only be fan-made cards) cycleName = "other" + otherCardsDetected = true end -- maybe initialize table @@ -214,6 +220,12 @@ function cardComparator(id1, id2) end end +-- inform the player card panel about the presence of other cards (no cycle -> fan-made) +function updatePlayerCardPanel() + local panel = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayerCardPanel") + panel.call("createXML", otherCardsDetected) +end + ---@return boolean: If true, the bag is currently not indexing and ready to be accessed function isIndexReady() if not indexingDone then @@ -223,14 +235,14 @@ function isIndexReady() end -- Returns a specific card from the bag, based on ArkhamDB ID ----@param id string ID of the card to retrieve +---@param params table ID of the card to retrieve ---@return table: If the indexes are still being constructed, returns an empty table. -- Otherwise, a single table with the following fields --- cardData: TTS object data, suitable for spawning the card --- cardMetadata: Table of parsed metadata -function getCardById(id) +-- data: TTS object data, suitable for spawning the card +-- metadata: Table of parsed metadata +function getCardById(params) if not isIndexReady() then return {} end - return cardIdIndex[id] + return cardIdIndex[params.id] end -- Returns a list of cards from the bag matching a class and level (0 or upgraded) @@ -239,8 +251,8 @@ end -- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0 ---@return table: If the indexes are still being constructed, returns an empty table. -- Otherwise, a list of tables, each with the following fields --- cardData: TTS object data, suitable for spawning the card --- cardMetadata: Table of parsed metadata +-- data: TTS object data, suitable for spawning the card +-- metadata: Table of parsed metadata function getCardsByClassAndLevel(params) if not isIndexReady() then return {} end @@ -250,7 +262,7 @@ function getCardsByClassAndLevel(params) else upgradeKey = "-level0" end - return classAndLevelIndex[params.class..upgradeKey] + return classAndLevelIndex[params.class .. upgradeKey] end -- Returns a list of cards from the bag matching a cycle @@ -259,8 +271,8 @@ end -- sortByMetadata: true to sort the table by metadata instead of ID ---@return table: If the indexes are still being constructed, returns an empty table. -- Otherwise, a list of tables, each with the following fields --- cardData: TTS object data, suitable for spawning the card --- cardMetadata: Table of parsed metadata +-- data: TTS object data, suitable for spawning the card +-- metadata: Table of parsed metadata function getCardsByCycle(params) if not isIndexReady() then return {} end diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index 03bfabe3..e43e693a 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -19,16 +19,15 @@ do ---@param id string ID of the card to retrieve ---@return table: If the indexes are still being constructed, returns an empty table. -- Otherwise, a single table with the following fields - -- cardData: TTS object data, suitable for spawning the card - -- cardMetadata: Table of parsed metadata + -- data: TTS object data, suitable for spawning the card + -- metadata: Table of parsed metadata AllCardsBagApi.getCardById = function(id) - return getAllCardsBag().call("getCardById", id) + return getAllCardsBag().call("getCardById", { id = id }) end - -- Gets a random basic weakness from the bag. Once a given ID has been returned - -- it will be removed from the list and cannot be selected again until a reload - -- occurs or the indexes are rebuilt, which will refresh the list to include all - -- weaknesses. + -- Gets a random basic weakness from the bag. Once a given ID has been returned it + -- will be removed from the list and cannot be selected again until a reload occurs + -- or the indexes are rebuilt, which will refresh the list to include all weaknesses. ---@return string: ID of the selected weakness. AllCardsBagApi.getRandomWeaknessId = function() return getAllCardsBag().call("getRandomWeaknessId") @@ -64,8 +63,8 @@ do ---@param upgraded boolean True for upgraded cards (Level 1-5), false for Level 0 ---@return table: If the indexes are still being constructed, returns an empty table. -- Otherwise, a list of tables, each with the following fields - -- cardData: TTS object data, suitable for spawning the card - -- cardMetadata: Table of parsed metadata + -- data: TTS object data, suitable for spawning the card + -- metadata: Table of parsed metadata AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded) return returnCopyOfList(getAllCardsBag().call("getCardsByClassAndLevel", { class = class, upgraded = upgraded })) end @@ -75,8 +74,8 @@ do ---@param sortByMetadata boolean If true, sorts the table by metadata instead of ID ---@return table: If the indexes are still being constructed, returns an empty table. -- Otherwise, a list of tables, each with the following fields - -- cardData: TTS object data, suitable for spawning the card - -- cardMetadata: Table of parsed metadata + -- data: TTS object data, suitable for spawning the card + -- metadata: Table of parsed metadata AllCardsBagApi.getCardsByCycle = function(cycle, sortByMetadata) return returnCopyOfList(getAllCardsBag().call("getCardsByCycle", { cycle = cycle, sortByMetadata = sortByMetadata })) end diff --git a/src/playercards/PlayerCardPanel.ttslua b/src/playercards/PlayerCardPanel.ttslua index 90b2e1b6..dcefb509 100644 --- a/src/playercards/PlayerCardPanel.ttslua +++ b/src/playercards/PlayerCardPanel.ttslua @@ -333,6 +333,70 @@ function createInvestigatorModeButtons() }) end +function createXML(showOtherCardsButton) + -- basic XML for the help button + local xmlTable = { + { + tag = "Panel", + attributes = { + active = "false", + id = "helpPanel", + position = "-165 -70 -2", + rotation = "0 0 180", + height = "50", + width = "107", + color = "#00000099" + }, + children = { + tag = "Text", + attributes = { + id = "helpText", + rectAlignment = "MiddleCenter", + height = "480", + width = "1000", + scale = "0.1 0.1 1", + fontSize = "66", + color = "#F5F5F5", + backgroundColor = "#FF0000", + alignment = "MiddleLeft", + horizontalOverflow = "wrap", + text = "• Select a group to place cards\n" .. + "• Copy the cards you want for your deck\n" .. + "• Select a new group to clear the placed cards and see new ones\n" .. + "• Clear to remove all cards" + } + } + } + } + + -- add the "Additional Cards" button if cards without cycle were detected + if showOtherCardsButton then + local otherCardsButtonXml = { + tag = "Panel", + attributes = { + position = "44.25 65.75 -11", + rotation = "0 0 180", + height = "225", + width = "225", + scale = "0.1 0.1 1", + onClick = "spawnOtherCards" + }, + children = { + tag = "Image", + attributes = { image = "OtherCards" } + } + } + table.insert(xmlTable, otherCardsButtonXml) + end + helpVisibleToPlayers = {} + self.UI.setXmlTable(xmlTable) +end + +-- click function for the XML button for the additional player cards +function spawnOtherCards() + spawnCycle("Other") +end + function toggleHelp(_, playerColor, _) if helpVisibleToPlayers[playerColor] then helpVisibleToPlayers[playerColor] = nil @@ -612,11 +676,6 @@ function placeClassCards(cardClass, isUpgraded) end end --- called by the XML UI to spawn cards from fan-made camnpaigns -function spawnOtherCards() - spawnCycle("Other") -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) @@ -641,20 +700,6 @@ function spawnCycle(cycle) }) end --- Comparison function used to sort the class card bag indexes. Sorts by card level, then name, then subname. -function cardComparator(id1, id2) - local card1 = cardIdIndex[id1] - local card2 = cardIdIndex[id2] - - if card1.metadata.level ~= card2.metadata.level then - return card1.metadata.level < card2.metadata.level - elseif card1.data.Nickname ~= card2.data.Nickname then - return card1.data.Nickname < card2.data.Nickname - else - return card1.data.Description < card2.data.Description - end -end - function spawnBonded() prepareToPlaceCards() spawnBag.spawn({ diff --git a/src/playercards/PlayerCardSpawner.ttslua b/src/playercards/PlayerCardSpawner.ttslua index 1fbfdc0e..e0501619 100644 --- a/src/playercards/PlayerCardSpawner.ttslua +++ b/src/playercards/PlayerCardSpawner.ttslua @@ -16,7 +16,7 @@ Spawner = { } ---@param sort boolean True if this list of cards should be sorted before spawning ---@param callback? function Callback to be called after the card/deck spawns. Spawner.spawnCards = function(cardList, pos, rot, sort, callback) - if (sort) then + if sort then table.sort(cardList, Spawner.cardComparator) end @@ -25,9 +25,9 @@ Spawner.spawnCards = function(cardList, pos, rot, sort, callback) local investigatorCards = { } for _, card in ipairs(cardList) do - if (card.metadata.type == "Investigator") then + if card.metadata.type == "Investigator" then table.insert(investigatorCards, card) - elseif (card.metadata.type == "Minicard") then + elseif card.metadata.type == "Minicard" then table.insert(miniCards, card) else table.insert(standardCards, card) @@ -46,7 +46,7 @@ Spawner.spawnCards = function(cardList, pos, rot, sort, callback) end Spawner.spawnCardSpread = function(cardList, startPos, maxCols, rot, sort, callback) - if (sort) then + if sort then table.sort(cardList, Spawner.cardComparator) end @@ -201,7 +201,7 @@ end ---@return string id >= startId Spawner.findNextAvailableId = function(objectTable, startId) local id = startId - while (objectTable[id] ~= nil) do + while objectTable[id] ~= nil do id = tostring(tonumber(id) + 1) end return id diff --git a/src/playercards/SpawnBag.ttslua b/src/playercards/SpawnBag.ttslua index 982d489c..31db3b9d 100644 --- a/src/playercards/SpawnBag.ttslua +++ b/src/playercards/SpawnBag.ttslua @@ -85,12 +85,12 @@ do local cardsToSpawn = {} for _, cardId in ipairs(spawnSpec.cards) do - local cardData = allCardsBagApi.getCardById(cardId) - if cardData ~= nil then - table.insert(cardsToSpawn, cardData) + local card = allCardsBagApi.getCardById(cardId) + if card ~= nil then + table.insert(cardsToSpawn, card) end end - if (spawnSpec.spread) then + if spawnSpec.spread then Spawner.spawnCardSpread(cardsToSpawn, spawnSpec.globalPos, spawnSpec.spreadCols or 9999, spawnSpec.rotation, false, internal.recordPlacedObject) else -- TTS decks come out in reverse order of the cards, reverse the list so the input order stays diff --git a/xml/playercards/PlayerCardPanel.xml b/xml/playercards/PlayerCardPanel.xml deleted file mode 100644 index b1f900d1..00000000 --- a/xml/playercards/PlayerCardPanel.xml +++ /dev/null @@ -1,31 +0,0 @@ - - -• Select a group to place cards -• Copy the cards you want for your deck -• Select a new group to clear the placed cards and see new ones -• Clear to remove all cards - - - - - \ No newline at end of file From b2d9ef5cd65704eb32085e66f5eca2cfc75c1521 Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Wed, 29 May 2024 00:30:43 +0200 Subject: [PATCH 6/6] fixed bug with index + formatting --- src/playercards/AllCardsBag.ttslua | 129 +++++++++++++------------- src/playercards/AllCardsBagApi.ttslua | 6 +- 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/src/playercards/AllCardsBag.ttslua b/src/playercards/AllCardsBag.ttslua index c08978e2..0e961400 100644 --- a/src/playercards/AllCardsBag.ttslua +++ b/src/playercards/AllCardsBag.ttslua @@ -1,10 +1,10 @@ local guidReferenceApi = require("core/GUIDReferenceApi") -local cardIdIndex = { } -local classAndLevelIndex = { } -local basicWeaknessList = { } -local uniqueWeaknessList = { } -local cycleIndex = { } +local cardIdIndex = {} +local classAndLevelIndex = {} +local basicWeaknessList = {} +local uniqueWeaknessList = {} +local cycleIndex = {} local indexingDone = false local otherCardsDetected = false @@ -29,23 +29,23 @@ end -- Resets all current bag indexes function clearIndexes() indexingDone = false - cardIdIndex = { } - classAndLevelIndex = { } - classAndLevelIndex["Guardian-upgrade"] = { } - classAndLevelIndex["Seeker-upgrade"] = { } - classAndLevelIndex["Mystic-upgrade"] = { } - classAndLevelIndex["Survivor-upgrade"] = { } - classAndLevelIndex["Rogue-upgrade"] = { } - classAndLevelIndex["Neutral-upgrade"] = { } - classAndLevelIndex["Guardian-level0"] = { } - classAndLevelIndex["Seeker-level0"] = { } - classAndLevelIndex["Mystic-level0"] = { } - classAndLevelIndex["Survivor-level0"] = { } - classAndLevelIndex["Rogue-level0"] = { } - classAndLevelIndex["Neutral-level0"] = { } - cycleIndex = { } - basicWeaknessList = { } - uniqueWeaknessList = { } + cardIdIndex = {} + classAndLevelIndex = {} + classAndLevelIndex["Guardian-upgrade"] = {} + classAndLevelIndex["Seeker-upgrade"] = {} + classAndLevelIndex["Mystic-upgrade"] = {} + classAndLevelIndex["Survivor-upgrade"] = {} + classAndLevelIndex["Rogue-upgrade"] = {} + classAndLevelIndex["Neutral-upgrade"] = {} + classAndLevelIndex["Guardian-level0"] = {} + classAndLevelIndex["Seeker-level0"] = {} + classAndLevelIndex["Mystic-level0"] = {} + classAndLevelIndex["Survivor-level0"] = {} + classAndLevelIndex["Rogue-level0"] = {} + classAndLevelIndex["Neutral-level0"] = {} + cycleIndex = {} + basicWeaknessList = {} + uniqueWeaknessList = {} end -- Clears the bag indexes and starts the coroutine to rebuild the indexes @@ -88,8 +88,8 @@ function buildIndex() end for _, cardData in ipairs(hotfixData.ContainedObjects) do - -- process containers if cardData.ContainedObjects then + -- process containers for _, deepCardData in ipairs(cardData.ContainedObjects) do addCardToIndex(deepCardData) cardCount = cardCount + 1 @@ -98,8 +98,8 @@ function buildIndex() coroutine.yield(0) end end - -- process single cards else + -- process single cards addCardToIndex(cardData) cardCount = cardCount + 1 if cardCount > 19 then @@ -121,15 +121,19 @@ end ---@param cardData table TTS object data for the card function addCardToIndex(cardData) -- using the more efficient 'json.parse()' to speed this process up - local status, cardMetadata = pcall(function() json.parse(cardData.GMNotes) end) + local status, cardMetadata = pcall(function() return json.parse(cardData.GMNotes) end) -- if an error happens, fallback to the regular parser - if status ~= true then + if status ~= true or cardMetadata == nil then + log("Fast parser failed for " .. cardData.Nickname .. ", using old parser instead.") cardMetadata = JSON.decode(cardData.GMNotes) end -- if metadata was not valid JSON or empty, don't add the card - if not cardMetadata then return end + if not cardMetadata == nil then + log("Error parsing " .. cardData.Nickname) + return + end -- use the ZoopGuid as fallback if no id present cardMetadata.id = cardMetadata.id or cardMetadata.TtsZoopGuid @@ -146,35 +150,32 @@ end -- Creates the supplemental indexes for classes, weaknesses etc. function buildSupplementalIndexes() for cardId, card in pairs(cardIdIndex) do - local cardMetadata = card.metadata -- If the ID key and the metadata ID don't match this is a duplicate card created by an alternate_id, and we should skip it - if cardId == cardMetadata.id then + if cardId == card.metadata.id then -- Add card to the basic weakness list, if appropriate. Some weaknesses have multiple copies, and are added multiple times - if cardMetadata.weakness then - table.insert(uniqueWeaknessList, cardMetadata.id) - if cardMetadata.basicWeaknessCount ~= nil then - for i = 1, cardMetadata.basicWeaknessCount do - table.insert(basicWeaknessList, cardMetadata.id) + if card.metadata.weakness then + table.insert(uniqueWeaknessList, card.metadata.id) + if card.metadata.basicWeaknessCount ~= nil then + for i = 1, card.metadata.basicWeaknessCount do + table.insert(basicWeaknessList, card.metadata.id) end end end -- Excludes signature cards (which have no class or level) - if cardMetadata.class ~= nil and cardMetadata.level ~= nil then - local upgradeKey - if cardMetadata.level > 0 then + if card.metadata.class ~= nil and card.metadata.level ~= nil then + local upgradeKey = "-level0" + if card.metadata.level > 0 then upgradeKey = "-upgrade" - else - upgradeKey = "-level0" end -- parse classes (separated by "|") and add the card to the appropriate class and level indices - for str in cardMetadata.class:gmatch("([^|]+)") do - table.insert(classAndLevelIndex[str .. upgradeKey], cardMetadata.id) + for str in card.metadata.class:gmatch("([^|]+)") do + table.insert(classAndLevelIndex[str .. upgradeKey], card.metadata.id) end -- add to cycle index - local cycleName = cardMetadata.cycle + local cycleName = card.metadata.cycle if cycleName ~= nil then cycleName = string.lower(cycleName) @@ -191,9 +192,9 @@ function buildSupplementalIndexes() -- maybe initialize table if cycleIndex[cycleName] == nil then - cycleIndex[cycleName] = { } + cycleIndex[cycleName] = {} end - table.insert(cycleIndex[cycleName], cardMetadata.id) + table.insert(cycleIndex[cycleName], card.metadata.id) end end end @@ -236,7 +237,7 @@ end ---@return boolean: If true, the bag is currently not indexing and ready to be accessed function isIndexReady() if not indexingDone then - broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) + broadcastToAll("Still loading player cards, please try again in a few seconds", { 0.9, 0.2, 0.2 }) end return indexingDone end @@ -263,11 +264,9 @@ end function getCardsByClassAndLevel(params) if not isIndexReady() then return {} end - local upgradeKey + local upgradeKey = "-level0" if params.upgraded then upgradeKey = "-upgrade" - else - upgradeKey = "-level0" end return classAndLevelIndex[params.class .. upgradeKey] end @@ -347,31 +346,31 @@ end -- Searches the bag for cards which match the given name and returns a list. Note that this is -- an O(n) search without index support. It may be slow. -- Parameter array must contain these fields to define the search: --- name String or string fragment to search for names --- exact Whether the name match should be exact +-- name: String or string fragment to search for names +-- exact: Whether the name match should be exact function getCardsByName(params) local name = params.name local exact = params.exact - local results = { } + local results = {} + -- Track cards (by ID) that we've added to avoid duplicates that may come from alternate IDs - local addedCards = { } + local addedCards = {} for _, cardData in pairs(cardIdIndex) do if (not addedCards[cardData.metadata.id]) then if (exact and (string.lower(cardData.data.Nickname) == string.lower(name))) or (not exact and string.find(string.lower(cardData.data.Nickname), string.lower(name), 1, true)) then - table.insert(results, cardData) - addedCards[cardData.metadata.id] = true + table.insert(results, cardData) + addedCards[cardData.metadata.id] = true end end end return results end --- Gets a random basic weakness from the bag. Once a given ID has been returned --- it will be removed from the list and cannot be selected again until a reload --- occurs or the indexes are rebuilt, which will refresh the list to include all --- weaknesses. --- Return: String ID of the selected weakness. +-- Gets a random basic weakness from the bag. Once a given ID has been returned it will be +-- removed from the list and cannot be selected again until a reload occurs or the indexes +-- are rebuilt, which will refresh the list to include all weaknesses. +---@return string: ID of the selected weakness function getRandomWeaknessId() local availableWeaknesses = buildAvailableWeaknesses() if #availableWeaknesses > 0 then @@ -381,9 +380,9 @@ end -- Constructs a list of available basic weaknesses by starting with the full pool of basic -- weaknesses then removing any which are currently in the play or deck construction areas --- Return: Table array of weakness IDs which are valid to choose from +---@return table: Array of weakness IDs which are valid to choose from function buildAvailableWeaknesses() - local weaknessesInPlay = { } + local weaknessesInPlay = {} local allObjects = getAllObjects() for _, object in ipairs(allObjects) do if object.type == "Deck" then @@ -395,7 +394,7 @@ function buildAvailableWeaknesses() end end - local availableWeaknesses = { } + local availableWeaknesses = {} for _, weaknessId in ipairs(basicWeaknessList) do if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1 @@ -427,7 +426,7 @@ end function isBasicWeakness(cardMetadata) return cardMetadata ~= nil - and cardMetadata.weakness - and cardMetadata.basicWeaknessCount ~= nil - and cardMetadata.basicWeaknessCount > 0 + and cardMetadata.weakness + and cardMetadata.basicWeaknessCount ~= nil + and cardMetadata.basicWeaknessCount > 0 end diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index e43e693a..f2f4cc3b 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -28,7 +28,7 @@ do -- Gets a random basic weakness from the bag. Once a given ID has been returned it -- will be removed from the list and cannot be selected again until a reload occurs -- or the indexes are rebuilt, which will refresh the list to include all weaknesses. - ---@return string: ID of the selected weakness. + ---@return string: ID of the selected weakness AllCardsBagApi.getRandomWeaknessId = function() return getAllCardsBag().call("getRandomWeaknessId") end @@ -46,8 +46,8 @@ do getAllCardsBag().call("rebuildIndexForHotfix") end - -- Searches the bag for cards which match the given name and returns a list. Note that this is - -- an O(n) search without index support. It may be slow. + -- Searches the bag for cards which match the given name and returns a list. + -- Note that this is an O(n) search without index support. It may be slow. ---@param name string or string fragment to search for names ---@param exact boolean Whether the name match should be exact AllCardsBagApi.getCardsByName = function(name, exact)