From 8bc7ea9f94ba418e6b51acc16bf04d4f16ac72b4 Mon Sep 17 00:00:00 2001 From: Chr1Z93 Date: Mon, 15 Jul 2024 01:15:06 +0200 Subject: [PATCH] updated clue spawning --- src/core/PlayArea.ttslua | 2 +- src/core/token/TokenManager.ttslua | 62 ++++++++++------ src/util/SearchLib.ttslua | 1 + src/util/TokenSpawnTool.ttslua | 110 +++++++++++++++++++++-------- 4 files changed, 122 insertions(+), 53 deletions(-) diff --git a/src/core/PlayArea.ttslua b/src/core/PlayArea.ttslua index 22447b3f..c5319986 100644 --- a/src/core/PlayArea.ttslua +++ b/src/core/PlayArea.ttslua @@ -464,7 +464,7 @@ end -- Count victory points from locations in play area ---@param highlightOff boolean True if highlighting should be enabled ----@return. Returns the total amount of VP found in the play area +---@return number totalVP Total amount of VP found in the play area function countVP(highlightOff) local totalVP = 0 diff --git a/src/core/token/TokenManager.ttslua b/src/core/token/TokenManager.ttslua index afc89c32..66d783c0 100644 --- a/src/core/token/TokenManager.ttslua +++ b/src/core/token/TokenManager.ttslua @@ -299,9 +299,10 @@ do ---@param card tts__Object Card object to be replenished ---@param uses table The already decoded metadata.uses (to avoid decoding again) TokenManager.maybeReplenishCard = function(card, uses) - -- TODO: support for cards with multiple uses AND replenish (as of yet, no official card needs that) - if uses[1].count and uses[1].replenish then - internal.replenishTokens(card, uses) + for _, useInfo in ipairs(uses) do + if useInfo.count and useInfo.replenish then + internal.replenishTokens(card, useInfo) + end end end @@ -459,59 +460,76 @@ do return nil end - -- Dynamically create positions for clues on a card. + -- Dynamically create positions for clues on a card ---@param card tts__Object Card the clues will be placed on ---@param count number How many clues? ---@return table: Array of global positions to spawn the clues at internal.buildClueOffsets = function(card, count) + -- make sure clues always spawn from left to right + local modifier = -1 + if card.is_face_down then + modifier = 1 + end + local cluePositions = {} for i = 1, count do local row = math.floor(1 + (i - 1) / 4) local column = (i - 1) % 4 - local cluePos = card.positionToWorld(Vector(-0.825 + 0.55 * column, 0, -1.5 + 0.55 * row)) - cluePos.y = cluePos.y + 0.05 + local localPos = Vector((-0.825 + 0.55 * column) * modifier, 0, -1.5 + 0.55 * row) + local cluePos = card.positionToWorld(localPos) + Vector(0, 0.03, 0) table.insert(cluePositions, cluePos) end return cluePositions end ---@param card tts__Object Card object to be replenished - ---@param uses table The already decoded metadata.uses (to avoid decoding again) - internal.replenishTokens = function(card, uses) + ---@param useInfo table The already decoded subtable of metadata.uses (to avoid decoding again) + internal.replenishTokens = function(card, useInfo) -- get current amount of matching resource tokens on the card local clickableResourceCounter = nil local foundTokens = 0 - local searchType = string.lower(uses[1].type) - for _, obj in ipairs(searchLib.onObject(card, "isTileOrToken")) do - local memo = obj.getMemo() - - if searchType == memo then + if useInfo.token == "clue" then + for _, obj in ipairs(searchLib.onObject(card, "isClue")) do foundTokens = foundTokens + math.abs(obj.getQuantity()) obj.destruct() - elseif memo == "resourceCounter" then - foundTokens = obj.getVar("val") - clickableResourceCounter = obj - break + end + elseif useInfo.token == "doom" then + for _, obj in ipairs(searchLib.onObject(card, "isDoom")) do + foundTokens = foundTokens + math.abs(obj.getQuantity()) + obj.destruct() + end + else + local searchType = string.lower(useInfo.type) + for _, obj in ipairs(searchLib.onObject(card, "isTileOrToken")) do + local memo = obj.getMemo() + if searchType == memo then + foundTokens = foundTokens + math.abs(obj.getQuantity()) + obj.destruct() + elseif memo == "resourceCounter" then + foundTokens = obj.getVar("val") + clickableResourceCounter = obj + break + end end end -- this is the theoretical new amount of uses (to be checked below) - local newCount = foundTokens + uses[1].replenish + local newCount = foundTokens + useInfo.replenish -- if there are already more uses than the replenish amount, keep them - if foundTokens > uses[1].count then + if foundTokens > useInfo.count then newCount = foundTokens -- only replenish up until the replenish amount - elseif newCount > uses[1].count then - newCount = uses[1].count + elseif newCount > useInfo.count then + newCount = useInfo.count end -- update the clickable counter or spawn a group of tokens if clickableResourceCounter then clickableResourceCounter.call("updateVal", newCount) else - TokenManager.spawnTokenGroup(card, uses[1].token, newCount, _, uses[1].type) + TokenManager.spawnTokenGroup(card, useInfo.token, newCount, _, useInfo.type) end end diff --git a/src/util/SearchLib.ttslua b/src/util/SearchLib.ttslua index 7d5d5fa7..ad5da1b2 100644 --- a/src/util/SearchLib.ttslua +++ b/src/util/SearchLib.ttslua @@ -5,6 +5,7 @@ do isDeck = function(x) return x.type == "Deck" end, isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end, isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end, + isDoom = function(x) return x.memo == "clueDoom" and x.is_face_down == true end, isTileOrToken = function(x) return x.type == "Tile" end, isUniversalToken = function(x) return x.getMemo() == "universalActionAbility" end, } diff --git a/src/util/TokenSpawnTool.ttslua b/src/util/TokenSpawnTool.ttslua index 650d4dbc..d6be72f4 100644 --- a/src/util/TokenSpawnTool.ttslua +++ b/src/util/TokenSpawnTool.ttslua @@ -1,4 +1,3 @@ -local guidReferenceApi = require("core/GUIDReferenceApi") local playermatApi = require("playermat/PlayermatApi") local searchLib = require("util/SearchLib") local tokenManager = require("core/token/TokenManager") @@ -24,35 +23,23 @@ function onScriptingButtonDown(index, playerColor) -- check for subtype of resource based on card below if tokenType == "resource" then - local card - local hoverObj = Player[playerColor].getHoverObject() - if hoverObj and hoverObj.type == "Card" then - card = hoverObj - elseif hoverObj then - -- use the first card below the hovered object if it's not a card1 - for _, obj in ipairs(searchLib.belowPosition(position, "isCard")) do - card = obj - break - end + local card = getTargetCard(playerColor, position) + + if card and not card.is_face_down then + local status = addUseToCard(card, tokenType) + if status == true then return end end - -- get the metadata from the card and maybe replenish a use - if card and not card.is_face_down then - local metadata = JSON.decode(card.getGMNotes()) or {} - local uses = metadata.uses or {} - for _, useInfo in ipairs(uses) do - if useInfo.token == "resource" then - -- artifically create replenish data to re-use that existing functionality - uses[1].count = 999 - uses[1].replenish = 1 - local matColor = playermatApi.getMatColorByPosition(position) - local mat = guidReferenceApi.getObjectByOwnerAndType(matColor, "Playermat") - tokenManager.maybeReplenishCard(card, uses, mat) - return - end - end + -- check hovered object for location data or 'uses (x clues)' and add one + elseif tokenType == "clue" then + local card = getTargetCard(playerColor, position) + + if card and (not card.is_face_down or card.hasTag("Location")) then + local status = addUseToCard(card, tokenType) + if status == true then return end end - -- check hovered object for "resourceCounter" tokens and increase them instead + + -- check hovered object for "resourceCounter" tokens and increase them instead elseif tokenType == "resourceCounter" then local hoverObj = Player[playerColor].getHoverObject() if hoverObj then @@ -61,7 +48,8 @@ function onScriptingButtonDown(index, playerColor) return end end - -- check hovered object for "damage" and "horror" tokens and increase them instead + + -- check hovered object for "damage" and "horror" tokens and increase them instead elseif tokenType == "damage" or tokenType == "horror" then local hoverObj = Player[playerColor].getHoverObject() if hoverObj then @@ -74,12 +62,74 @@ function onScriptingButtonDown(index, playerColor) end end end - -- check for nearest investigator card and change action token state to its class + + -- check for nearest investigator card and change action token state to its class elseif tokenType == "universalActionAbility" then local matColor = playermatApi.getMatColorByPosition(position) + local matRotation = playermatApi.returnRotation(matColor) local class = playermatApi.returnInvestigatorClass(matColor) - callback = function(spawned) spawned.call("updateClassAndSymbol", { class = class, symbol = class }) end + callback = function(spawned) + spawned.setRotation(matRotation) + spawned.call("updateClassAndSymbol", { class = class, symbol = class }) + end end tokenManager.spawnToken(position, tokenType, rotation, callback) end + +-- gets the target card for this operation +---@param playerColor string Color of the triggering player +---@param position tts__Vector Position to check for a card (if there isn't a hovered card) +function getTargetCard(playerColor, position) + local hoverObj = Player[playerColor].getHoverObject() + if hoverObj and hoverObj.type == "Card" then + return hoverObj + elseif hoverObj then + -- use the first card below the hovered object if it's not a card + for _, obj in ipairs(searchLib.belowPosition(position, "isCard")) do + return obj + end + end +end + +-- adds a use to a card (TODO: probably move this to the TokenManager?) +---@param card tts__Object Card that should get a use added +---@param useType string Type of uses to be added +function addUseToCard(card, useType) + local metadata = JSON.decode(card.getGMNotes()) or {} + + -- get correct data for location + if metadata.type == "Location" then + if not card.is_face_down and metadata.locationFront ~= nil then + metadata = metadata.locationFront + elseif metadata.locationBack ~= nil then + metadata = metadata.locationBack + end + + -- if there are no uses at all, add "empty" uses for fake replenishing (only for clues) + if metadata.uses == nil then + metadata.uses = { { token = "clue" } } + end + end + + local match = false + for _, useInfo in ipairs(metadata.uses) do + if useInfo.token == useType then + -- artificially create replenish data to re-use that existing functionality + useInfo.count = 999 + useInfo.replenish = 1 + match = true + else + -- artificially disable other uses from replenishing + useInfo.replenish = nil + end + end + + -- if matching uses were found, perform the "fake" replenish + if match then + tokenManager.maybeReplenishCard(card, metadata.uses) + return true + else + return false + end +end