Merge branch 'main' into playermat-xml

This commit is contained in:
Chr1Z93 2024-07-28 22:18:46 +02:00
commit 430c584cc5
6 changed files with 183 additions and 113 deletions

View File

@ -4,7 +4,7 @@ do
local ArkhamDb = {} local ArkhamDb = {}
local internal = {} local internal = {}
local tabooList = {} local tabooList = {}
local configuration local configuration
@ -69,6 +69,11 @@ do
deckId deckId
} }
-- use secondary api (arkham.build) if deckid is too long
if string.len(deckId) == 15 then
deckUri = { configuration.api_uri2, deckId }
end
local deck = Request.start(deckUri, function(status) local deck = Request.start(deckUri, function(status)
if string.find(status.text, "<!DOCTYPE html>") then if string.find(status.text, "<!DOCTYPE html>") then
internal.maybePrint("Private deck ID " .. deckId .. " is not shared.", playerColor) internal.maybePrint("Private deck ID " .. deckId .. " is not shared.", playerColor)
@ -94,11 +99,11 @@ do
---@param cardId string ArkhamDB ID of the card that could not be found ---@param cardId string ArkhamDB ID of the card that could not be found
---@param playerColor string Color of the player's deck that had the problem ---@param playerColor string Color of the player's deck that had the problem
ArkhamDb.logCardNotFound = function(cardId, playerColor) ArkhamDb.logCardNotFound = function(cardId, playerColor)
local request = Request.start({ Request.start({
configuration.api_uri, configuration.api_uri,
configuration.cards, configuration.cards,
cardId cardId
}, },
function(result) function(result)
local adbCardInfo = JSON.decode(internal.fixUtf16String(result.text)) local adbCardInfo = JSON.decode(internal.fixUtf16String(result.text))
local cardName = adbCardInfo.real_name local cardName = adbCardInfo.real_name
@ -360,8 +365,8 @@ do
---@param slots table The slot list for cards in this deck. Table key is the cardId, value is the number of those cards which will be spawned ---@param slots table The slot list for cards in this deck. Table key is the cardId, value is the number of those cards which will be spawned
internal.extractBondedCards = function(slots) internal.extractBondedCards = function(slots)
-- Create a list of bonded cards first so we don't modify slots while iterating -- Create a list of bonded cards first so we don't modify slots while iterating
local bondedCards = { } local bondedCards = {}
local bondedList = { } local bondedList = {}
for cardId, cardCount in pairs(slots) do for cardId, cardCount in pairs(slots) do
local card = allCardsBagApi.getCardById(cardId) local card = allCardsBagApi.getCardById(cardId)
if card ~= nil and card.metadata.bonded ~= nil then if card ~= nil and card.metadata.bonded ~= nil then
@ -534,8 +539,7 @@ do
if self.is_successful then if self.is_successful then
callback(self.content, table.unpack(arguments)) callback(self.content, table.unpack(arguments))
end end
end, function() return self.is_done end, function() return self.is_done end)
end)
end end
return ArkhamDb return ArkhamDb

View File

@ -1,9 +1,9 @@
---@type table Contains fields used by the deck importer ---@type table Contains fields used by the deck importer
configuration = { configuration = {
api_uri = "https://arkhamdb.com/api/public", api_uri = "https://arkhamdb.com/api/public",
api_uri2 = "https://api.arkham.build/v1/public/share",
public_deck = "decklist", public_deck = "decklist",
private_deck = "deck", private_deck = "deck",
cards = "card", cards = "card",
taboo = "taboos", taboo = "taboos"
card_bag_guid = "15bb07"
} }

View File

@ -84,85 +84,85 @@ end
function makeOptionToggles() function makeOptionToggles()
-- common parameters -- common parameters
local checkbox_parameters = {} local checkboxParameters = {}
checkbox_parameters.function_owner = self checkboxParameters.function_owner = self
checkbox_parameters.width = INPUT_FIELD_WIDTH checkboxParameters.width = INPUT_FIELD_WIDTH
checkbox_parameters.height = INPUT_FIELD_HEIGHT checkboxParameters.height = INPUT_FIELD_HEIGHT
checkbox_parameters.scale = { 0.1, 0.1, 0.1 } checkboxParameters.scale = { 0.1, 0.1, 0.1 }
checkbox_parameters.font_size = 240 checkboxParameters.font_size = 240
checkbox_parameters.hover_color = { 0.4, 0.6, 0.8 } checkboxParameters.hover_color = { 0.4, 0.6, 0.8 }
checkbox_parameters.color = FIELD_COLOR checkboxParameters.color = FIELD_COLOR
-- public / private deck -- public / private deck
checkbox_parameters.click_function = "publicPrivateChanged" checkboxParameters.click_function = "publicPrivateChanged"
checkbox_parameters.position = { 0.25, 0.1, -0.102 } checkboxParameters.position = { 0.25, 0.1, -0.102 }
checkbox_parameters.tooltip = "Published or private deck?\n\nPLEASE USE A PRIVATE DECK IF JUST FOR TTS TO AVOID FLOODING ARKHAMDB PUBLISHED DECK LISTS!" checkboxParameters.tooltip = "Published or private deck?\n\nPLEASE USE A PRIVATE DECK IF JUST FOR TTS TO AVOID FLOODING ARKHAMDB PUBLISHED DECK LISTS!"
checkbox_parameters.label = PRIVATE_TOGGLE_LABELS[privateDeck] checkboxParameters.label = PRIVATE_TOGGLE_LABELS[privateDeck]
self.createButton(checkbox_parameters) self.createButton(checkboxParameters)
-- load upgraded? -- load upgraded?
checkbox_parameters.click_function = "loadUpgradedChanged" checkboxParameters.click_function = "loadUpgradedChanged"
checkbox_parameters.position = { 0.25, 0.1, -0.01 } checkboxParameters.position = { 0.25, 0.1, -0.01 }
checkbox_parameters.tooltip = "Load newest upgrade or exact deck?" checkboxParameters.tooltip = "Load newest upgrade or exact deck?"
checkbox_parameters.label = UPGRADED_TOGGLE_LABELS[loadNewestDeck] checkboxParameters.label = UPGRADED_TOGGLE_LABELS[loadNewestDeck]
self.createButton(checkbox_parameters) self.createButton(checkboxParameters)
-- load investigators? -- load investigators?
checkbox_parameters.click_function = "loadInvestigatorsChanged" checkboxParameters.click_function = "loadInvestigatorsChanged"
checkbox_parameters.position = { 0.25, 0.1, 0.081 } checkboxParameters.position = { 0.25, 0.1, 0.081 }
checkbox_parameters.tooltip = "Spawn investigator cards?" checkboxParameters.tooltip = "Spawn investigator cards?"
checkbox_parameters.label = LOAD_INVESTIGATOR_TOGGLE_LABELS[loadInvestigators] checkboxParameters.label = LOAD_INVESTIGATOR_TOGGLE_LABELS[loadInvestigators]
self.createButton(checkbox_parameters) self.createButton(checkboxParameters)
end end
-- Create the four deck ID entry fields -- Create the four deck ID entry fields
function makeDeckIdFields() function makeDeckIdFields()
local input_parameters = {} local inputParameters = {}
-- Parameters common to all entry fields -- Parameters common to all entry fields
input_parameters.function_owner = self inputParameters.function_owner = self
input_parameters.scale = { 0.1, 0.1, 0.1 } inputParameters.scale = { 0.1, 0.1, 0.1 }
input_parameters.width = INPUT_FIELD_WIDTH inputParameters.width = INPUT_FIELD_WIDTH
input_parameters.height = INPUT_FIELD_HEIGHT inputParameters.height = INPUT_FIELD_HEIGHT
input_parameters.font_size = 320 inputParameters.font_size = 320
input_parameters.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'" inputParameters.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!"
input_parameters.alignment = 3 -- Center inputParameters.alignment = 3 -- Center
input_parameters.color = FIELD_COLOR inputParameters.color = FIELD_COLOR
input_parameters.font_color = { 0, 0, 0 } inputParameters.font_color = { 0, 0, 0 }
input_parameters.validation = 2 -- Integer inputParameters.validation = 4 -- alphanumeric (to support arkham.build IDs)
-- Green -- Green
input_parameters.input_function = "greenDeckChanged" inputParameters.input_function = "greenDeckChanged"
input_parameters.position = { -0.166, 0.1, 0.385 } inputParameters.position = { -0.166, 0.1, 0.385 }
input_parameters.value = greenDeckId inputParameters.value = greenDeckId
self.createInput(input_parameters) self.createInput(inputParameters)
-- Red -- Red
input_parameters.input_function = "redDeckChanged" inputParameters.input_function = "redDeckChanged"
input_parameters.position = { 0.171, 0.1, 0.385 } inputParameters.position = { 0.171, 0.1, 0.385 }
input_parameters.value = redDeckId inputParameters.value = redDeckId
self.createInput(input_parameters) self.createInput(inputParameters)
-- White -- White
input_parameters.input_function = "whiteDeckChanged" inputParameters.input_function = "whiteDeckChanged"
input_parameters.position = { -0.166, 0.1, 0.474 } inputParameters.position = { -0.166, 0.1, 0.474 }
input_parameters.value = whiteDeckId inputParameters.value = whiteDeckId
self.createInput(input_parameters) self.createInput(inputParameters)
-- Orange -- Orange
input_parameters.input_function = "orangeDeckChanged" inputParameters.input_function = "orangeDeckChanged"
input_parameters.position = { 0.171, 0.1, 0.474 } inputParameters.position = { 0.171, 0.1, 0.474 }
input_parameters.value = orangeDeckId inputParameters.value = orangeDeckId
self.createInput(input_parameters) self.createInput(inputParameters)
end end
-- Create the Build All button. This is a transparent button which covers the Build All portion of the background graphic -- Create the Build All button. This is a transparent button which covers the Build All portion of the background graphic
function makeBuildButton() function makeBuildButton()
local button_parameters = {} local buttonParameters = {}
button_parameters.click_function = "loadDecks" buttonParameters.click_function = "loadDecks"
button_parameters.function_owner = self buttonParameters.function_owner = self
button_parameters.position = { 0, 0.1, 0.71 } buttonParameters.position = { 0, 0.1, 0.71 }
button_parameters.width = 320 buttonParameters.width = 320
button_parameters.height = 30 buttonParameters.height = 30
button_parameters.color = { 0, 0, 0, 0 } buttonParameters.color = { 0, 0, 0, 0 }
button_parameters.tooltip = "Click to build all four decks!" buttonParameters.tooltip = "Click to build all four decks!"
self.createButton(button_parameters) self.createButton(buttonParameters)
end end
-- Event handlers for deck ID change -- Event handlers for deck ID change
@ -174,17 +174,17 @@ function greenDeckChanged(_, _, inputValue) greenDeckId = inputValue end
-- Event handlers for toggle buttons -- Event handlers for toggle buttons
function publicPrivateChanged() function publicPrivateChanged()
privateDeck = not privateDeck privateDeck = not privateDeck
self.editButton { index = 0, label = PRIVATE_TOGGLE_LABELS[privateDeck] } self.editButton({ index = 0, label = PRIVATE_TOGGLE_LABELS[privateDeck] })
end end
function loadUpgradedChanged() function loadUpgradedChanged()
loadNewestDeck = not loadNewestDeck loadNewestDeck = not loadNewestDeck
self.editButton { index = 1, label = UPGRADED_TOGGLE_LABELS[loadNewestDeck] } self.editButton({ index = 1, label = UPGRADED_TOGGLE_LABELS[loadNewestDeck] })
end end
function loadInvestigatorsChanged() function loadInvestigatorsChanged()
loadInvestigators = not loadInvestigators loadInvestigators = not loadInvestigators
self.editButton { index = 2, label = LOAD_INVESTIGATOR_TOGGLE_LABELS[loadInvestigators] } self.editButton({ index = 2, label = LOAD_INVESTIGATOR_TOGGLE_LABELS[loadInvestigators] })
end end
function loadDecks() function loadDecks()
@ -243,12 +243,12 @@ end
function buildDeck(playerColor, deckId) function buildDeck(playerColor, deckId)
local uiState = getUiState() local uiState = getUiState()
arkhamDb.getDecklist( arkhamDb.getDecklist(
playerColor, playerColor,
deckId, deckId,
uiState.privateDeck, uiState.privateDeck,
uiState.loadNewest, uiState.loadNewest,
uiState.investigators, uiState.investigators,
loadCards) loadCards)
end end
-- Process the slot list, which defines the card Ids and counts of cards to load. Spawn those cards -- Process the slot list, which defines the card Ids and counts of cards to load. Spawn those cards
@ -360,9 +360,9 @@ function deckSpawned(deck, playerColor)
-- Process in reverse order so taking cards out doesn't upset the indexing -- Process in reverse order so taking cards out doesn't upset the indexing
for i = #deckCards, 1, -1 do for i = #deckCards, 1, -1 do
local cardMetadata = JSON.decode(deckCards[i].GMNotes) or { } local cardMetadata = JSON.decode(deckCards[i].GMNotes) or {}
if cardMetadata.startsInHand then if cardMetadata.startsInHand then
deck.takeObject({ index = i - 1, position = handPos, flip = true, smooth = true}) deck.takeObject({ index = i - 1, position = handPos, flip = true, smooth = true })
end end
end end
@ -556,9 +556,10 @@ function handleHunchDeck(investigatorId, cardList, bondedList, playerColor)
table.insert(insightList, i) table.insert(insightList, i)
end end
end end
-- Process cards to move them to the hunch deck. This is done in reverse order because the sorting needs
-- to be reversed (deck sorts for face down). Performance here may be an issue, as table.remove() is an O(n) -- Process cards to move them to the hunch deck. This is done in reverse order because the sorting needs
-- operation which makes the full shift O(n^2). But keep it simple unless it becomes a problem -- to be reversed (deck sorts for face down). Performance here may be an issue, as table.remove() is an O(n)
-- operation which makes the full shift O(n^2). But keep it simple unless it becomes a problem
for i = #insightList, 1, -1 do for i = #insightList, 1, -1 do
local moving = cardList[insightList[i]] local moving = cardList[insightList[i]]
moving.zone = "SetAside5" moving.zone = "SetAside5"
@ -663,7 +664,7 @@ function handleCustomizableUpgrades(cardList, customizations)
row = tonumber(str) + 1 row = tonumber(str) + 1
elseif counter == 2 then elseif counter == 2 then
if selectedUpgrades[row] == nil then if selectedUpgrades[row] == nil then
selectedUpgrades[row] = { } selectedUpgrades[row] = {}
end end
selectedUpgrades[row].xp = tonumber(str) selectedUpgrades[row].xp = tonumber(str)
elseif counter == 3 and str ~= "" then elseif counter == 3 and str ~= "" then
@ -674,7 +675,7 @@ function handleCustomizableUpgrades(cardList, customizations)
elseif baseId == "09079" then -- Living Ink skill selection elseif baseId == "09079" then -- Living Ink skill selection
-- All skills, regardless of row, are placed in upgrade slot 1 as a comma-delimited list -- All skills, regardless of row, are placed in upgrade slot 1 as a comma-delimited list
if selectedUpgrades[1] == nil then if selectedUpgrades[1] == nil then
selectedUpgrades[1] = { } selectedUpgrades[1] = {}
end end
if selectedUpgrades[1].text == nil then if selectedUpgrades[1].text == nil then
selectedUpgrades[1].text = str selectedUpgrades[1].text = str

View File

@ -103,9 +103,11 @@ function takeCardIntoThreatArea(playerColor, hoveredObject)
modifierY = -90 modifierY = -90
end end
-- contruct feedback message
local cardName = hoveredObject.getName() local cardName = hoveredObject.getName()
if cardName == "" then cardName = "card" end if cardName == "" then cardName = "a card" end
broadcastToAll("Moved " .. cardName .. " to " .. getColoredName(playerColor) .. "'s threat area.", "White") local playerName = getColoredName(playerColor)
broadcastToAll("Moved " .. cardName .. " to " .. playerName .. "'s threat area.", "White")
-- get new rotation (rounded) -- get new rotation (rounded)
local cardRot = hoveredObject.getRotation() local cardRot = hoveredObject.getRotation()
@ -266,27 +268,66 @@ function removeOneUse(playerColor, hoveredObject)
if hoveredObject.type == "Tile" then if hoveredObject.type == "Tile" then
targetObject = hoveredObject targetObject = hoveredObject
elseif hoveredObject.type == "Card" then elseif hoveredObject.type == "Card" then
-- grab the first use type from the metadata (or nil) local searchResult = searchLib.onObject(hoveredObject, "isTileOrToken")
local notes = JSON.decode(hoveredObject.getGMNotes()) or {}
local usesData = notes.uses or {}
local useInfo = usesData[1] or {}
local searchForType = useInfo.type
if searchForType then searchForType = searchForType:lower() end
for _, obj in ipairs(searchLib.onObject(hoveredObject, "isTileOrToken")) do if #searchResult == 0 then
if not obj.locked and obj.memo ~= "resourceCounter" then broadcastToColor("No tokens found!", playerColor, "Yellow")
-- check for matching object, otherwise use the first hit return
if obj.memo and obj.memo == searchForType then end
targetObject = obj
break -- index the found tokens by memo (only the first of each type)
elseif not targetObject then local indexByMemo = {}
targetObject = obj for _, obj in ipairs(searchResult) do
if not obj.locked then
if obj.memo and indexByMemo[obj.memo] == nil then
indexByMemo[obj.memo] = obj
elseif indexByMemo["NO_MEMO"] == nil then
indexByMemo["NO_MEMO"] = obj
end end
end end
end end
-- use metadata (if present) to determine targetObject
local usesAreTypeOfResource = false
local notes = JSON.decode(hoveredObject.getGMNotes()) or {}
for _, useInfo in ipairs(notes.uses or {}) do
if useInfo.type then
local discardMemo = useInfo.type:lower()
if indexByMemo[discardMemo] then
targetObject = indexByMemo[discardMemo]
break
end
end
if useInfo.token == "resource" then
usesAreTypeOfResource = true
end
end
-- check for alternatives (check resources first if tokens are a type of resource)
if not targetObject then
if usesAreTypeOfResource and indexByMemo["resource"] then
targetObject = indexByMemo["resource"]
else
for memo, obj in pairs(indexByMemo) do
if memo ~= "resourceCounter" and memo ~= "NO_MEMO" then
targetObject = obj
break
end
end
end
end
-- if there's still not a target check for clickable counter and token without memo
if not targetObject then
if indexByMemo["resourceCounter"] then
indexByMemo["resourceCounter"].call("modifyValue", -1)
return
elseif indexByMemo["NO_MEMO"] then
targetObject = indexByMemo["NO_MEMO"]
end
end
end end
-- error handling
if not targetObject then if not targetObject then
broadcastToColor("No tokens found!", playerColor, "Yellow") broadcastToColor("No tokens found!", playerColor, "Yellow")
return return
@ -307,6 +348,16 @@ function removeOneUse(playerColor, hoveredObject)
end end
-- feedback message -- feedback message
local cardName
if hoveredObject.type == "Card" then
cardName = hoveredObject.getName()
else
local searchResult = searchLib.belowPosition(targetObject.getPosition(), "isCard")
if #searchResult > 0 then
cardName = searchResult[1].getName()
end
end
local tokenName = targetObject.getName() local tokenName = targetObject.getName()
if tokenName == "" then if tokenName == "" then
if targetObject.memo ~= "" then if targetObject.memo ~= "" then
@ -317,15 +368,23 @@ function removeOneUse(playerColor, hoveredObject)
else else
tokenName = "Clue" tokenName = "Clue"
end end
elseif targetObject.memo == "resourceCounter" then
tokenName = "Resource Counter"
else else
tokenName = titleCase(targetObject.memo) tokenName = titleCase(targetObject.memo)
end end
else else
tokenName = "Unknown" tokenName = "unknown token"
end end
end end
broadcastToAll(getColoredName(playerColor) .. " removed a token: " .. tokenName, playerColor) -- construct feedback message
local playerName = getColoredName(playerColor)
local cardInfo = ""
if cardName and cardName ~= "" then
cardInfo = " from " .. cardName
end
broadcastToAll(playerName .. " removed a token (" .. tokenName .. ")".. cardInfo .. ".", "White")
local discardForMatColor = getColorToDiscardFor(hoveredObject, playerColor) local discardForMatColor = getColorToDiscardFor(hoveredObject, playerColor)
playermatApi.discardListOfObjects(discardForMatColor, { targetObject }) playermatApi.discardListOfObjects(discardForMatColor, { targetObject })
@ -437,7 +496,7 @@ function takeClueFromLocation(playerColor, hoveredObject)
end end
elseif hoveredObject.type == "Infinite" and hoveredObject.getName() == "Clue tokens" then elseif hoveredObject.type == "Infinite" and hoveredObject.getName() == "Clue tokens" then
clue = hoveredObject.takeObject() clue = hoveredObject.takeObject()
cardName = "token pool" cardName = "the token pool"
else else
broadcastToColor("Hover a clue or card with clues and try again.", messageColor, "Yellow") broadcastToColor("Hover a clue or card with clues and try again.", messageColor, "Yellow")
return return
@ -470,11 +529,13 @@ function takeClueFromLocation(playerColor, hoveredObject)
clue.setRotation(rot) clue.setRotation(rot)
end end
if cardName then -- construct feedback message
broadcastToAll(getColoredName(playerColor) .. " took one clue from " .. cardName .. ".", "White") local playerName = getColoredName(playerColor)
else local cardInfo = ""
broadcastToAll(getColoredName(playerColor) .. " took one clue.", "White") if cardName and cardName ~= "" then
cardInfo = " from " .. cardName
end end
broadcastToAll(playerName .. " took one clue" .. cardInfo .. ".", "White")
victoryDisplayApi.update() victoryDisplayApi.update()
end end

View File

@ -49,6 +49,10 @@ function updateVal(newVal)
end end
function addOrSubtract(_, _, isRightClick) function addOrSubtract(_, _, isRightClick)
val = math.min(math.max(val + (isRightClick and -1 or 1), MIN_VALUE), MAX_VALUE) modifyValue(isRightClick and -1 or 1)
end
function modifyValue(mod)
val = math.min(math.max(val + tonumber(mod), MIN_VALUE), MAX_VALUE)
self.editButton({ index = 0, label = tostring(val) }) self.editButton({ index = 0, label = tostring(val) })
end end

View File

@ -114,7 +114,7 @@ function addUseToCard(card, useType)
end end
local match = false local match = false
for _, useInfo in ipairs(metadata.uses) do for _, useInfo in ipairs(metadata.uses or {}) do
if useInfo.token == useType then if useInfo.token == useType then
-- artificially create replenish data to re-use that existing functionality -- artificially create replenish data to re-use that existing functionality
useInfo.count = 999 useInfo.count = 999