Merge branch 'main' into gamekeyhandler
This commit is contained in:
commit
1c138c750d
@ -22,6 +22,13 @@
|
|||||||
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/2342503777940937086/92256BDF101E6272AD1E3F5F0043D311DF708F03/",
|
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/2342503777940937086/92256BDF101E6272AD1E3F5F0043D311DF708F03/",
|
||||||
"WidthScale": 0
|
"WidthScale": 0
|
||||||
},
|
},
|
||||||
|
"CustomUIAssets": [
|
||||||
|
{
|
||||||
|
"Name": "OtherCards",
|
||||||
|
"Type": 0,
|
||||||
|
"URL": "http://cloud-3.steamusercontent.com/ugc/2446096169989812196/B5C491331EB348C261F561DC7A19968ECF9FC74A/"
|
||||||
|
}
|
||||||
|
],
|
||||||
"Description": "",
|
"Description": "",
|
||||||
"DragSelectable": true,
|
"DragSelectable": true,
|
||||||
"GMNotes": "",
|
"GMNotes": "",
|
||||||
@ -53,5 +60,5 @@
|
|||||||
"scaleZ": 10
|
"scaleZ": 10
|
||||||
},
|
},
|
||||||
"Value": 0,
|
"Value": 0,
|
||||||
"XmlUI": "\u003cInclude src=\"playercards/PlayerCardPanel.xml\"/\u003e"
|
"XmlUI": ""
|
||||||
}
|
}
|
||||||
|
@ -150,11 +150,8 @@ end
|
|||||||
|
|
||||||
-- Event handlers for deck ID change
|
-- Event handlers for deck ID change
|
||||||
function redDeckChanged(_, _, inputValue) redDeckId = inputValue end
|
function redDeckChanged(_, _, inputValue) redDeckId = inputValue end
|
||||||
|
|
||||||
function orangeDeckChanged(_, _, inputValue) orangeDeckId = inputValue end
|
function orangeDeckChanged(_, _, inputValue) orangeDeckId = inputValue end
|
||||||
|
|
||||||
function whiteDeckChanged(_, _, inputValue) whiteDeckId = inputValue end
|
function whiteDeckChanged(_, _, inputValue) whiteDeckId = inputValue end
|
||||||
|
|
||||||
function greenDeckChanged(_, _, inputValue) greenDeckId = inputValue end
|
function greenDeckChanged(_, _, inputValue) greenDeckId = inputValue end
|
||||||
|
|
||||||
-- Event handlers for toggle buttons
|
-- Event handlers for toggle buttons
|
||||||
@ -174,14 +171,7 @@ function loadInvestigatorsChanged()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function loadDecks()
|
function loadDecks()
|
||||||
-- testLoadLotsOfDecks()
|
if not allCardsBagApi.isIndexReady() then return end
|
||||||
-- 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 (redDeckId ~= nil and redDeckId ~= "") then
|
if (redDeckId ~= nil and redDeckId ~= "") then
|
||||||
buildDeck("Red", redDeckId)
|
buildDeck("Red", redDeckId)
|
||||||
end
|
end
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
local cardIdIndex = { }
|
local guidReferenceApi = require("core/GUIDReferenceApi")
|
||||||
local classAndLevelIndex = { }
|
|
||||||
local basicWeaknessList = { }
|
local cardIdIndex = {}
|
||||||
local uniqueWeaknessList = { }
|
local classAndLevelIndex = {}
|
||||||
local cycleIndex = { }
|
local basicWeaknessList = {}
|
||||||
|
local uniqueWeaknessList = {}
|
||||||
|
local cycleIndex = {}
|
||||||
|
|
||||||
local indexingDone = false
|
local indexingDone = false
|
||||||
|
local otherCardsDetected = false
|
||||||
|
|
||||||
function onLoad()
|
function onLoad()
|
||||||
self.addContextMenuItem("Rebuild Index", startIndexBuild)
|
self.addContextMenuItem("Rebuild Index", startIndexBuild)
|
||||||
@ -12,13 +15,13 @@ function onLoad()
|
|||||||
Wait.frames(startIndexBuild, 30)
|
Wait.frames(startIndexBuild, 30)
|
||||||
end
|
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
|
-- 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
|
-- 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.
|
-- later, and we should rebuild the index to integrate the hotfix bag.
|
||||||
function rebuildIndexForHotfix()
|
function rebuildIndexForHotfix()
|
||||||
if (indexingDone) then
|
if indexingDone then
|
||||||
startIndexBuild()
|
startIndexBuild()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -26,23 +29,23 @@ end
|
|||||||
-- Resets all current bag indexes
|
-- Resets all current bag indexes
|
||||||
function clearIndexes()
|
function clearIndexes()
|
||||||
indexingDone = false
|
indexingDone = false
|
||||||
cardIdIndex = { }
|
cardIdIndex = {}
|
||||||
classAndLevelIndex = { }
|
classAndLevelIndex = {}
|
||||||
classAndLevelIndex["Guardian-upgrade"] = { }
|
classAndLevelIndex["Guardian-upgrade"] = {}
|
||||||
classAndLevelIndex["Seeker-upgrade"] = { }
|
classAndLevelIndex["Seeker-upgrade"] = {}
|
||||||
classAndLevelIndex["Mystic-upgrade"] = { }
|
classAndLevelIndex["Mystic-upgrade"] = {}
|
||||||
classAndLevelIndex["Survivor-upgrade"] = { }
|
classAndLevelIndex["Survivor-upgrade"] = {}
|
||||||
classAndLevelIndex["Rogue-upgrade"] = { }
|
classAndLevelIndex["Rogue-upgrade"] = {}
|
||||||
classAndLevelIndex["Neutral-upgrade"] = { }
|
classAndLevelIndex["Neutral-upgrade"] = {}
|
||||||
classAndLevelIndex["Guardian-level0"] = { }
|
classAndLevelIndex["Guardian-level0"] = {}
|
||||||
classAndLevelIndex["Seeker-level0"] = { }
|
classAndLevelIndex["Seeker-level0"] = {}
|
||||||
classAndLevelIndex["Mystic-level0"] = { }
|
classAndLevelIndex["Mystic-level0"] = {}
|
||||||
classAndLevelIndex["Survivor-level0"] = { }
|
classAndLevelIndex["Survivor-level0"] = {}
|
||||||
classAndLevelIndex["Rogue-level0"] = { }
|
classAndLevelIndex["Rogue-level0"] = {}
|
||||||
classAndLevelIndex["Neutral-level0"] = { }
|
classAndLevelIndex["Neutral-level0"] = {}
|
||||||
cycleIndex = { }
|
cycleIndex = {}
|
||||||
basicWeaknessList = { }
|
basicWeaknessList = {}
|
||||||
uniqueWeaknessList = { }
|
uniqueWeaknessList = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clears the bag indexes and starts the coroutine to rebuild the indexes
|
-- Clears the bag indexes and starts the coroutine to rebuild the indexes
|
||||||
@ -63,6 +66,7 @@ end
|
|||||||
function buildIndex()
|
function buildIndex()
|
||||||
local cardCount = 0
|
local cardCount = 0
|
||||||
indexingDone = false
|
indexingDone = false
|
||||||
|
otherCardsDetected = false
|
||||||
|
|
||||||
-- process the allcardsbag itself
|
-- process the allcardsbag itself
|
||||||
for _, cardData in ipairs(self.getData().ContainedObjects) do
|
for _, cardData in ipairs(self.getData().ContainedObjects) do
|
||||||
@ -84,8 +88,8 @@ function buildIndex()
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _, cardData in ipairs(hotfixData.ContainedObjects) do
|
for _, cardData in ipairs(hotfixData.ContainedObjects) do
|
||||||
-- process containers
|
|
||||||
if cardData.ContainedObjects then
|
if cardData.ContainedObjects then
|
||||||
|
-- process containers
|
||||||
for _, deepCardData in ipairs(cardData.ContainedObjects) do
|
for _, deepCardData in ipairs(cardData.ContainedObjects) do
|
||||||
addCardToIndex(deepCardData)
|
addCardToIndex(deepCardData)
|
||||||
cardCount = cardCount + 1
|
cardCount = cardCount + 1
|
||||||
@ -94,8 +98,8 @@ function buildIndex()
|
|||||||
coroutine.yield(0)
|
coroutine.yield(0)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- process single cards
|
|
||||||
else
|
else
|
||||||
|
-- process single cards
|
||||||
addCardToIndex(cardData)
|
addCardToIndex(cardData)
|
||||||
cardCount = cardCount + 1
|
cardCount = cardCount + 1
|
||||||
if cardCount > 19 then
|
if cardCount > 19 then
|
||||||
@ -108,6 +112,7 @@ function buildIndex()
|
|||||||
end
|
end
|
||||||
|
|
||||||
buildSupplementalIndexes()
|
buildSupplementalIndexes()
|
||||||
|
updatePlayerCardPanel()
|
||||||
indexingDone = true
|
indexingDone = true
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
@ -116,15 +121,19 @@ end
|
|||||||
---@param cardData table TTS object data for the card
|
---@param cardData table TTS object data for the card
|
||||||
function addCardToIndex(cardData)
|
function addCardToIndex(cardData)
|
||||||
-- using the more efficient 'json.parse()' to speed this process up
|
-- 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 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)
|
cardMetadata = JSON.decode(cardData.GMNotes)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if metadata was not valid JSON or empty, don't add the card
|
-- 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
|
-- use the ZoopGuid as fallback if no id present
|
||||||
cardMetadata.id = cardMetadata.id or cardMetadata.TtsZoopGuid
|
cardMetadata.id = cardMetadata.id or cardMetadata.TtsZoopGuid
|
||||||
@ -138,37 +147,35 @@ function addCardToIndex(cardData)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Creates the supplemental indexes for classes, weaknesses etc.
|
||||||
function buildSupplementalIndexes()
|
function buildSupplementalIndexes()
|
||||||
for cardId, card in pairs(cardIdIndex) do
|
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 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
|
-- Add card to the basic weakness list, if appropriate. Some weaknesses have multiple copies, and are added multiple times
|
||||||
if cardMetadata.weakness then
|
if card.metadata.weakness then
|
||||||
table.insert(uniqueWeaknessList, cardMetadata.id)
|
table.insert(uniqueWeaknessList, card.metadata.id)
|
||||||
if cardMetadata.basicWeaknessCount ~= nil then
|
if card.metadata.basicWeaknessCount ~= nil then
|
||||||
for i = 1, cardMetadata.basicWeaknessCount do
|
for i = 1, card.metadata.basicWeaknessCount do
|
||||||
table.insert(basicWeaknessList, cardMetadata.id)
|
table.insert(basicWeaknessList, card.metadata.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Excludes signature cards (which have no class or level)
|
-- Excludes signature cards (which have no class or level)
|
||||||
if cardMetadata.class ~= nil and cardMetadata.level ~= nil then
|
if card.metadata.class ~= nil and card.metadata.level ~= nil then
|
||||||
local upgradeKey
|
local upgradeKey = "-level0"
|
||||||
if cardMetadata.level > 0 then
|
if card.metadata.level > 0 then
|
||||||
upgradeKey = "-upgrade"
|
upgradeKey = "-upgrade"
|
||||||
else
|
|
||||||
upgradeKey = "-level0"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse classes (separated by "|") and add the card to the appropriate class and level indices
|
-- parse classes (separated by "|") and add the card to the appropriate class and level indices
|
||||||
for str in cardMetadata.class:gmatch("([^|]+)") do
|
for str in card.metadata.class:gmatch("([^|]+)") do
|
||||||
table.insert(classAndLevelIndex[str .. upgradeKey], cardMetadata.id)
|
table.insert(classAndLevelIndex[str .. upgradeKey], card.metadata.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add to cycle index
|
-- add to cycle index
|
||||||
local cycleName = cardMetadata.cycle
|
local cycleName = card.metadata.cycle
|
||||||
if cycleName ~= nil then
|
if cycleName ~= nil then
|
||||||
cycleName = string.lower(cycleName)
|
cycleName = string.lower(cycleName)
|
||||||
|
|
||||||
@ -177,12 +184,17 @@ function buildSupplementalIndexes()
|
|||||||
|
|
||||||
-- override cycle name for night of the zealot
|
-- override cycle name for night of the zealot
|
||||||
cycleName = cycleName:gsub("the night of the zealot", "core")
|
cycleName = cycleName:gsub("the night of the zealot", "core")
|
||||||
|
else
|
||||||
if cycleIndex[cycleName] == nil then
|
-- track cards without defined cycle (should only be fan-made cards)
|
||||||
cycleIndex[cycleName] = { }
|
cycleName = "other"
|
||||||
end
|
otherCardsDetected = true
|
||||||
table.insert(cycleIndex[cycleName], cardMetadata.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- maybe initialize table
|
||||||
|
if cycleIndex[cycleName] == nil then
|
||||||
|
cycleIndex[cycleName] = {}
|
||||||
|
end
|
||||||
|
table.insert(cycleIndex[cycleName], card.metadata.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -216,109 +228,173 @@ function cardComparator(id1, id2)
|
|||||||
end
|
end
|
||||||
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()
|
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
|
return indexingDone
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns a specific card from the bag, based on ArkhamDB ID
|
-- Returns a specific card from the bag, based on ArkhamDB ID
|
||||||
-- Params table:
|
---@param params table ID of the card to retrieve
|
||||||
-- id: String ID of the card to retrieve
|
---@return table: If the indexes are still being constructed, returns an empty table.
|
||||||
-- Return: If the indexes are still being constructed, an empty table is
|
-- Otherwise, a single table with the following fields
|
||||||
-- returned. Otherwise, a single table with the following fields
|
-- data: TTS object data, suitable for spawning the card
|
||||||
-- cardData: TTS object data, suitable for spawning the card
|
-- metadata: Table of parsed metadata
|
||||||
-- cardMetadata: Table of parsed metadata
|
|
||||||
function getCardById(params)
|
function getCardById(params)
|
||||||
if (not indexingDone) then
|
if not isIndexReady() then return {} end
|
||||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
|
||||||
return { }
|
|
||||||
end
|
|
||||||
return cardIdIndex[params.id]
|
return cardIdIndex[params.id]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns a list of cards from the bag matching a class and level (0 or upgraded)
|
-- Returns a list of cards from the bag matching a class and level (0 or upgraded)
|
||||||
-- Params table:
|
---@param params table
|
||||||
-- class: String class to retrieve ("Guardian", "Seeker", etc)
|
-- class: String class to retrieve ("Guardian", "Seeker", etc)
|
||||||
-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0
|
-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0
|
||||||
-- Return: If the indexes are still being constructed, returns an empty table.
|
---@return table: If the indexes are still being constructed, returns an empty table.
|
||||||
-- Otherwise, a list of tables, each with the following fields
|
-- Otherwise, a list of tables, each with the following fields
|
||||||
-- cardData: TTS object data, suitable for spawning the card
|
-- data: TTS object data, suitable for spawning the card
|
||||||
-- cardMetadata: Table of parsed metadata
|
-- metadata: Table of parsed metadata
|
||||||
function getCardsByClassAndLevel(params)
|
function getCardsByClassAndLevel(params)
|
||||||
if (not indexingDone) then
|
if not isIndexReady() then return {} end
|
||||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
|
||||||
return { }
|
local upgradeKey = "-level0"
|
||||||
end
|
if params.upgraded then
|
||||||
local upgradeKey
|
|
||||||
if (params.upgraded) then
|
|
||||||
upgradeKey = "-upgrade"
|
upgradeKey = "-upgrade"
|
||||||
|
end
|
||||||
|
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
|
||||||
|
-- data: TTS object data, suitable for spawning the card
|
||||||
|
-- metadata: Table of parsed metadata
|
||||||
|
function getCardsByCycle(params)
|
||||||
|
if not isIndexReady() then return {} end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
-- 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
|
else
|
||||||
upgradeKey = "-level0"
|
return card1.data.Description < card2.data.Description
|
||||||
end
|
end
|
||||||
return classAndLevelIndex[params.class..upgradeKey];
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function getCardsByCycle(cycleName)
|
-- helper function to calculate the class value for sorting from the "|" separated string
|
||||||
if (not indexingDone) then
|
function getClassValueFromString(s)
|
||||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
local classValueList = {
|
||||||
return { }
|
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
|
end
|
||||||
return cycleIndex[string.lower(cycleName)]
|
return classValue
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Searches the bag for cards which match the given name and returns a list. Note that this is
|
-- 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.
|
-- an O(n) search without index support. It may be slow.
|
||||||
-- Parameter array must contain these fields to define the search:
|
-- Parameter array must contain these fields to define the search:
|
||||||
-- name String or string fragment to search for names
|
-- name: String or string fragment to search for names
|
||||||
-- exact Whether the name match should be exact
|
-- exact: Whether the name match should be exact
|
||||||
function getCardsByName(params)
|
function getCardsByName(params)
|
||||||
local name = params.name
|
local name = params.name
|
||||||
local exact = params.exact
|
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
|
-- 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
|
for _, cardData in pairs(cardIdIndex) do
|
||||||
if (not addedCards[cardData.metadata.id]) then
|
if (not addedCards[cardData.metadata.id]) then
|
||||||
if (exact and (string.lower(cardData.data.Nickname) == string.lower(name)))
|
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
|
or (not exact and string.find(string.lower(cardData.data.Nickname), string.lower(name), 1, true)) then
|
||||||
table.insert(results, cardData)
|
table.insert(results, cardData)
|
||||||
addedCards[cardData.metadata.id] = true
|
addedCards[cardData.metadata.id] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return results
|
return results
|
||||||
end
|
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
|
||||||
-- it will be removed from the list and cannot be selected again until a reload
|
-- removed from the list and cannot be selected again until a reload occurs or the indexes
|
||||||
-- occurs or the indexes are rebuilt, which will refresh the list to include all
|
-- are rebuilt, which will refresh the list to include all weaknesses.
|
||||||
-- weaknesses.
|
---@return string: ID of the selected weakness
|
||||||
-- Return: String ID of the selected weakness.
|
|
||||||
function getRandomWeaknessId()
|
function getRandomWeaknessId()
|
||||||
local availableWeaknesses = buildAvailableWeaknesses()
|
local availableWeaknesses = buildAvailableWeaknesses()
|
||||||
if (#availableWeaknesses > 0) then
|
if #availableWeaknesses > 0 then
|
||||||
return availableWeaknesses[math.random(#availableWeaknesses)]
|
return availableWeaknesses[math.random(#availableWeaknesses)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Constructs a list of available basic weaknesses by starting with the full pool of basic
|
-- 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
|
-- 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()
|
function buildAvailableWeaknesses()
|
||||||
local weaknessesInPlay = { }
|
local weaknessesInPlay = {}
|
||||||
local allObjects = getAllObjects()
|
local allObjects = getAllObjects()
|
||||||
for _, object in ipairs(allObjects) do
|
for _, object in ipairs(allObjects) do
|
||||||
if (object.name == "Deck") then
|
if object.type == "Deck" then
|
||||||
for _, cardData in ipairs(object.getData().ContainedObjects) do
|
for _, cardData in ipairs(object.getData().ContainedObjects) do
|
||||||
local cardMetadata = JSON.decode(cardData.GMNotes)
|
incrementWeaknessCount(weaknessesInPlay, JSON.decode(cardData.GMNotes))
|
||||||
incrementWeaknessCount(weaknessesInPlay, cardMetadata)
|
|
||||||
end
|
end
|
||||||
elseif (object.name == "Card") then
|
elseif object.type == "Card" then
|
||||||
local cardMetadata = JSON.decode(object.getGMNotes())
|
incrementWeaknessCount(weaknessesInPlay, JSON.decode(object.getGMNotes()))
|
||||||
incrementWeaknessCount(weaknessesInPlay, cardMetadata)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local availableWeaknesses = { }
|
local availableWeaknesses = {}
|
||||||
for _, weaknessId in ipairs(basicWeaknessList) do
|
for _, weaknessId in ipairs(basicWeaknessList) do
|
||||||
if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then
|
if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then
|
||||||
weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1
|
weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1
|
||||||
@ -339,8 +415,8 @@ end
|
|||||||
|
|
||||||
-- Helper function that adds one to the table entry for the number of weaknesses in play
|
-- Helper function that adds one to the table entry for the number of weaknesses in play
|
||||||
function incrementWeaknessCount(table, cardMetadata)
|
function incrementWeaknessCount(table, cardMetadata)
|
||||||
if (isBasicWeakness(cardMetadata)) then
|
if isBasicWeakness(cardMetadata) then
|
||||||
if (table[cardMetadata.id] == nil) then
|
if table[cardMetadata.id] == nil then
|
||||||
table[cardMetadata.id] = 1
|
table[cardMetadata.id] = 1
|
||||||
else
|
else
|
||||||
table[cardMetadata.id] = table[cardMetadata.id] + 1
|
table[cardMetadata.id] = table[cardMetadata.id] + 1
|
||||||
@ -350,7 +426,7 @@ end
|
|||||||
|
|
||||||
function isBasicWeakness(cardMetadata)
|
function isBasicWeakness(cardMetadata)
|
||||||
return cardMetadata ~= nil
|
return cardMetadata ~= nil
|
||||||
and cardMetadata.weakness
|
and cardMetadata.weakness
|
||||||
and cardMetadata.basicWeaknessCount ~= nil
|
and cardMetadata.basicWeaknessCount ~= nil
|
||||||
and cardMetadata.basicWeaknessCount > 0
|
and cardMetadata.basicWeaknessCount > 0
|
||||||
end
|
end
|
||||||
|
@ -6,22 +6,29 @@ do
|
|||||||
return guidReferenceApi.getObjectByOwnerAndType("Mythos", "AllCardsBag")
|
return guidReferenceApi.getObjectByOwnerAndType("Mythos", "AllCardsBag")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns a specific card from the bag, based on ArkhamDB ID
|
-- internal function to create a copy of the table to avoid operating on variables owned by different objects
|
||||||
---@param id table String ID of the card to retrieve
|
local function returnCopyOfList(data)
|
||||||
---@return table table
|
local copiedList = {}
|
||||||
-- If the indexes are still being constructed, an empty table is
|
for _, id in ipairs(data) do
|
||||||
-- returned. Otherwise, a single table with the following fields
|
table.insert(copiedList, id)
|
||||||
-- cardData: TTS object data, suitable for spawning the card
|
end
|
||||||
-- cardMetadata: Table of parsed metadata
|
return copiedList
|
||||||
AllCardsBagApi.getCardById = function(id)
|
|
||||||
return getAllCardsBag().call("getCardById", {id = id})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Gets a random basic weakness from the bag. Once a given ID has been returned
|
-- Returns a specific card from the bag, based on ArkhamDB ID
|
||||||
-- it will be removed from the list and cannot be selected again until a reload
|
---@param id string ID of the card to retrieve
|
||||||
-- occurs or the indexes are rebuilt, which will refresh the list to include all
|
---@return table: If the indexes are still being constructed, returns an empty table.
|
||||||
-- weaknesses.
|
-- Otherwise, a single table with the following fields
|
||||||
---@return string: ID of the selected weakness.
|
-- data: TTS object data, suitable for spawning the card
|
||||||
|
-- metadata: Table of parsed metadata
|
||||||
|
AllCardsBagApi.getCardById = function(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.
|
||||||
|
---@return string: ID of the selected weakness
|
||||||
AllCardsBagApi.getRandomWeaknessId = function()
|
AllCardsBagApi.getRandomWeaknessId = function()
|
||||||
return getAllCardsBag().call("getRandomWeaknessId")
|
return getAllCardsBag().call("getRandomWeaknessId")
|
||||||
end
|
end
|
||||||
@ -30,21 +37,21 @@ do
|
|||||||
return getAllCardsBag().call("isIndexReady")
|
return getAllCardsBag().call("isIndexReady")
|
||||||
end
|
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
|
-- 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
|
-- 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.
|
-- later, and we should rebuild the index to integrate the hotfix bag.
|
||||||
AllCardsBagApi.rebuildIndexForHotfix = function()
|
AllCardsBagApi.rebuildIndexForHotfix = function()
|
||||||
return getAllCardsBag().call("rebuildIndexForHotfix")
|
getAllCardsBag().call("rebuildIndexForHotfix")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Searches the bag for cards which match the given name and returns a list. Note that this is
|
-- Searches the bag for cards which match the given name and returns a list.
|
||||||
-- an O(n) search without index support. It may be slow.
|
-- 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 name string or string fragment to search for names
|
||||||
---@param exact boolean Whether the name match should be exact
|
---@param exact boolean Whether the name match should be exact
|
||||||
AllCardsBagApi.getCardsByName = function(name, exact)
|
AllCardsBagApi.getCardsByName = function(name, exact)
|
||||||
return getAllCardsBag().call("getCardsByName", {name = name, exact = exact})
|
return returnCopyOfList(getAllCardsBag().call("getCardsByName", { name = name, exact = exact }))
|
||||||
end
|
end
|
||||||
|
|
||||||
AllCardsBagApi.isBagPresent = function()
|
AllCardsBagApi.isBagPresent = function()
|
||||||
@ -53,22 +60,29 @@ do
|
|||||||
|
|
||||||
-- Returns a list of cards from the bag matching a class and level (0 or upgraded)
|
-- 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 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.
|
---@return table: If the indexes are still being constructed, returns an empty table.
|
||||||
-- Otherwise, a list of tables, each with the following fields
|
-- Otherwise, a list of tables, each with the following fields
|
||||||
-- cardData: TTS object data, suitable for spawning the card
|
-- data: TTS object data, suitable for spawning the card
|
||||||
-- cardMetadata: Table of parsed metadata
|
-- metadata: Table of parsed metadata
|
||||||
AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded)
|
AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded)
|
||||||
return getAllCardsBag().call("getCardsByClassAndLevel", {class = class, upgraded = upgraded})
|
return returnCopyOfList(getAllCardsBag().call("getCardsByClassAndLevel", { class = class, upgraded = upgraded }))
|
||||||
end
|
end
|
||||||
|
|
||||||
AllCardsBagApi.getCardsByCycle = function(cycle)
|
-- Returns a list of cards from the bag matching a cycle
|
||||||
return getAllCardsBag().call("getCardsByCycle", 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
|
||||||
|
-- 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
|
end
|
||||||
|
|
||||||
AllCardsBagApi.getUniqueWeaknesses = function()
|
AllCardsBagApi.getUniqueWeaknesses = function()
|
||||||
return getAllCardsBag().call("getUniqueWeaknesses")
|
return returnCopyOfList(getAllCardsBag().call("getUniqueWeaknesses"))
|
||||||
end
|
end
|
||||||
|
|
||||||
return AllCardsBagApi
|
return AllCardsBagApi
|
||||||
end
|
end
|
||||||
|
@ -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 STARTER_DECK_MODE_SELECTED_COLOR = { 0.2, 0.2, 0.2, 0.8 }
|
||||||
local TRANSPARENT = { 0, 0, 0, 0 }
|
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_UP_ROTATION = { x = 0, y = 270, z = 0 }
|
||||||
local FACE_DOWN_ROTATION = { x = 0, y = 270, z = 180}
|
local FACE_DOWN_ROTATION = { x = 0, y = 270, z = 180 }
|
||||||
|
|
||||||
-- ---------- IMPORTANT ----------
|
-- ---------- IMPORTANT ----------
|
||||||
-- Coordinates defined below are in global dimensions relative to the panel - DO NOT USE THESE
|
-- 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
|
-- Layout width for a single card, in global coordinate space
|
||||||
local CARD_WIDTH = 2.3
|
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.
|
-- 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.
|
-- coordinates on these provide global disances while the Z is local.
|
||||||
local START_POSITIONS = {
|
local START_POSITIONS = {
|
||||||
classCards = Vector(CARD_WIDTH * 9.5, 2, 1.4),
|
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),
|
other = Vector(CARD_WIDTH * 9.5, 2, 1.4),
|
||||||
randomWeakness = Vector(0, 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
|
-- 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)
|
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_POSITION_SHIFT_COL = Vector(-6, 0, 0)
|
||||||
local INVESTIGATOR_MAX_COLS = 6
|
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
|
-- decks use SIGNATURE_OFFSET
|
||||||
local INVESTIGATOR_CARD_OFFSET = Vector(0, 0, 2.55)
|
local INVESTIGATOR_CARD_OFFSET = Vector(0, 0, 2.55)
|
||||||
local INVESTIGATOR_SIGNATURE_OFFSET = Vector(0, 0, 5.75)
|
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 startPositions
|
||||||
local cardRowOffset
|
local cardRowOffset
|
||||||
local cardGroupOffset
|
local cardGroupOffset
|
||||||
@ -78,7 +76,14 @@ local investigatorPositionShiftCol
|
|||||||
local investigatorCardOffset
|
local investigatorCardOffset
|
||||||
local investigatorSignatureOffset
|
local investigatorSignatureOffset
|
||||||
|
|
||||||
local CLASS_LIST = { "Guardian", "Seeker", "Rogue", "Mystic", "Survivor", "Neutral" }
|
local CLASS_LIST = {
|
||||||
|
"Guardian",
|
||||||
|
"Seeker",
|
||||||
|
"Rogue",
|
||||||
|
"Mystic",
|
||||||
|
"Survivor",
|
||||||
|
"Neutral"
|
||||||
|
}
|
||||||
local CYCLE_LIST = {
|
local CYCLE_LIST = {
|
||||||
"Core",
|
"Core",
|
||||||
"The Dunwich Legacy",
|
"The Dunwich Legacy",
|
||||||
@ -94,9 +99,8 @@ local CYCLE_LIST = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local excludedNonBasicWeaknesses
|
local excludedNonBasicWeaknesses
|
||||||
|
local spawnStarterDecks = false
|
||||||
local starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY
|
local helpVisibleToPlayers = {}
|
||||||
local helpVisibleToPlayers = { }
|
|
||||||
|
|
||||||
function onSave()
|
function onSave()
|
||||||
return JSON.encode({ spawnBagState = spawnBag.getStateForSave() })
|
return JSON.encode({ spawnBagState = spawnBag.getStateForSave() })
|
||||||
@ -104,7 +108,7 @@ end
|
|||||||
|
|
||||||
function onLoad(savedData)
|
function onLoad(savedData)
|
||||||
if savedData and savedData ~= "" then
|
if savedData and savedData ~= "" then
|
||||||
local saveState = JSON.decode(savedData) or { }
|
local saveState = JSON.decode(savedData) or {}
|
||||||
if saveState.spawnBagState ~= nil then
|
if saveState.spawnBagState ~= nil then
|
||||||
spawnBag.loadFromSave(saveState.spawnBagState)
|
spawnBag.loadFromSave(saveState.spawnBagState)
|
||||||
end
|
end
|
||||||
@ -117,7 +121,7 @@ end
|
|||||||
-- Build a list of non-basic weaknesses which should be excluded from the last weakness set,
|
-- Build a list of non-basic weaknesses which should be excluded from the last weakness set,
|
||||||
-- including all signature cards and evolved weaknesses.
|
-- including all signature cards and evolved weaknesses.
|
||||||
function buildExcludedWeaknessList()
|
function buildExcludedWeaknessList()
|
||||||
excludedNonBasicWeaknesses = { }
|
excludedNonBasicWeaknesses = {}
|
||||||
for _, investigator in pairs(INVESTIGATORS) do
|
for _, investigator in pairs(INVESTIGATORS) do
|
||||||
for _, signatureId in ipairs(investigator.signatures) do
|
for _, signatureId in ipairs(investigator.signatures) do
|
||||||
excludedNonBasicWeaknesses[signatureId] = true
|
excludedNonBasicWeaknesses[signatureId] = true
|
||||||
@ -274,9 +278,8 @@ function createCycleButtons()
|
|||||||
if rowCount == 3 then
|
if rowCount == 3 then
|
||||||
-- Account for two centered buttons on the final row
|
-- Account for two centered buttons on the final row
|
||||||
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET / 2
|
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET / 2
|
||||||
--[[ Account for centered button on the final row
|
-- Account for centered button on the final row
|
||||||
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
|
-- buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
|
||||||
]]
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
|
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
|
||||||
@ -297,8 +300,6 @@ function createClearButton()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function createInvestigatorModeButtons()
|
function createInvestigatorModeButtons()
|
||||||
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
|
|
||||||
|
|
||||||
self.createButton({
|
self.createButton({
|
||||||
function_owner = self,
|
function_owner = self,
|
||||||
click_function = "setCardsOnlyMode",
|
click_function = "setCardsOnlyMode",
|
||||||
@ -306,18 +307,18 @@ function createInvestigatorModeButtons()
|
|||||||
height = 170,
|
height = 170,
|
||||||
width = 760,
|
width = 760,
|
||||||
scale = Vector(0.25, 1, 0.25),
|
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({
|
self.createButton({
|
||||||
function_owner = self,
|
function_owner = self,
|
||||||
click_function = "setStarterDeckMode",
|
click_function = "setspawnStarterDecks",
|
||||||
position = Vector(0.66, 0.1, -0.322),
|
position = Vector(0.66, 0.1, -0.322),
|
||||||
height = 170,
|
height = 170,
|
||||||
width = 760,
|
width = 760,
|
||||||
scale = Vector(0.25, 1, 0.25),
|
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({
|
self.createButton({
|
||||||
function_owner = self,
|
function_owner = self,
|
||||||
label = "✓",
|
label = "✓",
|
||||||
@ -325,12 +326,77 @@ function createInvestigatorModeButtons()
|
|||||||
position = Vector(checkX, 0.11, -0.317),
|
position = Vector(checkX, 0.11, -0.317),
|
||||||
height = 0,
|
height = 0,
|
||||||
width = 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 },
|
font_color = { 0, 0, 0 },
|
||||||
color = { 1, 1, 1 }
|
color = { 1, 1, 1 }
|
||||||
})
|
})
|
||||||
end
|
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, _)
|
function toggleHelp(_, playerColor, _)
|
||||||
if helpVisibleToPlayers[playerColor] then
|
if helpVisibleToPlayers[playerColor] then
|
||||||
helpVisibleToPlayers[playerColor] = nil
|
helpVisibleToPlayers[playerColor] = nil
|
||||||
@ -354,13 +420,13 @@ function updateHelpVisibility()
|
|||||||
self.UI.setAttribute("helpPanel", "active", string.len(visibility) > 0)
|
self.UI.setAttribute("helpPanel", "active", string.len(visibility) > 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function setStarterDeckMode()
|
function setspawnStarterDecks()
|
||||||
starterDeckMode = STARTER_DECK_MODE_STARTERS
|
spawnStarterDecks = true
|
||||||
updateStarterModeButtons()
|
updateStarterModeButtons()
|
||||||
end
|
end
|
||||||
|
|
||||||
function setCardsOnlyMode()
|
function setCardsOnlyMode()
|
||||||
starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY
|
spawnStarterDecks = false
|
||||||
updateStarterModeButtons()
|
updateStarterModeButtons()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -384,7 +450,7 @@ end
|
|||||||
function scalePositions()
|
function scalePositions()
|
||||||
-- Assume scaling is consistent in X and Z dimensions
|
-- Assume scaling is consistent in X and Z dimensions
|
||||||
local scale = 1 / self.getScale().x
|
local scale = 1 / self.getScale().x
|
||||||
startPositions = { }
|
startPositions = {}
|
||||||
for key, pos in pairs(START_POSITIONS) do
|
for key, pos in pairs(START_POSITIONS) do
|
||||||
-- Because a scaled object means a different global size, using global distance for Z results in
|
-- 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
|
-- the cards being closer or farther depending on the scale. Leave the Z values and only scale X and Y
|
||||||
@ -405,14 +471,12 @@ function deleteAll()
|
|||||||
spawnBag.recall(true)
|
spawnBag.recall(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spawn an investigator group, based on the current UI setting for either investigators or starter
|
-- Spawn an investigator group, based on the current UI setting for either investigators or starter decks
|
||||||
-- decks.
|
|
||||||
---@param groupName string Name of the group to spawn, matching a key in InvestigatorPanelData
|
---@param groupName string Name of the group to spawn, matching a key in InvestigatorPanelData
|
||||||
function spawnInvestigatorGroup(groupName)
|
function spawnInvestigatorGroup(groupName)
|
||||||
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
|
|
||||||
prepareToPlaceCards()
|
prepareToPlaceCards()
|
||||||
Wait.frames(function()
|
Wait.frames(function()
|
||||||
if starterMode then
|
if spawnStarterDecks then
|
||||||
spawnStarters(groupName)
|
spawnStarters(groupName)
|
||||||
else
|
else
|
||||||
spawnInvestigators(groupName)
|
spawnInvestigators(groupName)
|
||||||
@ -420,12 +484,12 @@ function spawnInvestigatorGroup(groupName)
|
|||||||
end, 2)
|
end, 2)
|
||||||
end
|
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.
|
-- investigator cards and minicards as well as the signature cards.
|
||||||
---@param groupName string Name of the group to spawn, matching a key in InvestigatorPanelData
|
---@param groupName string Name of the group to spawn, matching a key in InvestigatorPanelData
|
||||||
function spawnInvestigators(groupName)
|
function spawnInvestigators(groupName)
|
||||||
if INVESTIGATOR_GROUPS[groupName] == nil then
|
if INVESTIGATOR_GROUPS[groupName] == nil then
|
||||||
printToAll("No " .. groupName .. " data yet")
|
printToAll("No investigator data for " .. groupName .. " yet")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -434,7 +498,7 @@ function spawnInvestigators(groupName)
|
|||||||
local investigatorCount = #INVESTIGATOR_GROUPS[groupName]
|
local investigatorCount = #INVESTIGATOR_GROUPS[groupName]
|
||||||
local position = getInvestigatorRowStartPos(investigatorCount, row)
|
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
|
for _, spawnSpec in ipairs(buildInvestigatorSpawnSpec(investigatorName, INVESTIGATORS[investigatorName], position)) do
|
||||||
spawnBag.spawn(spawnSpec)
|
spawnBag.spawn(spawnSpec)
|
||||||
end
|
end
|
||||||
@ -451,14 +515,14 @@ end
|
|||||||
function getInvestigatorRowStartPos(investigatorCount, row)
|
function getInvestigatorRowStartPos(investigatorCount, row)
|
||||||
local rowStart = Vector(startPositions.investigator)
|
local rowStart = Vector(startPositions.investigator)
|
||||||
rowStart:add(Vector(
|
rowStart:add(Vector(
|
||||||
investigatorPositionShiftRow.x * (row - 1),
|
investigatorPositionShiftRow.x * (row - 1),
|
||||||
investigatorPositionShiftRow.y * (row - 1),
|
investigatorPositionShiftRow.y * (row - 1),
|
||||||
investigatorPositionShiftRow.z * (row - 1)))
|
investigatorPositionShiftRow.z * (row - 1)))
|
||||||
local investigatorsInRow = math.min(investigatorCount - INVESTIGATOR_MAX_COLS * (row - 1), INVESTIGATOR_MAX_COLS)
|
local investigatorsInRow = math.min(investigatorCount - INVESTIGATOR_MAX_COLS * (row - 1), INVESTIGATOR_MAX_COLS)
|
||||||
rowStart:add(Vector(
|
rowStart:add(Vector(
|
||||||
investigatorPositionShiftCol.x * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2,
|
investigatorPositionShiftCol.x * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2,
|
||||||
investigatorPositionShiftCol.y * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2,
|
investigatorPositionShiftCol.y * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2,
|
||||||
investigatorPositionShiftCol.z * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2))
|
investigatorPositionShiftCol.z * (INVESTIGATOR_MAX_COLS - investigatorsInRow) / 2))
|
||||||
return rowStart
|
return rowStart
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -470,23 +534,23 @@ function buildInvestigatorSpawnSpec(investigatorName, investigatorData, position
|
|||||||
local sigPos = Vector(position):add(investigatorSignatureOffset)
|
local sigPos = Vector(position):add(investigatorSignatureOffset)
|
||||||
local spawns = buildCommonSpawnSpec(investigatorName, investigatorData, position)
|
local spawns = buildCommonSpawnSpec(investigatorName, investigatorData, position)
|
||||||
table.insert(spawns, {
|
table.insert(spawns, {
|
||||||
name = investigatorName .. "signatures",
|
name = investigatorName .. "signatures",
|
||||||
cards = investigatorData.signatures,
|
cards = investigatorData.signatures,
|
||||||
globalPos = self.positionToWorld(sigPos),
|
globalPos = self.positionToWorld(sigPos),
|
||||||
rotation = FACE_UP_ROTATION
|
rotation = FACE_UP_ROTATION
|
||||||
})
|
})
|
||||||
|
|
||||||
return spawns
|
return spawns
|
||||||
end
|
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
|
-- shared, and will only differ in whether they spawn the full stack of possible investigator and
|
||||||
-- minicards, or only the first of each.
|
-- minicards, or only the first of each.
|
||||||
---@param investigatorName string Name of the investigator, matching a key in InvestigatorPanelData
|
---@param investigatorName string Name of the investigator, matching a key in InvestigatorPanelData
|
||||||
---@param investigatorData table Spawn definition for the investigator, retrieved from INVESTIGATORS
|
---@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 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
|
---@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)
|
function buildCommonSpawnSpec(investigatorName, investigatorData, position, oneCardOnly)
|
||||||
local cardPos = Vector(position):add(investigatorCardOffset)
|
local cardPos = Vector(position):add(investigatorCardOffset)
|
||||||
return {
|
return {
|
||||||
@ -533,23 +597,24 @@ function spawnStarterDeck(investigatorName, investigatorData, position)
|
|||||||
end
|
end
|
||||||
local deckPos = Vector(position):add(investigatorSignatureOffset)
|
local deckPos = Vector(position):add(investigatorSignatureOffset)
|
||||||
arkhamDb.getDecklist("None", investigatorData.starterDeck, true, false, false, function(slots)
|
arkhamDb.getDecklist("None", investigatorData.starterDeck, true, false, false, function(slots)
|
||||||
local cardIdList = { }
|
local cardIdList = {}
|
||||||
for id, count in pairs(slots) do
|
for id, count in pairs(slots) do
|
||||||
for i = 1, count do
|
for i = 1, count do
|
||||||
table.insert(cardIdList, id)
|
table.insert(cardIdList, id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
spawnBag.spawn({
|
spawnBag.spawn({
|
||||||
name = investigatorName.."starter",
|
name = investigatorName .. "starter",
|
||||||
cards = cardIdList,
|
cards = cardIdList,
|
||||||
globalPos = self.positionToWorld(deckPos),
|
globalPos = self.positionToWorld(deckPos),
|
||||||
rotation = FACE_DOWN_ROTATION
|
rotation = FACE_DOWN_ROTATION
|
||||||
})
|
})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clears the currently placed cards, then places cards for the given class and level spread
|
-- 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 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)
|
function spawnClassCards(cardClass, isUpgraded)
|
||||||
prepareToPlaceCards()
|
prepareToPlaceCards()
|
||||||
Wait.frames(function() placeClassCards(cardClass, isUpgraded) end, 2)
|
Wait.frames(function() placeClassCards(cardClass, isUpgraded) end, 2)
|
||||||
@ -557,18 +622,15 @@ end
|
|||||||
|
|
||||||
-- Spawn the class cards.
|
-- Spawn the class cards.
|
||||||
---@param cardClass string Class to place ("Guardian", "Seeker", etc)
|
---@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)
|
function placeClassCards(cardClass, isUpgraded)
|
||||||
local indexReady = allCardsBagApi.isIndexReady()
|
if not allCardsBagApi.isIndexReady() then return end
|
||||||
if (not indexReady) then
|
|
||||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local cardIdList = allCardsBagApi.getCardsByClassAndLevel(cardClass, isUpgraded)
|
local cardIdList = allCardsBagApi.getCardsByClassAndLevel(cardClass, isUpgraded)
|
||||||
|
|
||||||
local skillList = { }
|
local skillList = {}
|
||||||
local eventList = { }
|
local eventList = {}
|
||||||
local assetList = { }
|
local assetList = {}
|
||||||
for _, cardId in ipairs(cardIdList) do
|
for _, cardId in ipairs(cardIdList) do
|
||||||
local cardMetadata = allCardsBagApi.getCardById(cardId).metadata
|
local cardMetadata = allCardsBagApi.getCardById(cardId).metadata
|
||||||
if (cardMetadata.type == "Skill") then
|
if (cardMetadata.type == "Skill") then
|
||||||
@ -617,21 +679,20 @@ end
|
|||||||
-- Spawns the investigator sets and all cards for the given cycle
|
-- 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
|
---@param cycle string Name of a cycle, should match the standard used in card metadata
|
||||||
function spawnCycle(cycle)
|
function spawnCycle(cycle)
|
||||||
|
if not allCardsBagApi.isIndexReady() then return end
|
||||||
|
|
||||||
prepareToPlaceCards()
|
prepareToPlaceCards()
|
||||||
spawnInvestigators(cycle)
|
spawnInvestigators(cycle)
|
||||||
local indexReady = allCardsBagApi.isIndexReady()
|
|
||||||
if (not indexReady) then
|
-- sort custom cards
|
||||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
local sortByMetadata = false
|
||||||
return
|
if cycle == "Other" then
|
||||||
end
|
sortByMetadata = true
|
||||||
local cycleCardList = allCardsBagApi.getCardsByCycle(cycle)
|
|
||||||
local copiedList = { }
|
|
||||||
for i, id in ipairs(cycleCardList) do
|
|
||||||
copiedList[i] = id
|
|
||||||
end
|
end
|
||||||
|
|
||||||
spawnBag.spawn({
|
spawnBag.spawn({
|
||||||
name = "cycle"..cycle,
|
name = "cycle" .. cycle,
|
||||||
cards = copiedList,
|
cards = allCardsBagApi.getCardsByCycle(cycle, sortByMetadata),
|
||||||
globalPos = self.positionToWorld(startPositions.cycle),
|
globalPos = self.positionToWorld(startPositions.cycle),
|
||||||
rotation = FACE_UP_ROTATION,
|
rotation = FACE_UP_ROTATION,
|
||||||
spread = true,
|
spread = true,
|
||||||
@ -671,16 +732,13 @@ end
|
|||||||
|
|
||||||
-- Clears the current cards, and places all basic weaknesses on the table.
|
-- Clears the current cards, and places all basic weaknesses on the table.
|
||||||
function spawnWeaknesses()
|
function spawnWeaknesses()
|
||||||
|
if not allCardsBagApi.isIndexReady() then return end
|
||||||
|
|
||||||
prepareToPlaceCards()
|
prepareToPlaceCards()
|
||||||
local indexReady = allCardsBagApi.isIndexReady()
|
|
||||||
if (not indexReady) then
|
local basicWeaknessList = {}
|
||||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
local otherWeaknessList = {}
|
||||||
return
|
for _, id in ipairs(allCardsBagApi.getUniqueWeaknesses()) do
|
||||||
end
|
|
||||||
local weaknessIdList = allCardsBagApi.getUniqueWeaknesses()
|
|
||||||
local basicWeaknessList = { }
|
|
||||||
local otherWeaknessList = { }
|
|
||||||
for i, id in ipairs(weaknessIdList) do
|
|
||||||
local cardMetadata = allCardsBagApi.getCardById(id).metadata
|
local cardMetadata = allCardsBagApi.getCardById(id).metadata
|
||||||
if cardMetadata.basicWeaknessCount ~= nil and cardMetadata.basicWeaknessCount > 0 then
|
if cardMetadata.basicWeaknessCount ~= nil and cardMetadata.basicWeaknessCount > 0 then
|
||||||
table.insert(basicWeaknessList, id)
|
table.insert(basicWeaknessList, id)
|
||||||
@ -721,7 +779,7 @@ function spawnRandomWeakness()
|
|||||||
prepareToPlaceCards()
|
prepareToPlaceCards()
|
||||||
local weaknessId = allCardsBagApi.getRandomWeaknessId()
|
local weaknessId = allCardsBagApi.getRandomWeaknessId()
|
||||||
if (weaknessId == nil) then
|
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
|
return
|
||||||
end
|
end
|
||||||
spawnBag.spawn({
|
spawnBag.spawn({
|
||||||
|
@ -16,7 +16,7 @@ Spawner = { }
|
|||||||
---@param sort boolean True if this list of cards should be sorted before spawning
|
---@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.
|
---@param callback? function Callback to be called after the card/deck spawns.
|
||||||
Spawner.spawnCards = function(cardList, pos, rot, sort, callback)
|
Spawner.spawnCards = function(cardList, pos, rot, sort, callback)
|
||||||
if (sort) then
|
if sort then
|
||||||
table.sort(cardList, Spawner.cardComparator)
|
table.sort(cardList, Spawner.cardComparator)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -25,9 +25,9 @@ Spawner.spawnCards = function(cardList, pos, rot, sort, callback)
|
|||||||
local investigatorCards = { }
|
local investigatorCards = { }
|
||||||
|
|
||||||
for _, card in ipairs(cardList) do
|
for _, card in ipairs(cardList) do
|
||||||
if (card.metadata.type == "Investigator") then
|
if card.metadata.type == "Investigator" then
|
||||||
table.insert(investigatorCards, card)
|
table.insert(investigatorCards, card)
|
||||||
elseif (card.metadata.type == "Minicard") then
|
elseif card.metadata.type == "Minicard" then
|
||||||
table.insert(miniCards, card)
|
table.insert(miniCards, card)
|
||||||
else
|
else
|
||||||
table.insert(standardCards, card)
|
table.insert(standardCards, card)
|
||||||
@ -46,7 +46,7 @@ Spawner.spawnCards = function(cardList, pos, rot, sort, callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
Spawner.spawnCardSpread = function(cardList, startPos, maxCols, rot, sort, callback)
|
Spawner.spawnCardSpread = function(cardList, startPos, maxCols, rot, sort, callback)
|
||||||
if (sort) then
|
if sort then
|
||||||
table.sort(cardList, Spawner.cardComparator)
|
table.sort(cardList, Spawner.cardComparator)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ end
|
|||||||
---@return string id >= startId
|
---@return string id >= startId
|
||||||
Spawner.findNextAvailableId = function(objectTable, startId)
|
Spawner.findNextAvailableId = function(objectTable, startId)
|
||||||
local id = startId
|
local id = startId
|
||||||
while (objectTable[id] ~= nil) do
|
while objectTable[id] ~= nil do
|
||||||
id = tostring(tonumber(id) + 1)
|
id = tostring(tonumber(id) + 1)
|
||||||
end
|
end
|
||||||
return id
|
return id
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
require("playercards/PlayerCardSpawner")
|
require("playercards/PlayerCardSpawner")
|
||||||
|
|
||||||
-- Allows spawning of defined lists of cards which will be created from the template in the All
|
-- 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
|
-- 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
|
-- 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.
|
-- spawned objects. Objects moved out of this area will not be cleaned up.
|
||||||
--
|
--
|
||||||
-- SpawnSpec: Spawning requires a spawn specification with the following structure:
|
-- 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
|
-- but each requires a separate name
|
||||||
-- cards: A list of card IDs to be spawned
|
-- 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 }
|
-- 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
|
-- 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: 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 moving to the right. globalPos will define the location of the first card, each
|
||||||
-- after that will be moved a predefined distance
|
-- after that will be moved a predefined distance
|
||||||
-- spreadCols: Optional integer. If spread is true, specifies the maximum columns cards will be
|
-- 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
|
-- 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)
|
-- cards will be in a single row (however long that may be)
|
||||||
-- }
|
-- }
|
||||||
-- See BondedBag.ttslua for an example
|
-- See BondedBag.ttslua for an example
|
||||||
do
|
do
|
||||||
local allCardsBagApi = require("playercards/AllCardsBagApi")
|
local allCardsBagApi = require("playercards/AllCardsBagApi")
|
||||||
|
|
||||||
local SpawnBag = { }
|
local SpawnBag = {}
|
||||||
local internal = { }
|
local internal = {}
|
||||||
|
|
||||||
-- To assist debugging, will draw a box around the recall zone when it's set up
|
-- To assist debugging, will draw a box around the recall zone when it's set up
|
||||||
local SHOW_RECALL_ZONE = false
|
local SHOW_RECALL_ZONE = false
|
||||||
@ -36,8 +36,8 @@ do
|
|||||||
local RECALL_BUFFER_Z = 0.5
|
local RECALL_BUFFER_Z = 0.5
|
||||||
|
|
||||||
-- In order to mimic the behavior of the previous memory buttons we use a temporary bag when
|
-- 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
|
-- 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
|
-- this object. Once all placed cards are recalled bag to this bag, it will be destroyed
|
||||||
local RECALL_BAG = {
|
local RECALL_BAG = {
|
||||||
Name = "Bag",
|
Name = "Bag",
|
||||||
Transform = {
|
Transform = {
|
||||||
@ -58,8 +58,8 @@ do
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- Tracks what has been placed by this "bag" so they can be recalled
|
-- Tracks what has been placed by this "bag" so they can be recalled
|
||||||
local placedSpecs = { }
|
local placedSpecs = {}
|
||||||
local placedObjectGuids = { }
|
local placedObjectGuids = {}
|
||||||
local recallZone = nil
|
local recallZone = nil
|
||||||
|
|
||||||
-- Loads a table of saved state, extracted during the parent object's onLoad
|
-- Loads a table of saved state, extracted during the parent object's onLoad
|
||||||
@ -81,24 +81,16 @@ do
|
|||||||
-- Places the given spawnSpec on the table. See comment at the start of the file for spawnSpec table data and examples
|
-- 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)
|
SpawnBag.spawn = function(spawnSpec)
|
||||||
-- Limit to one placement at a time
|
-- Limit to one placement at a time
|
||||||
if (placedSpecs[spawnSpec.name]) then
|
if placedSpecs[spawnSpec.name] or spawnSpec == nil then return end
|
||||||
return
|
|
||||||
end
|
local cardsToSpawn = {}
|
||||||
if (spawnSpec == nil) then
|
for _, cardId in ipairs(spawnSpec.cards) do
|
||||||
-- TODO: error here
|
local card = allCardsBagApi.getCardById(cardId)
|
||||||
return
|
if card ~= nil then
|
||||||
end
|
table.insert(cardsToSpawn, card)
|
||||||
local cardsToSpawn = { }
|
|
||||||
local cardList = spawnSpec.cards
|
|
||||||
for _, cardId in ipairs(cardList) do
|
|
||||||
local cardData = allCardsBagApi.getCardById(cardId)
|
|
||||||
if (cardData ~= nil) then
|
|
||||||
table.insert(cardsToSpawn, cardData)
|
|
||||||
else
|
|
||||||
-- TODO: error here
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (spawnSpec.spread) then
|
if spawnSpec.spread then
|
||||||
Spawner.spawnCardSpread(cardsToSpawn, spawnSpec.globalPos, spawnSpec.spreadCols or 9999, spawnSpec.rotation, false, internal.recordPlacedObject)
|
Spawner.spawnCardSpread(cardsToSpawn, spawnSpec.globalPos, spawnSpec.spreadCols or 9999, spawnSpec.rotation, false, internal.recordPlacedObject)
|
||||||
else
|
else
|
||||||
-- TTS decks come out in reverse order of the cards, reverse the list so the input order stays
|
-- TTS decks come out in reverse order of the cards, reverse the list so the input order stays
|
||||||
@ -120,14 +112,13 @@ do
|
|||||||
internal.recallSpawned()
|
internal.recallSpawned()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We've recalled everything we can, some cards may have been moved out of the
|
-- We've recalled everything we can, some cards may have been moved out of the card area. Just reset at this point.
|
||||||
-- card area. Just reset at this point.
|
placedSpecs = {}
|
||||||
placedSpecs = { }
|
placedObjectGuids = {}
|
||||||
placedObjectGuids = { }
|
|
||||||
recallZone = nil
|
recallZone = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Deleted all spawned cards.
|
-- Delete all spawned cards
|
||||||
internal.deleteSpawned = function()
|
internal.deleteSpawned = function()
|
||||||
for guid, _ in pairs(placedObjectGuids) do
|
for guid, _ in pairs(placedObjectGuids) do
|
||||||
local obj = getObjectFromGUID(guid)
|
local obj = getObjectFromGUID(guid)
|
||||||
@ -140,9 +131,9 @@ do
|
|||||||
end
|
end
|
||||||
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()
|
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
|
for guid, _ in pairs(placedObjectGuids) do
|
||||||
local obj = getObjectFromGUID(guid)
|
local obj = getObjectFromGUID(guid)
|
||||||
if (obj ~= nil) then
|
if (obj ~= nil) then
|
||||||
@ -156,71 +147,70 @@ do
|
|||||||
trash.destruct()
|
trash.destruct()
|
||||||
end
|
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)
|
internal.recordPlacedObject = function(spawned)
|
||||||
placedObjectGuids[spawned.getGUID()] = true
|
placedObjectGuids[spawned.getGUID()] = true
|
||||||
internal.expandRecallZone(spawned)
|
internal.expandRecallZone(spawned)
|
||||||
end
|
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
|
-- be maintained as the bounding box of the extreme object positions, plus a small amount of buffer
|
||||||
internal.expandRecallZone = function(spawnedCard)
|
internal.expandRecallZone = function(spawnedCard)
|
||||||
local pos = spawnedCard.getPosition()
|
local pos = spawnedCard.getPosition()
|
||||||
if (recallZone == nil) then
|
if (recallZone == nil) then
|
||||||
-- First card out of the bag, initialize surrounding that
|
-- 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.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 }
|
recallZone.lowerRight = { x = pos.x - RECALL_BUFFER_X, z = pos.z - RECALL_BUFFER_Z }
|
||||||
return
|
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
|
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 y = 1.5
|
||||||
local thick = 0.05
|
local thick = 0.05
|
||||||
Global.setVectorLines({
|
Global.setVectorLines({
|
||||||
{
|
{
|
||||||
points = { {recallZone.upperLeft.x,y,recallZone.upperLeft.z}, {recallZone.upperLeft.x,y,recallZone.lowerRight.z} },
|
points = { { recallZone.upperLeft.x, y, recallZone.upperLeft.z }, { recallZone.upperLeft.x, y, recallZone.lowerRight.z } },
|
||||||
color = {1,0,0},
|
color = { 1, 0, 0 },
|
||||||
thickness = thick,
|
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} },
|
points = { { recallZone.upperLeft.x, y, recallZone.lowerRight.z }, { recallZone.lowerRight.x, y, recallZone.lowerRight.z } },
|
||||||
color = {1,0,0},
|
color = { 1, 0, 0 },
|
||||||
thickness = thick,
|
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} },
|
points = { { recallZone.lowerRight.x, y, recallZone.lowerRight.z }, { recallZone.lowerRight.x, y, recallZone.upperLeft.z } },
|
||||||
color = {1,0,0},
|
color = { 1, 0, 0 },
|
||||||
thickness = thick,
|
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} },
|
points = { { recallZone.lowerRight.x, y, recallZone.upperLeft.z }, { recallZone.upperLeft.x, y, recallZone.upperLeft.z } },
|
||||||
color = {1,0,0},
|
color = { 1, 0, 0 },
|
||||||
thickness = thick,
|
thickness = thick,
|
||||||
rotation = {0,0,0}
|
rotation = { 0, 0, 0 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
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.
|
-- will return true so that everything can be easily cleaned up.
|
||||||
internal.isInRecallZone = function(obj)
|
internal.isInRecallZone = function(obj)
|
||||||
if (recallZone == nil) then
|
if (recallZone == nil) then
|
||||||
@ -228,11 +218,11 @@ do
|
|||||||
end
|
end
|
||||||
local pos = obj.getPosition()
|
local pos = obj.getPosition()
|
||||||
return (pos.x < recallZone.upperLeft.x and pos.x > recallZone.lowerRight.x
|
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
|
end
|
||||||
|
|
||||||
internal.reverseList = function(list)
|
internal.reverseList = function(list)
|
||||||
local reversed = { }
|
local reversed = {}
|
||||||
for i = 1, #list do
|
for i = 1, #list do
|
||||||
reversed[i] = list[#list - i + 1]
|
reversed[i] = list[#list - i + 1]
|
||||||
end
|
end
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
<Panel
|
|
||||||
active="false"
|
|
||||||
id="helpPanel"
|
|
||||||
position="-165 -60 -2"
|
|
||||||
rotation="0 0 180"
|
|
||||||
height="55"
|
|
||||||
width="107"
|
|
||||||
color="#00000099">
|
|
||||||
<Text
|
|
||||||
id="helpText"
|
|
||||||
rectAlignment="MiddleCenter"
|
|
||||||
height="490"
|
|
||||||
width="1000"
|
|
||||||
scale="0.1 0.1 1"
|
|
||||||
fontSize="66"
|
|
||||||
color="#F5F5F5"
|
|
||||||
backgroundColor="#FF0000"
|
|
||||||
alignment="UpperLeft"
|
|
||||||
horizontalOverflow="wrap">
|
|
||||||
• 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</Text>
|
|
||||||
</Panel>
|
|
Loading…
Reference in New Issue
Block a user