Merge pull request #944 from argonui/attachment-import
Updated Deck Importer's Attachment handling
This commit is contained in:
commit
0958f6fa7e
@ -34,6 +34,10 @@
|
||||
"Nickname": "Attachment Helper",
|
||||
"Snap": true,
|
||||
"Sticky": true,
|
||||
"Tags": [
|
||||
"AttachmentHelperBag",
|
||||
"CleanUpHelper_ignore"
|
||||
],
|
||||
"Tooltip": true,
|
||||
"Transform": {
|
||||
"posX": 27.677,
|
||||
|
@ -164,28 +164,47 @@ function onDrop(playerColor)
|
||||
-- assume the first eligible object as intended sync object
|
||||
local syncObj = searchResult[1]
|
||||
local syncName = syncObj.getName()
|
||||
local notes = syncObj.getGMNotes()
|
||||
local md = JSON.decode(notes) or {}
|
||||
local md = JSON.decode(syncObj.getGMNotes()) or {}
|
||||
|
||||
local found = loadDataFromMetadata({
|
||||
md = md,
|
||||
playerColor = playerColor,
|
||||
syncName = syncName,
|
||||
syncRotation = syncObj.getRotation()
|
||||
})
|
||||
if not found then
|
||||
printToColor("Didn't find background for '" .. syncName .. "'!", playerColor, "Orange")
|
||||
end
|
||||
end
|
||||
|
||||
function loadDataFromMetadata(params)
|
||||
local md = params.md
|
||||
local syncRotation = params.syncRotation
|
||||
local playerColor = params.playerColor
|
||||
local syncName = params.syncName
|
||||
|
||||
-- loop through background table
|
||||
for _, bgInfo in ipairs(BACKGROUNDS) do
|
||||
if bgInfo.id == md.id or bgInfo.id2 == md.id then
|
||||
if syncName and playerColor then
|
||||
printToColor("Background for '" .. syncName .. "' loaded!", playerColor, "Green")
|
||||
end
|
||||
showIcons = bgInfo.icons
|
||||
|
||||
-- update rotation
|
||||
local syncRotY = syncObj.getRotation().y
|
||||
-- maybe update rotation
|
||||
if syncRotation then
|
||||
if md.type == "Investigator" then
|
||||
syncRotY = syncRotY + 90
|
||||
syncRotation.y = syncRotation.y + 90
|
||||
end
|
||||
self.setRotation(self.getRotation():setAt("y", syncRotation.y))
|
||||
end
|
||||
self.setRotation(self.getRotation():setAt("y", syncRotY))
|
||||
|
||||
-- update the image
|
||||
updateImage(bgInfo.url)
|
||||
return
|
||||
return true
|
||||
end
|
||||
end
|
||||
printToColor("Didn't find background for '" .. syncName .. "'!", playerColor, "Orange")
|
||||
return false
|
||||
end
|
||||
|
||||
-- called by context menu to change background image
|
||||
|
@ -193,7 +193,11 @@ do
|
||||
for _, weaknessId in ipairs(weaknessIds) do
|
||||
slots[weaknessId] = (slots[weaknessId] or 0) + 1
|
||||
end
|
||||
internal.maybePrint("Added " .. #weaknessIds .. " random basic weakness(es) to deck", playerColor)
|
||||
if #weaknessIds == 1 then
|
||||
internal.maybePrint("Added 1 random basic weakness to deck", playerColor)
|
||||
else
|
||||
internal.maybePrint("Added " .. #weaknessIds .. " random basic weaknesses to deck", playerColor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -2,13 +2,13 @@ require("playercards/PlayerCardSpawner")
|
||||
|
||||
local allCardsBagApi = require("playercards/AllCardsBagApi")
|
||||
local arkhamDb = require("arkhamdb/ArkhamDb")
|
||||
local GlobalApi = require("core/GlobalApi")
|
||||
local guidReferenceApi = require("core/GUIDReferenceApi")
|
||||
local playermatApi = require("playermat/PlayermatApi")
|
||||
local zones = require("playermat/Zones")
|
||||
|
||||
local matsWithInvestigator = {}
|
||||
local startsInPlayCount = 0
|
||||
|
||||
local INPUT_FIELD_HEIGHT = 340
|
||||
local INPUT_FIELD_WIDTH = 1500
|
||||
local FIELD_COLOR = { 0.9, 0.7, 0.5 }
|
||||
@ -222,16 +222,19 @@ end
|
||||
---@param cardMetadata table Contains card metadata
|
||||
---@return string Zone Name of the zone such as "Deck", "SetAside1", etc. (See zones file for a list of valid zones)
|
||||
function getDefaultCardZone(cardMetadata, bondedList)
|
||||
if cardMetadata.id == "09080-m" then -- Have to check the Servitor before other minicards
|
||||
if cardMetadata.id == "09080-m" then
|
||||
-- Have to check the Servitor before other minicards
|
||||
return "SetAside5"
|
||||
elseif cardMetadata.id == "09006" or cardMetadata.id == "06233" or cardMetadata.id == "06275"
|
||||
or cardMetadata.id == "71052" or bondedList[cardMetadata.id] then
|
||||
-- On The Mend, False Awakening, Jewel of Sarnath and bonded cards are set aside
|
||||
return "SetAside2"
|
||||
elseif cardMetadata.id == "07303" then
|
||||
-- Ancestral Knowledge
|
||||
return "SetAside3"
|
||||
elseif cardMetadata.id == "09077" or cardMetadata.id == "09077-t" then
|
||||
-- Underworld Market
|
||||
return "SetAside6"
|
||||
elseif cardMetadata.id == "09006" then -- On The Mend is set aside
|
||||
return "SetAside2"
|
||||
elseif cardMetadata.id == "06233" or cardMetadata.id == "06275" then -- False Awakening is set aside
|
||||
return "SetAside2"
|
||||
elseif cardMetadata.id == "71052" then -- Jewel of Sarnath is set aside
|
||||
return "SetAside2"
|
||||
elseif bondedList[cardMetadata.id] then
|
||||
return "SetAside2"
|
||||
elseif cardMetadata.type == "Investigator" then
|
||||
return "Investigator"
|
||||
elseif cardMetadata.type == "Minicard" then
|
||||
@ -242,7 +245,6 @@ function getDefaultCardZone(cardMetadata, bondedList)
|
||||
return startsInPlayTracker()
|
||||
elseif cardMetadata.permanent then
|
||||
return "SetAside1"
|
||||
-- SetAside3 is used for Ancestral Knowledge / Underworld Market
|
||||
else
|
||||
return "Deck"
|
||||
end
|
||||
@ -284,15 +286,46 @@ end
|
||||
---@param loadAltInvestigator string Contains the name of alternative art for the investigator ("normal", "revised" or "promo")
|
||||
function loadCards(slots, investigatorId, bondedList, customizations, playerColor, loadAltInvestigator)
|
||||
function coinside()
|
||||
local slotsCopy = deepCopy(slots)
|
||||
local cardsToSpawn = {}
|
||||
local resourceModifier = 0
|
||||
local zoneWithAttachments = {}
|
||||
cardsWithAttachments = {
|
||||
["03264"] = "reserve", -- Stick to the Plan
|
||||
["05002"] = true, -- Joe Diamond
|
||||
["07303"] = "reserve", -- Ancestral Knowledge
|
||||
["09077"] = true, -- Underworld Market
|
||||
["09077-t"] = true, -- Underworld Market (Taboo)
|
||||
["10079"] = "reserve" -- Bewitching
|
||||
}
|
||||
|
||||
-- reset the startsInPlayCount
|
||||
startsInPlayCount = 0
|
||||
|
||||
-- reserve slots for certain cards with attachments
|
||||
for cardId, state in pairs(cardsWithAttachments) do
|
||||
if state == "reserve" then
|
||||
if slotsCopy[cardId] and slotsCopy[cardId] > 0 then
|
||||
-- increase startsInPlayCount by 1 and reserve slot for this card
|
||||
startsInPlayCount = startsInPlayCount + 1
|
||||
cardsWithAttachments[cardId] = startsInPlayCount
|
||||
|
||||
-- reserve an additional slot for the attachments
|
||||
startsInPlayCount = startsInPlayCount + 1
|
||||
zoneWithAttachments["Blank" .. startsInPlayCount] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for cardId, cardCount in pairs(slots) do
|
||||
local card = allCardsBagApi.getCardById(cardId)
|
||||
if card ~= nil then
|
||||
local cardZone = getDefaultCardZone(card.metadata, bondedList)
|
||||
local reservedSlot = cardsWithAttachments[cardId]
|
||||
if type(reservedSlot) == "number" and reservedSlot < 6 then
|
||||
cardZone = "Blank" .. cardsWithAttachments[cardId]
|
||||
else
|
||||
cardZone = getDefaultCardZone(card.metadata, bondedList)
|
||||
end
|
||||
for i = 1, cardCount do
|
||||
table.insert(cardsToSpawn, { data = card.data, metadata = card.metadata, zone = cardZone })
|
||||
end
|
||||
@ -308,10 +341,13 @@ function loadCards(slots, investigatorId, bondedList, customizations, playerColo
|
||||
end
|
||||
|
||||
updateStartingResources(playerColor, resourceModifier)
|
||||
handleAncestralKnowledge(cardsToSpawn)
|
||||
handleUnderworldMarket(cardsToSpawn, playerColor)
|
||||
handleHunchDeck(investigatorId, cardsToSpawn, bondedList, playerColor)
|
||||
handleSpiritDeck(investigatorId, cardsToSpawn, playerColor, customizations)
|
||||
handleAllAttachments(cardsToSpawn, slotsCopy, bondedList, customizations, playerColor)
|
||||
|
||||
-- parallel Jim Culver handling
|
||||
if investigatorId == "02004-p" or investigatorId == "02004-pb" then
|
||||
handleSpiritDeck(cardsToSpawn, playerColor, customizations)
|
||||
end
|
||||
|
||||
handleCustomizableUpgrades(cardsToSpawn, customizations)
|
||||
handlePeteSignatureAssets(investigatorId, cardsToSpawn)
|
||||
|
||||
@ -321,12 +357,37 @@ function loadCards(slots, investigatorId, bondedList, customizations, playerColo
|
||||
-- Check for existing cards in zones and maybe skip them
|
||||
removeBusyZones(playerColor, zoneDecks)
|
||||
|
||||
-- Spawn attachment helpers if option is enabled
|
||||
if GlobalApi.getOptionPanelState()["showAttachmentHelper"] then
|
||||
local objs = getObjectsWithTag("AttachmentHelperBag")
|
||||
if #objs > 0 then
|
||||
local helperRot = zones.getDefaultCardRotation(playerColor, "Blank1")
|
||||
for cardId, reservedSlot in pairs(cardsWithAttachments) do
|
||||
if type(reservedSlot) == "number" and reservedSlot < 6 then
|
||||
local helper = objs[1].takeObject({
|
||||
position = zones.getZonePosition(playerColor, "Blank" .. (reservedSlot + 1)):setAt("y", 1.8),
|
||||
rotation = helperRot,
|
||||
smooth = false
|
||||
})
|
||||
coWaitFrames(3)
|
||||
helper.call("loadDataFromMetadata", { md = { id = cardId } })
|
||||
coWaitFrames(3)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Spawn the list for each zone
|
||||
for zone, zoneCards in pairs(zoneDecks) do
|
||||
local deckPos = zones.getZonePosition(playerColor, zone):setAt("y", 3)
|
||||
local deckRot = zones.getDefaultCardRotation(playerColor, zone)
|
||||
local callback = nil
|
||||
|
||||
-- if this is a zone with attachments, spawn cards facedown and add additional delay
|
||||
if zoneWithAttachments[zone] then
|
||||
deckRot = deckRot:setAt("z", 180)
|
||||
end
|
||||
|
||||
-- If cards are spread too close together TTS groups them weirdly, selecting multiples
|
||||
-- when hovering over a single card. This distance is the minimum to avoid that.
|
||||
local spreadDistance = 1.15
|
||||
@ -347,7 +408,7 @@ function loadCards(slots, investigatorId, bondedList, customizations, playerColo
|
||||
callback = function(card) loadAltArt(card, loadAltInvestigator) end
|
||||
end
|
||||
Spawner.spawnCards(zoneCards, deckPos, deckRot, true, callback)
|
||||
coroutine.yield(0)
|
||||
coWaitFrames(3)
|
||||
end
|
||||
|
||||
-- Look for any cards which haven't been loaded
|
||||
@ -364,7 +425,6 @@ function loadCards(slots, investigatorId, bondedList, customizations, playerColo
|
||||
resumeLoadDecks()
|
||||
return 1
|
||||
end
|
||||
|
||||
startLuaCoroutine(self, "coinside")
|
||||
end
|
||||
|
||||
@ -393,8 +453,7 @@ end
|
||||
|
||||
-- Converts the Raven Quill's selections from card IDs to card names. This could be more elegant
|
||||
-- but the inputs are very static so we're using some brute force.
|
||||
---@param selectionString string provided by ArkhamDB, indicates the customization selections
|
||||
-- Should be either a single card ID or two separated by a ^ (e.g. XXXXX^YYYYY)
|
||||
---@param selectionString string customization selections (either a single card ID or two separated by a ^: XXXXX^YYYYY)
|
||||
function convertRavenQuillSelections(selectionString)
|
||||
if string.len(selectionString) == 5 then
|
||||
return getCardName(selectionString)
|
||||
@ -404,8 +463,7 @@ function convertRavenQuillSelections(selectionString)
|
||||
end
|
||||
|
||||
-- Converts Grizzled's selections from a single string with "^".
|
||||
---@param selectionString string provided by ArkhamDB, indicates the customization selections
|
||||
-- Should be two traits separated by a ^ (e.g. XXXXX^YYYYY)
|
||||
---@param selectionString string customization selections (two traits separated by a ^: XXXXX^YYYYY)
|
||||
function convertGrizzledSelections(selectionString)
|
||||
return selectionString:gsub("%^", ", ")
|
||||
end
|
||||
@ -488,135 +546,126 @@ function removeBusyZones(playerColor, zoneDecks)
|
||||
zoneDecks["Blank" .. i] = nil
|
||||
end
|
||||
zoneDecks["UnderSetAside3"] = nil
|
||||
zoneDecks["UnderSetAside6"] = nil
|
||||
zoneDecks["Deck"] = nil
|
||||
printToAll("Skipped deck import", playerColor)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check to see if the deck list has Ancestral Knowledge. If it does, move 5 random skills to SetAside3
|
||||
---@param cardList table Deck list being created
|
||||
function handleAncestralKnowledge(cardList)
|
||||
local hasAncestralKnowledge = false
|
||||
-- handle cards with attachments (Stick to the Plan, Underworld Market and Bewitching)
|
||||
function handleAllAttachments(cardsToSpawn, slotsCopy, bondedList, customizations, playerColor)
|
||||
for cardId, reservedSlot in pairs(cardsWithAttachments) do
|
||||
if slotsCopy[cardId] and slotsCopy[cardId] > 0 then
|
||||
if customizations["attachments_" .. cardId] and type(reservedSlot) == "number" and reservedSlot < 6 then
|
||||
handleAttachment(cardId, cardsToSpawn, customizations)
|
||||
elseif cardId == "09077" then
|
||||
handleUnderworldMarket(cardsToSpawn, customizations, playerColor)
|
||||
elseif cardId == "05002" then
|
||||
handleHunchDeck(cardsToSpawn, customizations, bondedList, playerColor)
|
||||
elseif cardId == "07303" then
|
||||
handleAncestralKnowledge(cardsToSpawn)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function handleAttachment(parentId, cardsToSpawn, customizations)
|
||||
-- get list of cards that are attached (split by ",")
|
||||
local attachmentList = {}
|
||||
for str in string.gmatch(customizations["attachments_" .. parentId], "([^,]+)") do
|
||||
table.insert(attachmentList, str)
|
||||
end
|
||||
|
||||
-- process cards
|
||||
local zone = "Blank" .. (cardsWithAttachments[parentId] + 1)
|
||||
for i = #attachmentList, 1, -1 do
|
||||
for j = #cardsToSpawn, 1, -1 do
|
||||
if cardsToSpawn[j].metadata.id == attachmentList[i] and cardsToSpawn[j].zone == "Deck" then
|
||||
cardsToSpawn[j].zone = zone
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function handleAncestralKnowledge(cardsToSpawn)
|
||||
local skillList = {}
|
||||
|
||||
-- Have to process the entire list to check for Ancestral Knowledge and get all possible skills, so do both in one pass
|
||||
for i, card in ipairs(cardList) do
|
||||
if card.metadata.id == "07303" then
|
||||
hasAncestralKnowledge = true
|
||||
card.zone = "SetAside3"
|
||||
elseif (card.metadata.type == "Skill"
|
||||
and card.zone == "Deck"
|
||||
and not card.metadata.weakness) then
|
||||
-- process cardlist for skills
|
||||
for i, card in ipairs(cardsToSpawn) do
|
||||
if card.metadata.type == "Skill" and card.zone == "Deck" and not card.metadata.weakness then
|
||||
table.insert(skillList, i)
|
||||
end
|
||||
end
|
||||
|
||||
if not hasAncestralKnowledge then return end
|
||||
|
||||
-- Move 5 random skills to SetAside3
|
||||
-- move 5 random skills to SetAside3
|
||||
for i = 1, 5 do
|
||||
local skillListIndex = math.random(#skillList)
|
||||
cardList[skillList[skillListIndex]].zone = "UnderSetAside3"
|
||||
table.remove(skillList, skillListIndex)
|
||||
cardsToSpawn[skillList[skillListIndex]].zone = "UnderSetAside3"
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for and handle Underworld Market by moving all Illicit cards to UnderSetAside3
|
||||
---@param cardList table Deck list being created
|
||||
-- Extract all Insight events to SetAside5 to build the Hunch Deck for Joe Diamond
|
||||
---@param cardsToSpawn table Deck list being created
|
||||
---@param customizations table ArkhamDB data for customizations on customizable cards
|
||||
---@param playerColor string Color this deck is being loaded for
|
||||
function handleUnderworldMarket(cardList, playerColor)
|
||||
local hasMarket = false
|
||||
local illicitList = {}
|
||||
|
||||
-- Process the entire list to check for Underworld Market and get all possible Illicit cards, doing both in one pass
|
||||
for i, card in ipairs(cardList) do
|
||||
if card.metadata.id == "09077" then
|
||||
hasMarket = true
|
||||
card.zone = "SetAside3"
|
||||
elseif card.metadata.traits ~= nil and string.find(card.metadata.traits, "Illicit", 1, true) and card.zone == "Deck" then
|
||||
table.insert(illicitList, i)
|
||||
function handleHunchDeck(cardsToSpawn, customizations, bondedList, playerColor)
|
||||
local attachmentList = {}
|
||||
if customizations["attachments_05002"] then
|
||||
-- get list of cards that are attached (split by ",")
|
||||
for str in string.gmatch(customizations["attachments_05002"], "([^,]+)") do
|
||||
table.insert(attachmentList, str)
|
||||
end
|
||||
end
|
||||
|
||||
if not hasMarket then return end
|
||||
|
||||
if #illicitList < 10 then
|
||||
printToAll("Only " .. #illicitList .. " Illicit cards in your deck, you can't trigger Underworld Market's ability.", playerColor)
|
||||
else
|
||||
-- Process cards to move them to the market deck. This is done in reverse order because the sorting needs
|
||||
-- to be reversed (deck sorts for face down). Performance here may be an issue, as table.remove() is an O(n)
|
||||
-- operation which makes the full shift O(n^2). But keep it simple unless it becomes a problem
|
||||
for i = #illicitList, 1, -1 do
|
||||
local moving = cardList[illicitList[i]]
|
||||
moving.zone = "UnderSetAside3"
|
||||
table.remove(cardList, illicitList[i])
|
||||
table.insert(cardList, moving)
|
||||
end
|
||||
|
||||
if #illicitList > 10 then
|
||||
printToAll("Moved all " .. #illicitList .. " Illicit cards to the Market deck, reduce it to 10", playerColor)
|
||||
else
|
||||
printToAll("Built the Market deck", playerColor)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If the investigator is Joe Diamond, extract all Insight events to SetAside5 to build the Hunch Deck
|
||||
---@param investigatorId string ID for the deck's investigator card
|
||||
---@param cardList table Deck list being created
|
||||
---@param playerColor string Color this deck is being loaded for
|
||||
function handleHunchDeck(investigatorId, cardList, bondedList, playerColor)
|
||||
if investigatorId ~= "05002" then return end
|
||||
|
||||
local insightList = {}
|
||||
for i, card in ipairs(cardList) do
|
||||
if (card.metadata.type == "Event"
|
||||
-- regular handling, get all possible insight events
|
||||
for i, card in ipairs(cardsToSpawn) do
|
||||
if card.metadata.type == "Event"
|
||||
and card.metadata.traits ~= nil
|
||||
and string.match(card.metadata.traits, "Insight")
|
||||
and bondedList[card.metadata.id] == nil) then
|
||||
table.insert(insightList, i)
|
||||
and bondedList[card.metadata.id] == nil then
|
||||
table.insert(attachmentList, card.metadata.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Process cards to move them to the hunch deck. This is done in reverse order because the sorting needs
|
||||
-- to be reversed (deck sorts for face down). Performance here may be an issue, as table.remove() is an O(n)
|
||||
-- operation which makes the full shift O(n^2). But keep it simple unless it becomes a problem
|
||||
for i = #insightList, 1, -1 do
|
||||
local moving = cardList[insightList[i]]
|
||||
moving.zone = "SetAside5"
|
||||
table.remove(cardList, insightList[i])
|
||||
table.insert(cardList, moving)
|
||||
local count = #attachmentList
|
||||
|
||||
-- change zone for cards
|
||||
for i = count, 1, -1 do
|
||||
for j = #cardsToSpawn, 1, -1 do
|
||||
if cardsToSpawn[j].metadata.id == attachmentList[i] and cardsToSpawn[j].zone == "Deck" then
|
||||
cardsToSpawn[j].zone = "SetAside5"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #insightList < 11 then
|
||||
printToAll("Joe's hunch deck must have 11 cards but the deck only has " .. #insightList .. " Insight events.", playerColor)
|
||||
elseif #insightList > 11 then
|
||||
printToAll("Moved all " .. #insightList .. " Insight events to the hunch deck, reduce it to 11.", playerColor)
|
||||
if count < 11 then
|
||||
printToAll("Joe's hunch deck must have 11 cards but the deck only has " .. count .. " Insight events.", playerColor)
|
||||
elseif count > 11 then
|
||||
printToAll("Moved all " .. count .. " Insight events to the hunch deck, reduce it to 11.", playerColor)
|
||||
else
|
||||
printToAll("Built Joe's hunch deck", playerColor)
|
||||
end
|
||||
end
|
||||
|
||||
-- If the investigator is Parallel Jim Culver, extract all Ally assets to SetAside5 to build the Spirit Deck
|
||||
---@param investigatorId string ID for the deck's investigator card
|
||||
---@param cardList table Deck list being created
|
||||
-- Extract all Ally assets to SetAside5 to build the Spirit Deck for parallel Jim Culver
|
||||
---@param cardsToSpawn table Deck list being created
|
||||
---@param playerColor string Color this deck is being loaded for
|
||||
---@param customizations table Additional deck information
|
||||
function handleSpiritDeck(investigatorId, cardList, playerColor, customizations)
|
||||
if investigatorId ~= "02004-p" and investigatorId ~= "02004-pb" then return end
|
||||
|
||||
---@param customizations table ArkhamDB data for customizations on customizable cards
|
||||
function handleSpiritDeck(cardsToSpawn, playerColor, customizations)
|
||||
local spiritList = {}
|
||||
if customizations["extra_deck"] then
|
||||
-- split by ","
|
||||
for str in string.gmatch(customizations["extra_deck"], "([^,]+)") do
|
||||
local card = allCardsBagApi.getCardById(str)
|
||||
if card ~= nil then
|
||||
table.insert(cardList, { data = card.data, metadata = card.metadata, zone = "SetAside5" })
|
||||
table.insert(cardsToSpawn, { data = card.data, metadata = card.metadata, zone = "SetAside5" })
|
||||
table.insert(spiritList, str)
|
||||
end
|
||||
end
|
||||
else
|
||||
for i, card in ipairs(cardList) do
|
||||
for i, card in ipairs(cardsToSpawn) do
|
||||
if card.metadata.id == "90053" or (
|
||||
card.metadata.type == "Asset"
|
||||
and card.metadata.traits ~= nil
|
||||
@ -627,14 +676,8 @@ function handleSpiritDeck(investigatorId, cardList, playerColor, customizations)
|
||||
end
|
||||
end
|
||||
|
||||
-- Process cards to move them to the spirit deck. This is done in reverse order because the sorting needs
|
||||
-- to be reversed (deck sorts for face down). Performance here may be an issue, as table.remove() is an O(n)
|
||||
-- operation which makes the full shift O(n^2). But keep it simple unless it becomes a problem
|
||||
for i = #spiritList, 1, -1 do
|
||||
local moving = cardList[spiritList[i]]
|
||||
moving.zone = "SetAside5"
|
||||
table.remove(cardList, spiritList[i])
|
||||
table.insert(cardList, moving)
|
||||
cardsToSpawn[spiritList[i]].zone = "SetAside5"
|
||||
end
|
||||
end
|
||||
|
||||
@ -647,6 +690,47 @@ function handleSpiritDeck(investigatorId, cardList, playerColor, customizations)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for and handle Underworld Market by moving all Illicit cards to UnderSetAside6
|
||||
---@param cardsToSpawn table Deck list being created
|
||||
---@param customizations table ArkhamDB data for customizations on customizable cards
|
||||
---@param playerColor string Color this deck is being loaded for
|
||||
function handleUnderworldMarket(cardsToSpawn, customizations, playerColor)
|
||||
local attachmentList = {}
|
||||
if customizations["attachments_09077"] then
|
||||
-- get list of cards that are attached (split by ",")
|
||||
for str in string.gmatch(customizations["attachments_09077"], "([^,]+)") do
|
||||
table.insert(attachmentList, str)
|
||||
end
|
||||
else
|
||||
-- regular handling, get all possible Illicit cards
|
||||
for i, card in ipairs(cardsToSpawn) do
|
||||
if card.metadata.traits ~= nil and string.match(card.metadata.traits, "Illicit") and card.zone == "Deck" then
|
||||
table.insert(attachmentList, card.metadata.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local count = #attachmentList
|
||||
|
||||
-- change zone for cards
|
||||
for i = count, 1, -1 do
|
||||
for j = #cardsToSpawn, 1, -1 do
|
||||
if cardsToSpawn[j].metadata.id == attachmentList[i] and cardsToSpawn[j].zone == "Deck" then
|
||||
cardsToSpawn[j].zone = "UnderSetAside6"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if count < 10 then
|
||||
printToAll("Only " .. count .. " Illicit cards in your deck, you can't trigger Underworld Market's ability.", playerColor)
|
||||
elseif count > 10 then
|
||||
printToAll("Moved all " .. count .. " Illicit cards to the Market deck, reduce it to 10", playerColor)
|
||||
else
|
||||
printToAll("Built the Market deck", playerColor)
|
||||
end
|
||||
end
|
||||
|
||||
-- For any customization upgrade cards in the card list, process the metadata from the deck to
|
||||
-- set the save state to show the correct checkboxes/text field values
|
||||
---@param cardList table Deck list being created
|
||||
@ -762,3 +846,24 @@ function updateStartingResources(playerColor, resourceModifier)
|
||||
printToAll("Modified starting resources", playerColor)
|
||||
end
|
||||
end
|
||||
|
||||
-- internal function to create a copy of a table
|
||||
function deepCopy(data)
|
||||
if type(data) ~= "table" then return data end
|
||||
local copiedList = {}
|
||||
for key, value in pairs(data) do
|
||||
if type(value) == "table" then
|
||||
copiedList[key] = deepCopy(value)
|
||||
else
|
||||
copiedList[key] = value
|
||||
end
|
||||
end
|
||||
return copiedList
|
||||
end
|
||||
|
||||
-- pauses the current coroutine for 'frameCount' frames
|
||||
function coWaitFrames(frameCount)
|
||||
for k = 1, frameCount do
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
|
@ -15,10 +15,10 @@
|
||||
-- SetAside[4-6]: Column farther away from the mat, with 4 at the top and 6 at the bottom.
|
||||
-- SetAside1: Permanent cards
|
||||
-- SetAside2: Bonded cards
|
||||
-- SetAside3: Ancestral Knowledge / Underworld Market
|
||||
-- SetAside3: Ancestral Knowledge
|
||||
-- SetAside4: Upgrade sheets for customizable cards
|
||||
-- SetAside5: Hunch Deck for Joe Diamond
|
||||
-- SetAside6: currently unused
|
||||
-- SetAside5: Hunch Deck for Joe Diamond // Summoned Servitor Minicard
|
||||
-- SetAside6: Underworld Market
|
||||
-- AboveSetAside: Investigator specific object
|
||||
-- BelowSetAside: Investigator specific object
|
||||
|
||||
@ -26,97 +26,59 @@ do
|
||||
local playermatApi = require("playermat/PlayermatApi")
|
||||
local Zones = {}
|
||||
|
||||
-- local coordinates for each playermat
|
||||
local commonZones = {}
|
||||
commonZones["Investigator"] = { -1.177, 0, 0.002 }
|
||||
commonZones["Deck"] = { -1.82, 0, 0 }
|
||||
commonZones["Discard"] = { -1.82, 0, 0.61 }
|
||||
commonZones["Ally"] = { -0.615, 0, 0.024 }
|
||||
commonZones["Body"] = { -0.630, 0, 0.553 }
|
||||
commonZones["Hand1"] = { 0.215, 0, 0.042 }
|
||||
commonZones["Hand2"] = { -0.180, 0, 0.037 }
|
||||
commonZones["Arcane1"] = { 0.212, 0, 0.559 }
|
||||
commonZones["Arcane2"] = { -0.171, 0, 0.557 }
|
||||
commonZones["Tarot"] = { 0.602, 0, 0.033 }
|
||||
commonZones["Accessory"] = { 0.602, 0, 0.555 }
|
||||
commonZones["Blank1"] = { 1.758, 0, 0.040 }
|
||||
commonZones["Blank2"] = { 1.754, 0, 0.563 }
|
||||
commonZones["Blank3"] = { 1.371, 0, 0.038 }
|
||||
commonZones["Blank4"] = { 1.371, 0, 0.558 }
|
||||
commonZones["Blank5"] = { 0.98, 0, 0.035 }
|
||||
commonZones["Blank6"] = { 0.977, 0, 0.556 }
|
||||
commonZones["Threat1"] = { -0.911, 0, -0.625 }
|
||||
commonZones["Threat2"] = { -0.454, 0, -0.625 }
|
||||
commonZones["Threat3"] = { 0.002, 0, -0.625 }
|
||||
commonZones["Threat4"] = { 0.459, 0, -0.625 }
|
||||
commonZones["Investigator"] = Vector(-1.177, 0, 0.002)
|
||||
commonZones["Deck"] = Vector(-1.82, 0, 0)
|
||||
commonZones["Discard"] = Vector(-1.82, 0, 0.61)
|
||||
commonZones["Ally"] = Vector(-0.616, 0, 0.024)
|
||||
commonZones["Body"] = Vector(-0.631, 0, 0.551)
|
||||
commonZones["Hand1"] = Vector(0.217, 0, 0.035)
|
||||
commonZones["Hand2"] = Vector(-0.177, 0, 0.032)
|
||||
commonZones["Arcane1"] = Vector(0.212, 0, 0.559)
|
||||
commonZones["Arcane2"] = Vector(-0.174, 0, 0.551)
|
||||
commonZones["Tarot"] = Vector(0.602, 0, 0.033)
|
||||
commonZones["Accessory"] = Vector(0.605, 0, 0.555)
|
||||
commonZones["Blank1"] = Vector(1.758, 0, 0.040)
|
||||
commonZones["Blank2"] = Vector(1.754, 0, 0.563)
|
||||
commonZones["Blank3"] = Vector(1.371, 0, 0.038)
|
||||
commonZones["Blank4"] = Vector(1.371, 0, 0.558)
|
||||
commonZones["Blank5"] = Vector(0.98, 0, 0.035)
|
||||
commonZones["Blank6"] = Vector(0.977, 0, 0.556)
|
||||
commonZones["Threat1"] = Vector(-0.911, 0, -0.625)
|
||||
commonZones["Threat2"] = Vector(-0.454, 0, -0.625)
|
||||
commonZones["Threat3"] = Vector(0.002, 0, -0.625)
|
||||
commonZones["Threat4"] = Vector(0.459, 0, -0.625)
|
||||
|
||||
-- local coordinates for white / green (inverted X for orange / red)
|
||||
local mirrorZones = {}
|
||||
mirrorZones["Minicard"] = Vector(-1, 0, -1.45)
|
||||
mirrorZones["SetAside1"] = Vector(2.35, 0, -0.520)
|
||||
mirrorZones["SetAside2"] = Vector(2.35, 0, 0.042)
|
||||
mirrorZones["SetAside3"] = Vector(2.35, 0, 0.605)
|
||||
mirrorZones["UnderSetAside3"] = Vector(2.50, 0, 0.805)
|
||||
mirrorZones["SetAside4"] = Vector(2.78, 0, -0.520)
|
||||
mirrorZones["SetAside5"] = Vector(2.78, 0, 0.042)
|
||||
mirrorZones["SetAside6"] = Vector(2.78, 0, 0.605)
|
||||
mirrorZones["UnderSetAside6"] = Vector(2.93, 0, 0.805)
|
||||
mirrorZones["AboveSetAside"] = Vector(2.35, 0, -1.069)
|
||||
mirrorZones["BelowSetAside"] = Vector(2.85, 0, 1.650)
|
||||
|
||||
local zoneData = {}
|
||||
zoneData["White"] = {}
|
||||
zoneData["White"]["Investigator"] = commonZones["Investigator"]
|
||||
zoneData["White"]["Deck"] = commonZones["Deck"]
|
||||
zoneData["White"]["Discard"] = commonZones["Discard"]
|
||||
zoneData["White"]["Ally"] = commonZones["Ally"]
|
||||
zoneData["White"]["Body"] = commonZones["Body"]
|
||||
zoneData["White"]["Hand1"] = commonZones["Hand1"]
|
||||
zoneData["White"]["Hand2"] = commonZones["Hand2"]
|
||||
zoneData["White"]["Arcane1"] = commonZones["Arcane1"]
|
||||
zoneData["White"]["Arcane2"] = commonZones["Arcane2"]
|
||||
zoneData["White"]["Tarot"] = commonZones["Tarot"]
|
||||
zoneData["White"]["Accessory"] = commonZones["Accessory"]
|
||||
zoneData["White"]["Blank1"] = commonZones["Blank1"]
|
||||
zoneData["White"]["Blank2"] = commonZones["Blank2"]
|
||||
zoneData["White"]["Blank3"] = commonZones["Blank3"]
|
||||
zoneData["White"]["Blank4"] = commonZones["Blank4"]
|
||||
zoneData["White"]["Blank5"] = commonZones["Blank5"]
|
||||
zoneData["White"]["Blank6"] = commonZones["Blank6"]
|
||||
zoneData["White"]["Threat1"] = commonZones["Threat1"]
|
||||
zoneData["White"]["Threat2"] = commonZones["Threat2"]
|
||||
zoneData["White"]["Threat3"] = commonZones["Threat3"]
|
||||
zoneData["White"]["Threat4"] = commonZones["Threat4"]
|
||||
zoneData["White"]["Minicard"] = { -1, 0, -1.45 }
|
||||
zoneData["White"]["SetAside1"] = { 2.35, 0, -0.520 }
|
||||
zoneData["White"]["SetAside2"] = { 2.35, 0, 0.042 }
|
||||
zoneData["White"]["SetAside3"] = { 2.35, 0, 0.605 }
|
||||
zoneData["White"]["UnderSetAside3"] = { 2.50, 0, 0.805 }
|
||||
zoneData["White"]["SetAside4"] = { 2.78, 0, -0.520 }
|
||||
zoneData["White"]["SetAside5"] = { 2.78, 0, 0.042 }
|
||||
zoneData["White"]["SetAside6"] = { 2.78, 0, 0.605 }
|
||||
zoneData["White"]["UnderSetAside6"] = { 2.93, 0, 0.805 }
|
||||
zoneData["White"]["AboveSetAside"] = { 2.35, 0, -1.069 }
|
||||
zoneData["White"]["BelowSetAside"] = { 2.85, 0, 1.650 }
|
||||
|
||||
zoneData["Orange"] = {}
|
||||
zoneData["Orange"]["Investigator"] = commonZones["Investigator"]
|
||||
zoneData["Orange"]["Deck"] = commonZones["Deck"]
|
||||
zoneData["Orange"]["Discard"] = commonZones["Discard"]
|
||||
zoneData["Orange"]["Ally"] = commonZones["Ally"]
|
||||
zoneData["Orange"]["Body"] = commonZones["Body"]
|
||||
zoneData["Orange"]["Hand1"] = commonZones["Hand1"]
|
||||
zoneData["Orange"]["Hand2"] = commonZones["Hand2"]
|
||||
zoneData["Orange"]["Arcane1"] = commonZones["Arcane1"]
|
||||
zoneData["Orange"]["Arcane2"] = commonZones["Arcane2"]
|
||||
zoneData["Orange"]["Tarot"] = commonZones["Tarot"]
|
||||
zoneData["Orange"]["Accessory"] = commonZones["Accessory"]
|
||||
zoneData["Orange"]["Blank1"] = commonZones["Blank1"]
|
||||
zoneData["Orange"]["Blank2"] = commonZones["Blank2"]
|
||||
zoneData["Orange"]["Blank3"] = commonZones["Blank3"]
|
||||
zoneData["Orange"]["Blank4"] = commonZones["Blank4"]
|
||||
zoneData["Orange"]["Blank5"] = commonZones["Blank5"]
|
||||
zoneData["Orange"]["Blank6"] = commonZones["Blank6"]
|
||||
zoneData["Orange"]["Threat1"] = commonZones["Threat1"]
|
||||
zoneData["Orange"]["Threat2"] = commonZones["Threat2"]
|
||||
zoneData["Orange"]["Threat3"] = commonZones["Threat3"]
|
||||
zoneData["Orange"]["Threat4"] = commonZones["Threat4"]
|
||||
zoneData["Orange"]["Minicard"] = { 1, 0, -1.45 }
|
||||
zoneData["Orange"]["SetAside1"] = { -2.35, 0, -0.520 }
|
||||
zoneData["Orange"]["SetAside2"] = { -2.35, 0, 0.042}
|
||||
zoneData["Orange"]["SetAside3"] = { -2.35, 0, 0.605 }
|
||||
zoneData["Orange"]["UnderSetAside3"] = { -2.50, 0, 0.805 }
|
||||
zoneData["Orange"]["SetAside4"] = { -2.78, 0, -0.520 }
|
||||
zoneData["Orange"]["SetAside5"] = { -2.78, 0, 0.042 }
|
||||
zoneData["Orange"]["SetAside6"] = { -2.78, 0, 0.605 }
|
||||
zoneData["Orange"]["UnderSetAside6"] = { -2.93, 0, 0.805 }
|
||||
zoneData["Orange"]["AboveSetAside"] = { -2.35, 0, -1.069 }
|
||||
zoneData["Orange"]["BelowSetAside"] = { -2.85, 0, 1.650 }
|
||||
|
||||
-- copy common coordinates
|
||||
for zoneName, zonePos in pairs(commonZones) do
|
||||
zoneData["White"][zoneName] = zonePos
|
||||
zoneData["Orange"][zoneName] = zonePos
|
||||
end
|
||||
|
||||
-- copy mirrored coordinates
|
||||
for zoneName, zonePos in pairs(mirrorZones) do
|
||||
zoneData["White"][zoneName] = zonePos
|
||||
zoneData["Orange"][zoneName] = zonePos * Vector(-1, 1, 1)
|
||||
end
|
||||
|
||||
-- Green positions are the same as White and Red the same as Orange
|
||||
zoneData["Red"] = zoneData["Orange"]
|
||||
@ -127,14 +89,11 @@ do
|
||||
---@param zoneName string Name of the zone to get the position for. See Zones object documentation for a list of valid zones.
|
||||
---@return tts__Vector|nil: Global position table, or nil if an invalid player color or zone is specified
|
||||
Zones.getZonePosition = function(playerColor, zoneName)
|
||||
if (playerColor ~= "Red"
|
||||
and playerColor ~= "Orange"
|
||||
and playerColor ~= "White"
|
||||
and playerColor ~= "Green") then
|
||||
return nil
|
||||
end
|
||||
if zoneData[playerColor] then
|
||||
return playermatApi.transformLocalPosition(zoneData[playerColor][zoneName], playerColor)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Return the global rotation for a card on the given player mat, based on its zone.
|
||||
---@param playerColor string Color name of the player mat to get the rotation for (e.g. "Red")
|
||||
@ -144,8 +103,8 @@ do
|
||||
-- Z rotation to place the card face up or face down.
|
||||
Zones.getDefaultCardRotation = function(playerColor, zoneName)
|
||||
local cardRotation = playermatApi.returnRotation(playerColor)
|
||||
if zoneName == "Deck" then
|
||||
cardRotation = cardRotation + Vector(0, 0, 180)
|
||||
if zoneName == "Deck" or zoneName == "UnderSetAside3" or zoneName == "UnderSetAside6" then
|
||||
cardRotation = cardRotation:setAt("z", 180)
|
||||
end
|
||||
return cardRotation
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user