2022-12-13 14:02:30 -05:00
|
|
|
-- Bundled by luabundle {"version":"1.6.0"}
|
|
|
|
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
|
|
|
|
local loadingPlaceholder = {[{}] = true}
|
|
|
|
|
|
|
|
local register
|
|
|
|
local modules = {}
|
|
|
|
|
|
|
|
local require
|
|
|
|
local loaded = {}
|
|
|
|
|
|
|
|
register = function(name, body)
|
|
|
|
if not modules[name] then
|
|
|
|
modules[name] = body
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
require = function(name)
|
|
|
|
local loadedModule = loaded[name]
|
|
|
|
|
|
|
|
if loadedModule then
|
|
|
|
if loadedModule == loadingPlaceholder then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if not modules[name] then
|
|
|
|
if not superRequire then
|
|
|
|
local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
|
|
|
|
error('Tried to require ' .. identifier .. ', but no such module has been registered')
|
|
|
|
else
|
|
|
|
return superRequire(name)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
loaded[name] = loadingPlaceholder
|
|
|
|
loadedModule = modules[name](require, loaded, register, modules)
|
|
|
|
loaded[name] = loadedModule
|
|
|
|
end
|
|
|
|
|
|
|
|
return loadedModule
|
|
|
|
end
|
|
|
|
|
|
|
|
return require, loaded, register, modules
|
|
|
|
end)(nil)
|
2024-03-10 09:56:22 -04:00
|
|
|
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
|
|
|
|
require("playercards/AllCardsBag")
|
|
|
|
end)
|
2022-12-13 14:02:30 -05:00
|
|
|
__bundle_register("playercards/AllCardsBag", function(require, _LOADED, __bundle_register, __bundle_modules)
|
2024-07-27 21:47:52 -04:00
|
|
|
local guidReferenceApi = require("core/GUIDReferenceApi")
|
|
|
|
|
|
|
|
local cardIdIndex = {}
|
|
|
|
local classAndLevelIndex = {}
|
|
|
|
local basicWeaknessList = {}
|
|
|
|
local uniqueWeaknessList = {}
|
|
|
|
local cycleIndex = {}
|
2021-09-30 19:49:38 -04:00
|
|
|
|
|
|
|
local indexingDone = false
|
2024-07-27 21:47:52 -04:00
|
|
|
local otherCardsDetected = false
|
2021-09-30 19:49:38 -04:00
|
|
|
|
|
|
|
function onLoad()
|
|
|
|
self.addContextMenuItem("Rebuild Index", startIndexBuild)
|
2021-10-18 15:54:27 -04:00
|
|
|
math.randomseed(os.time())
|
2021-09-30 19:49:38 -04:00
|
|
|
Wait.frames(startIndexBuild, 30)
|
|
|
|
end
|
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- Called by Hotfix bags when they load. If we are still loading indexes, then
|
2021-10-18 15:54:27 -04:00
|
|
|
-- the all cards and hotfix bags are being loaded together, and we can ignore
|
2024-07-27 21:47:52 -04:00
|
|
|
-- this call as the hotfix will be included in the initial indexing. If it is
|
2021-10-18 15:54:27 -04:00
|
|
|
-- called once indexing is complete it means the hotfix bag has been added
|
|
|
|
-- later, and we should rebuild the index to integrate the hotfix bag.
|
|
|
|
function rebuildIndexForHotfix()
|
2024-07-27 21:47:52 -04:00
|
|
|
if indexingDone then
|
2021-10-18 15:54:27 -04:00
|
|
|
startIndexBuild()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-09-30 19:49:38 -04:00
|
|
|
-- Resets all current bag indexes
|
|
|
|
function clearIndexes()
|
|
|
|
indexingDone = false
|
2024-07-27 21:47:52 -04:00
|
|
|
cardIdIndex = {}
|
|
|
|
classAndLevelIndex = {}
|
|
|
|
classAndLevelIndex["Guardian-upgrade"] = {}
|
|
|
|
classAndLevelIndex["Seeker-upgrade"] = {}
|
|
|
|
classAndLevelIndex["Mystic-upgrade"] = {}
|
|
|
|
classAndLevelIndex["Survivor-upgrade"] = {}
|
|
|
|
classAndLevelIndex["Rogue-upgrade"] = {}
|
|
|
|
classAndLevelIndex["Neutral-upgrade"] = {}
|
|
|
|
classAndLevelIndex["Guardian-level0"] = {}
|
|
|
|
classAndLevelIndex["Seeker-level0"] = {}
|
|
|
|
classAndLevelIndex["Mystic-level0"] = {}
|
|
|
|
classAndLevelIndex["Survivor-level0"] = {}
|
|
|
|
classAndLevelIndex["Rogue-level0"] = {}
|
|
|
|
classAndLevelIndex["Neutral-level0"] = {}
|
|
|
|
cycleIndex = {}
|
|
|
|
basicWeaknessList = {}
|
|
|
|
uniqueWeaknessList = {}
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Clears the bag indexes and starts the coroutine to rebuild the indexes
|
2024-02-17 19:48:30 -05:00
|
|
|
function startIndexBuild()
|
2021-09-30 19:49:38 -04:00
|
|
|
clearIndexes()
|
|
|
|
startLuaCoroutine(self, "buildIndex")
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
|
|
|
|
2024-02-17 19:48:30 -05:00
|
|
|
function onObjectLeaveContainer(container, _)
|
|
|
|
if container == self then
|
|
|
|
broadcastToAll("Removing cards from the All Player Cards bag may break some functions.", "Red")
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
|
|
|
end
|
2021-09-30 19:49:38 -04:00
|
|
|
|
2024-06-09 10:10:21 -04:00
|
|
|
-- Create the card indexes by iterating all cards in the bag, parsing their metadata
|
|
|
|
-- and creating the keyed lookup tables for the cards. This is a coroutine which will
|
|
|
|
-- spread the workload by processing 20 cards before yielding.
|
2021-09-30 19:49:38 -04:00
|
|
|
function buildIndex()
|
2024-02-04 10:51:51 -05:00
|
|
|
local cardCount = 0
|
2021-09-30 19:49:38 -04:00
|
|
|
indexingDone = false
|
2024-07-27 21:47:52 -04:00
|
|
|
otherCardsDetected = false
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- process the allcardsbag itself
|
|
|
|
for _, cardData in ipairs(self.getData().ContainedObjects) do
|
|
|
|
addCardToIndex(cardData)
|
|
|
|
cardCount = cardCount + 1
|
|
|
|
if cardCount > 19 then
|
|
|
|
cardCount = 0
|
|
|
|
coroutine.yield(0)
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- process hotfix bags (and the additional playercards bag)
|
|
|
|
for _, hotfixBag in ipairs(getObjectsWithTag("AllCardsHotfix")) do
|
|
|
|
local hotfixData = hotfixBag.getData()
|
2024-07-27 21:47:52 -04:00
|
|
|
|
|
|
|
-- if the bag is empty, continue with the next bag
|
|
|
|
if not hotfixData.ContainedObjects then
|
|
|
|
goto nextBag
|
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
for _, cardData in ipairs(hotfixData.ContainedObjects) do
|
|
|
|
if cardData.ContainedObjects then
|
2024-07-27 21:47:52 -04:00
|
|
|
-- process containers
|
2024-06-09 10:10:21 -04:00
|
|
|
for _, deepCardData in ipairs(cardData.ContainedObjects) do
|
|
|
|
addCardToIndex(deepCardData)
|
|
|
|
cardCount = cardCount + 1
|
|
|
|
if cardCount > 19 then
|
|
|
|
cardCount = 0
|
|
|
|
coroutine.yield(0)
|
2024-02-04 10:51:51 -05:00
|
|
|
end
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
else
|
2024-07-27 21:47:52 -04:00
|
|
|
-- process single cards
|
2024-06-09 10:10:21 -04:00
|
|
|
addCardToIndex(cardData)
|
|
|
|
cardCount = cardCount + 1
|
|
|
|
if cardCount > 19 then
|
|
|
|
cardCount = 0
|
|
|
|
coroutine.yield(0)
|
|
|
|
end
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
|
|
|
end
|
2024-07-27 21:47:52 -04:00
|
|
|
::nextBag::
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
2021-10-18 15:54:27 -04:00
|
|
|
buildSupplementalIndexes()
|
2024-07-27 21:47:52 -04:00
|
|
|
updatePlayerCardPanel()
|
2021-09-30 19:49:38 -04:00
|
|
|
indexingDone = true
|
|
|
|
return 1
|
|
|
|
end
|
|
|
|
|
2024-06-09 10:10:21 -04:00
|
|
|
-- Adds a card to any indexes it should be a part of, based on its metadata
|
2024-02-17 19:48:30 -05:00
|
|
|
---@param cardData table TTS object data for the card
|
2024-06-09 10:10:21 -04:00
|
|
|
function addCardToIndex(cardData)
|
|
|
|
-- using the more efficient 'json.parse()' to speed this process up
|
2024-07-27 21:47:52 -04:00
|
|
|
local status, cardMetadata = pcall(function() return json.parse(cardData.GMNotes) end)
|
|
|
|
|
|
|
|
-- if an error happens, fallback to the regular parser
|
|
|
|
if status ~= true or cardMetadata == nil then
|
|
|
|
log("Fast parser failed for " .. cardData.Nickname .. ", using old parser instead.")
|
|
|
|
cardMetadata = JSON.decode(cardData.GMNotes)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- if metadata was not valid JSON or empty, don't add the card
|
|
|
|
if not cardMetadata == nil then
|
|
|
|
log("Error parsing " .. cardData.Nickname)
|
|
|
|
return
|
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
2024-02-04 10:51:51 -05:00
|
|
|
-- use the ZoopGuid as fallback if no id present
|
2024-07-27 21:47:52 -04:00
|
|
|
cardMetadata.id = cardMetadata.id or cardMetadata.TtsZoopGuid
|
|
|
|
cardIdIndex[cardMetadata.id] = { data = cardData, metadata = cardMetadata }
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- also add data for alternate ids
|
|
|
|
if cardMetadata.alternate_ids ~= nil then
|
2021-09-30 19:49:38 -04:00
|
|
|
for _, alternateId in ipairs(cardMetadata.alternate_ids) do
|
|
|
|
cardIdIndex[alternateId] = { data = cardData, metadata = cardMetadata }
|
|
|
|
end
|
|
|
|
end
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
2021-09-30 19:49:38 -04:00
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- Creates the supplemental indexes for classes, weaknesses etc.
|
2021-10-18 15:54:27 -04:00
|
|
|
function buildSupplementalIndexes()
|
|
|
|
for cardId, card in pairs(cardIdIndex) do
|
2024-06-09 10:10:21 -04:00
|
|
|
-- 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
|
2024-07-27 21:47:52 -04:00
|
|
|
if cardId == card.metadata.id then
|
2024-06-09 10:10:21 -04:00
|
|
|
-- Add card to the basic weakness list, if appropriate. Some weaknesses have multiple copies, and are added multiple times
|
2024-07-27 21:47:52 -04:00
|
|
|
if card.metadata.weakness then
|
|
|
|
table.insert(uniqueWeaknessList, card.metadata.id)
|
|
|
|
if card.metadata.basicWeaknessCount ~= nil then
|
|
|
|
for i = 1, card.metadata.basicWeaknessCount do
|
|
|
|
table.insert(basicWeaknessList, card.metadata.id)
|
2023-01-29 19:31:52 -05:00
|
|
|
end
|
|
|
|
end
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
2021-09-30 19:49:38 -04:00
|
|
|
|
2024-06-09 10:10:21 -04:00
|
|
|
-- Excludes signature cards (which have no class or level)
|
2024-07-27 21:47:52 -04:00
|
|
|
if card.metadata.class ~= nil and card.metadata.level ~= nil then
|
|
|
|
local upgradeKey = "-level0"
|
|
|
|
if card.metadata.level > 0 then
|
2022-03-27 10:12:31 -04:00
|
|
|
upgradeKey = "-upgrade"
|
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- parse classes (separated by "|") and add the card to the appropriate class and level indices
|
2024-07-27 21:47:52 -04:00
|
|
|
for str in card.metadata.class:gmatch("([^|]+)") do
|
|
|
|
table.insert(classAndLevelIndex[str .. upgradeKey], card.metadata.id)
|
2022-03-27 10:12:31 -04:00
|
|
|
end
|
2023-01-29 19:31:52 -05:00
|
|
|
|
2024-06-09 10:10:21 -04:00
|
|
|
-- add to cycle index
|
2024-07-27 21:47:52 -04:00
|
|
|
local cycleName = card.metadata.cycle
|
2023-01-29 19:31:52 -05:00
|
|
|
if cycleName ~= nil then
|
|
|
|
cycleName = string.lower(cycleName)
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- remove "return to " from cycle names
|
|
|
|
cycleName = cycleName:gsub("return to ", "")
|
|
|
|
|
|
|
|
-- override cycle name for night of the zealot
|
|
|
|
cycleName = cycleName:gsub("the night of the zealot", "core")
|
2024-07-27 21:47:52 -04:00
|
|
|
else
|
|
|
|
-- track cards without defined cycle (should only be fan-made cards)
|
|
|
|
cycleName = "other"
|
|
|
|
otherCardsDetected = true
|
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- maybe initialize table
|
|
|
|
if cycleIndex[cycleName] == nil then
|
|
|
|
cycleIndex[cycleName] = {}
|
2023-01-29 19:31:52 -05:00
|
|
|
end
|
2024-07-27 21:47:52 -04:00
|
|
|
table.insert(cycleIndex[cycleName], card.metadata.id)
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
|
|
|
end
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- sort class and level indices
|
2021-10-18 15:54:27 -04:00
|
|
|
for _, indexTable in pairs(classAndLevelIndex) do
|
|
|
|
table.sort(indexTable, cardComparator)
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- sort cycle indices
|
2023-01-29 19:31:52 -05:00
|
|
|
for _, indexTable in pairs(cycleIndex) do
|
|
|
|
table.sort(indexTable)
|
|
|
|
end
|
2024-06-09 10:10:21 -04:00
|
|
|
|
|
|
|
-- sort weakness indices
|
2023-01-29 19:31:52 -05:00
|
|
|
table.sort(basicWeaknessList, cardComparator)
|
|
|
|
table.sort(uniqueWeaknessList, cardComparator)
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
|
|
|
|
2024-06-09 10:10:21 -04:00
|
|
|
-- Comparison function used to sort the class card bag indexes. Sorts by card level, then name, then subname.
|
2021-10-18 15:54:27 -04:00
|
|
|
function cardComparator(id1, id2)
|
|
|
|
local card1 = cardIdIndex[id1]
|
|
|
|
local card2 = cardIdIndex[id2]
|
2022-10-19 19:07:47 -04:00
|
|
|
|
2024-06-09 10:10:21 -04:00
|
|
|
if card1.metadata.level ~= card2.metadata.level then
|
2021-09-30 19:49:38 -04:00
|
|
|
return card1.metadata.level < card2.metadata.level
|
2024-06-09 10:10:21 -04:00
|
|
|
elseif card1.data.Nickname ~= card2.data.Nickname then
|
2021-09-30 19:49:38 -04:00
|
|
|
return card1.data.Nickname < card2.data.Nickname
|
2024-06-09 10:10:21 -04:00
|
|
|
else
|
|
|
|
return card1.data.Description < card2.data.Description
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- 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
|
2021-10-18 15:54:27 -04:00
|
|
|
function isIndexReady()
|
2024-07-27 21:47:52 -04:00
|
|
|
if not indexingDone then
|
|
|
|
broadcastToAll("Still loading player cards, please try again in a few seconds", { 0.9, 0.2, 0.2 })
|
|
|
|
end
|
2021-10-18 15:54:27 -04:00
|
|
|
return indexingDone
|
|
|
|
end
|
|
|
|
|
2021-09-30 19:49:38 -04:00
|
|
|
-- Returns a specific card from the bag, based on ArkhamDB ID
|
2024-07-27 21:47:52 -04:00
|
|
|
---@param params table ID of the card to retrieve
|
|
|
|
---@return table: If the indexes are still being constructed, returns an empty table.
|
|
|
|
-- Otherwise, a single table with the following fields
|
|
|
|
-- data: TTS object data, suitable for spawning the card
|
|
|
|
-- metadata: Table of parsed metadata
|
2021-09-30 19:49:38 -04:00
|
|
|
function getCardById(params)
|
2024-07-27 21:47:52 -04:00
|
|
|
if not isIndexReady() then return {} end
|
2021-09-30 19:49:38 -04:00
|
|
|
return cardIdIndex[params.id]
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns a list of cards from the bag matching a class and level (0 or upgraded)
|
2024-07-27 21:47:52 -04:00
|
|
|
---@param params table
|
|
|
|
-- class: String class to retrieve ("Guardian", "Seeker", etc)
|
|
|
|
-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0
|
|
|
|
---@return table: If the indexes are still being constructed, returns an empty table.
|
|
|
|
-- Otherwise, a list of tables, each with the following fields
|
|
|
|
-- data: TTS object data, suitable for spawning the card
|
|
|
|
-- metadata: Table of parsed metadata
|
2021-09-30 19:49:38 -04:00
|
|
|
function getCardsByClassAndLevel(params)
|
2024-07-27 21:47:52 -04:00
|
|
|
if not isIndexReady() then return {} end
|
|
|
|
|
|
|
|
local upgradeKey = "-level0"
|
|
|
|
if params.upgraded then
|
2021-09-30 19:49:38 -04:00
|
|
|
upgradeKey = "-upgrade"
|
2024-07-27 21:47:52 -04:00
|
|
|
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
|
2021-09-30 19:49:38 -04:00
|
|
|
else
|
2024-07-27 21:47:52 -04:00
|
|
|
return card1.data.Description < card2.data.Description
|
2021-09-30 19:49:38 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- helper function to calculate the class value for sorting from the "|" separated string
|
|
|
|
function getClassValueFromString(s)
|
|
|
|
local classValueList = {
|
|
|
|
Guardian = 1,
|
|
|
|
Seeker = 2,
|
|
|
|
Rogue = 3,
|
|
|
|
Mystic = 4,
|
|
|
|
Survivor = 5,
|
|
|
|
Neutral = 6
|
|
|
|
}
|
|
|
|
local classValue = 0
|
|
|
|
for str in s:gmatch("([^|]+)") do
|
|
|
|
-- this sorts multiclass cards
|
|
|
|
classValue = classValue * 10 + classValueList[str]
|
2023-01-29 19:31:52 -05:00
|
|
|
end
|
2024-07-27 21:47:52 -04:00
|
|
|
return classValue
|
2023-01-29 19:31:52 -05:00
|
|
|
end
|
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- 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.
|
2022-10-19 19:07:47 -04:00
|
|
|
-- Parameter array must contain these fields to define the search:
|
2024-07-27 21:47:52 -04:00
|
|
|
-- name: String or string fragment to search for names
|
|
|
|
-- exact: Whether the name match should be exact
|
2022-10-19 19:07:47 -04:00
|
|
|
function getCardsByName(params)
|
|
|
|
local name = params.name
|
|
|
|
local exact = params.exact
|
2024-07-27 21:47:52 -04:00
|
|
|
local results = {}
|
|
|
|
|
2022-10-19 19:07:47 -04:00
|
|
|
-- Track cards (by ID) that we've added to avoid duplicates that may come from alternate IDs
|
2024-07-27 21:47:52 -04:00
|
|
|
local addedCards = {}
|
2022-10-19 19:07:47 -04:00
|
|
|
for _, cardData in pairs(cardIdIndex) do
|
|
|
|
if (not addedCards[cardData.metadata.id]) then
|
|
|
|
if (exact and (string.lower(cardData.data.Nickname) == string.lower(name)))
|
|
|
|
or (not exact and string.find(string.lower(cardData.data.Nickname), string.lower(name), 1, true)) then
|
2024-07-27 21:47:52 -04:00
|
|
|
table.insert(results, cardData)
|
|
|
|
addedCards[cardData.metadata.id] = true
|
2022-10-19 19:07:47 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return results
|
|
|
|
end
|
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
-- 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
|
2021-09-30 19:49:38 -04:00
|
|
|
function getRandomWeaknessId()
|
2024-07-27 21:47:52 -04:00
|
|
|
local availableWeaknesses = buildAvailableWeaknesses()
|
|
|
|
if #availableWeaknesses > 0 then
|
2022-03-27 10:12:31 -04:00
|
|
|
return availableWeaknesses[math.random(#availableWeaknesses)]
|
2021-10-18 15:54:27 -04:00
|
|
|
end
|
2022-03-27 10:12:31 -04:00
|
|
|
end
|
2021-09-30 19:49:38 -04:00
|
|
|
|
2022-03-27 10:12:31 -04:00
|
|
|
-- 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
|
2024-07-27 21:47:52 -04:00
|
|
|
---@param traits? string Trait(s) to use as filter
|
|
|
|
---@return table: Array of weakness IDs which are valid to choose from
|
|
|
|
function buildAvailableWeaknesses(traits)
|
|
|
|
local weaknessesInPlay = {}
|
2022-03-27 10:12:31 -04:00
|
|
|
local allObjects = getAllObjects()
|
|
|
|
for _, object in ipairs(allObjects) do
|
2024-07-27 21:47:52 -04:00
|
|
|
if object.type == "Deck" then
|
2022-03-27 10:12:31 -04:00
|
|
|
for _, cardData in ipairs(object.getData().ContainedObjects) do
|
2024-07-27 21:47:52 -04:00
|
|
|
incrementWeaknessCount(weaknessesInPlay, JSON.decode(cardData.GMNotes))
|
2022-03-27 10:12:31 -04:00
|
|
|
end
|
2024-07-27 21:47:52 -04:00
|
|
|
elseif object.type == "Card" then
|
|
|
|
incrementWeaknessCount(weaknessesInPlay, JSON.decode(object.getGMNotes()))
|
2022-03-27 10:12:31 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-27 21:47:52 -04:00
|
|
|
local availableWeaknesses = {}
|
2022-03-27 10:12:31 -04:00
|
|
|
for _, weaknessId in ipairs(basicWeaknessList) do
|
|
|
|
if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then
|
|
|
|
weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1
|
|
|
|
else
|
2024-07-27 21:47:52 -04:00
|
|
|
if traits then
|
|
|
|
-- split the string into separate traits (separated by "|")
|
|
|
|
local allowedTraits = {}
|
|
|
|
for str in traits:gmatch("([^|]+)") do
|
|
|
|
-- remove dots
|
|
|
|
str = str:gsub("[%.]", "")
|
|
|
|
|
|
|
|
-- remove leading and trailing whitespace
|
|
|
|
str = str:match("^%s*(.-)%s*$")
|
|
|
|
|
|
|
|
-- make sure string ends with a dot
|
|
|
|
str = string.lower(str .. ".")
|
|
|
|
table.insert(allowedTraits, str)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- make sure the trait is present on the weakness
|
|
|
|
local card = cardIdIndex[weaknessId]
|
|
|
|
for _, allowedTrait in ipairs(allowedTraits) do
|
|
|
|
if string.contains(string.lower(card.metadata.traits), allowedTrait) then
|
|
|
|
table.insert(availableWeaknesses, weaknessId)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
table.insert(availableWeaknesses, weaknessId)
|
|
|
|
end
|
2022-03-27 10:12:31 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
return availableWeaknesses
|
|
|
|
end
|
|
|
|
|
2023-01-29 19:31:52 -05:00
|
|
|
function getBasicWeaknesses()
|
|
|
|
return basicWeaknessList
|
|
|
|
end
|
|
|
|
|
|
|
|
function getUniqueWeaknesses()
|
|
|
|
return uniqueWeaknessList
|
|
|
|
end
|
|
|
|
|
2022-03-27 10:12:31 -04:00
|
|
|
-- Helper function that adds one to the table entry for the number of weaknesses in play
|
|
|
|
function incrementWeaknessCount(table, cardMetadata)
|
2024-07-27 21:47:52 -04:00
|
|
|
if isBasicWeakness(cardMetadata) then
|
|
|
|
if table[cardMetadata.id] == nil then
|
2022-03-27 10:12:31 -04:00
|
|
|
table[cardMetadata.id] = 1
|
|
|
|
else
|
|
|
|
table[cardMetadata.id] = table[cardMetadata.id] + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function isBasicWeakness(cardMetadata)
|
|
|
|
return cardMetadata ~= nil
|
2024-07-27 21:47:52 -04:00
|
|
|
and cardMetadata.weakness
|
|
|
|
and cardMetadata.basicWeaknessCount ~= nil
|
|
|
|
and cardMetadata.basicWeaknessCount > 0
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
__bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules)
|
|
|
|
do
|
|
|
|
local GUIDReferenceApi = {}
|
|
|
|
|
|
|
|
local function getGuidHandler()
|
|
|
|
return getObjectFromGUID("123456")
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns the matching object
|
|
|
|
---@param owner string Parent object for this search
|
|
|
|
---@param type string Type of object to search for
|
|
|
|
---@return any: Object reference to the matching object
|
|
|
|
GUIDReferenceApi.getObjectByOwnerAndType = function(owner, type)
|
|
|
|
return getGuidHandler().call("getObjectByOwnerAndType", { owner = owner, type = type })
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns all matching objects as a table with references
|
|
|
|
---@param type string Type of object to search for
|
|
|
|
---@return table: List of object references to matching objects
|
|
|
|
GUIDReferenceApi.getObjectsByType = function(type)
|
|
|
|
return getGuidHandler().call("getObjectsByType", type)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns all matching objects as a table with references
|
|
|
|
---@param owner string Parent object for this search
|
|
|
|
---@return table: List of object references to matching objects
|
|
|
|
GUIDReferenceApi.getObjectsByOwner = function(owner)
|
|
|
|
return getGuidHandler().call("getObjectsByOwner", owner)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Sends new information to the reference handler to edit the main index
|
|
|
|
---@param owner string Parent of the object
|
|
|
|
---@param type string Type of the object
|
|
|
|
---@param guid string GUID of the object
|
|
|
|
GUIDReferenceApi.editIndex = function(owner, type, guid)
|
|
|
|
return getGuidHandler().call("editIndex", {
|
|
|
|
owner = owner,
|
|
|
|
type = type,
|
|
|
|
guid = guid
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns the owner of an object or the object it's located on
|
|
|
|
---@param object tts__GameObject Object for this search
|
|
|
|
---@return string: Parent of the object or object it's located on
|
|
|
|
GUIDReferenceApi.getOwnerOfObject = function(object)
|
|
|
|
return getGuidHandler().call("getOwnerOfObject", object)
|
|
|
|
end
|
|
|
|
|
|
|
|
return GUIDReferenceApi
|
2022-12-13 14:02:30 -05:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
return __bundle_require("__root")
|