diff --git a/src/arkhamdb/ArkhamDb.ttslua b/src/arkhamdb/ArkhamDb.ttslua index 691e9d65..1e9bfcbd 100644 --- a/src/arkhamdb/ArkhamDb.ttslua +++ b/src/arkhamdb/ArkhamDb.ttslua @@ -191,11 +191,11 @@ do slots[RANDOM_WEAKNESS_ID] = nil if randomWeaknessAmount > 0 then - for i = 1, randomWeaknessAmount do - local weaknessId = allCardsBagApi.getRandomWeaknessId(restrictions) + local weaknessIds = allCardsBagApi.getRandomWeaknessIds(randomWeaknessAmount, restrictions) + for _, weaknessId in ipairs(weaknessIds) do slots[weaknessId] = (slots[weaknessId] or 0) + 1 end - internal.maybePrint("Added " .. randomWeaknessAmount .. " random basic weakness(es) to deck", playerColor) + internal.maybePrint("Added " .. #weaknessIds .. " random basic weakness(es) to deck", playerColor) end end diff --git a/src/arkhamdb/DeckImporter.ttslua b/src/arkhamdb/DeckImporter.ttslua index 33b2a20d..5bcebb3a 100644 --- a/src/arkhamdb/DeckImporter.ttslua +++ b/src/arkhamdb/DeckImporter.ttslua @@ -25,10 +25,10 @@ local STANDALONE_TOGGLE_LABELS = {} STANDALONE_TOGGLE_LABELS[true] = "Yes" STANDALONE_TOGGLE_LABELS[false] = "No" -local redDeckId = "" -local orangeDeckId = "" -local whiteDeckId = "" -local greenDeckId = "" +redDeckId = "" +orangeDeckId = "" +whiteDeckId = "" +greenDeckId = "" local privateDeck = true local loadNewestDeck = true @@ -123,7 +123,7 @@ function makeDeckIdFields() iParams.width = INPUT_FIELD_WIDTH iParams.height = INPUT_FIELD_HEIGHT iParams.font_size = 320 - iParams.tooltip = "Deck ID from ArkhamDB URL of the deck\nPublic URL: 'https://arkhamdb.com/decklist/view/101/knowledge-overwhelming-solo-deck-1.0' = '101'\nPrivate URL: 'https://arkhamdb.com/deck/view/102' = '102'\n\nAlso supports the deck ID from shared decks from arkham.build!" + iParams.tooltip = "Deck ID from ArkhamDB URL of the deck\nPublished URL: 'https://arkhamdb.com/decklist/view/101/knowledge-overwhelming-solo-deck-1.0' = '101'\nPrivate URL: 'https://arkhamdb.com/deck/view/102' = '102'\n\nAlso supports the deck ID from shared decks from arkham.build!" iParams.alignment = 3 -- Center iParams.color = FIELD_COLOR iParams.font_color = { 0, 0, 0 } @@ -190,19 +190,28 @@ function standaloneChanged() end function loadDecks() + co = coroutine.create(loadDecksCoroutine) + resumeLoadDecks() +end + +function loadDecksCoroutine() if not allCardsBagApi.isIndexReady() then return end matsWithInvestigator = playermatApi.getUsedMatColors() - if redDeckId ~= nil and redDeckId ~= "" then - buildDeck("Red", redDeckId) + + for _, matColor in ipairs({"White", "Orange", "Green", "Red"}) do + local deckId = _G[string.lower(matColor) .. "DeckId"] + if deckId ~= nil and deckId ~= "" then + buildDeck(matColor, deckId) + coroutine.yield() + end end - if orangeDeckId ~= nil and orangeDeckId ~= "" then - buildDeck("Orange", orangeDeckId) - end - if whiteDeckId ~= nil and whiteDeckId ~= "" then - buildDeck("White", whiteDeckId) - end - if greenDeckId ~= nil and greenDeckId ~= "" then - buildDeck("Green", greenDeckId) +end + +function resumeLoadDecks() + log("resume") + if co and coroutine.status(co) ~= "dead" then + local status, err = coroutine.resume(co) + if not status then error(err) end end end @@ -345,6 +354,7 @@ function loadCards(slots, investigatorId, bondedList, customizations, playerColo if (not hadError) then printToAll("Deck loaded successfully!", playerColor) end + resumeLoadDecks() return 1 end diff --git a/src/playercards/AllCardsBag.ttslua b/src/playercards/AllCardsBag.ttslua index b0aa65ab..9e6d6f50 100644 --- a/src/playercards/AllCardsBag.ttslua +++ b/src/playercards/AllCardsBag.ttslua @@ -370,18 +370,34 @@ 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. ----@param restrictions? table Additional restrictions: ---- class string Class to restrict weakness to ---- standalone boolean Whether 'Campaign only' weaknesses should be exluded ---- traits? string Trait(s) to use as filter ----@return string: ID of the selected weakness -function getRandomWeaknessId(restrictions) - local availableWeaknesses = buildAvailableWeaknesses(restrictions) - if #availableWeaknesses > 0 then - return availableWeaknesses[math.random(#availableWeaknesses)] - else - broadcastToAll("No basic weakness available!", { 0.9, 0.2, 0.2 }) +---@param params table Bundled parameters: +--- count number Number of weaknesses +--- restrictions table Additional restrictions: +--- class string Class to restrict weakness to +--- standalone boolean Whether 'Campaign only' weaknesses should be exluded +--- traits? string Trait(s) to use as filter +---@return table: Table with IDs of the selected weaknesses +function getRandomWeaknessIds(params) + params.count = params.count or 1 + local availableWeaknesses = buildAvailableWeaknesses(params.restrictions) + + -- check if enough weaknesses are available + local missingWeaknesses = params.count - #availableWeaknesses + if missingWeaknesses > 0 then + broadcastToAll("Not enough basic weaknesses available! (" .. missingWeaknesses .. " missing)", { 0.9, 0.2, 0.2 }) end + + local drawnWeaknesses = {} + + -- Fisher-Yates shuffle algorithm + local n = #availableWeaknesses + for i = 1, math.min(params.count, n) do + local index = math.random(i, n) + table.insert(drawnWeaknesses, availableWeaknesses[index]) + availableWeaknesses[index], availableWeaknesses[i] = availableWeaknesses[i], availableWeaknesses[index] + end + + return drawnWeaknesses end -- Constructs a list of available basic weaknesses by starting with the full pool of basic diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index b62b1592..2a69145b 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -28,13 +28,14 @@ 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. + ---@param count number Number of weaknesses ---@param restrictions table Additional restrictions: --- class string Class to restrict weakness to --- standalone boolean Whether 'Campaign only' weaknesses should be exluded --- traits? string Trait(s) to use as filter - ---@return string: ID of the selected weakness - AllCardsBagApi.getRandomWeaknessId = function(restrictions) - return getAllCardsBag().call("getRandomWeaknessId", restrictions) + ---@return table: Table with IDs of the selected weaknesses + AllCardsBagApi.getRandomWeaknessIds = function(count, restrictions) + return returnCopyOfList(getAllCardsBag().call("getRandomWeaknessIds", {count = count, restrictions = restrictions})) end AllCardsBagApi.isIndexReady = function() diff --git a/src/playercards/PlayerCardPanel.ttslua b/src/playercards/PlayerCardPanel.ttslua index c2404696..51427e6a 100644 --- a/src/playercards/PlayerCardPanel.ttslua +++ b/src/playercards/PlayerCardPanel.ttslua @@ -782,17 +782,17 @@ function spawnRandomWeakness(_, playerColor, isRightClick) prepareToPlaceCards() if not isRightClick then - local weaknessId = allCardsBagApi.getRandomWeaknessId() - if weaknessId then - spawnSingleWeakness(weaknessId) + local weaknessIds = allCardsBagApi.getRandomWeaknessIds(1) + if weaknessIds[1] then + spawnSingleWeakness(weaknessIds[1]) end else Player[playerColor].showInputDialog("Specify a trait for the weakness (split multiple eligible traits with '|'):", lastWeaknessTrait, function(text) lastWeaknessTrait = text - local weaknessId = allCardsBagApi.getRandomWeaknessId({ traits = text }) - if weaknessId then - spawnSingleWeakness(weaknessId) + local weaknessIds = allCardsBagApi.getRandomWeaknessIds(1, { traits = text }) + if weaknessIds[1] then + spawnSingleWeakness(weaknessIds[1]) end end) end