updated clue spawning

This commit is contained in:
Chr1Z93 2024-07-15 01:15:06 +02:00
parent 843d6d4e10
commit 8bc7ea9f94
4 changed files with 122 additions and 53 deletions

View File

@ -464,7 +464,7 @@ end
-- Count victory points from locations in play area -- Count victory points from locations in play area
---@param highlightOff boolean True if highlighting should be enabled ---@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) function countVP(highlightOff)
local totalVP = 0 local totalVP = 0

View File

@ -299,9 +299,10 @@ do
---@param card tts__Object Card object to be replenished ---@param card tts__Object Card object to be replenished
---@param uses table The already decoded metadata.uses (to avoid decoding again) ---@param uses table The already decoded metadata.uses (to avoid decoding again)
TokenManager.maybeReplenishCard = function(card, uses) TokenManager.maybeReplenishCard = function(card, uses)
-- TODO: support for cards with multiple uses AND replenish (as of yet, no official card needs that) for _, useInfo in ipairs(uses) do
if uses[1].count and uses[1].replenish then if useInfo.count and useInfo.replenish then
internal.replenishTokens(card, uses) internal.replenishTokens(card, useInfo)
end
end end
end end
@ -459,59 +460,76 @@ do
return nil return nil
end 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 card tts__Object Card the clues will be placed on
---@param count number How many clues? ---@param count number How many clues?
---@return table: Array of global positions to spawn the clues at ---@return table: Array of global positions to spawn the clues at
internal.buildClueOffsets = function(card, count) 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 = {} local cluePositions = {}
for i = 1, count do for i = 1, count do
local row = math.floor(1 + (i - 1) / 4) local row = math.floor(1 + (i - 1) / 4)
local column = (i - 1) % 4 local column = (i - 1) % 4
local cluePos = card.positionToWorld(Vector(-0.825 + 0.55 * column, 0, -1.5 + 0.55 * row)) local localPos = Vector((-0.825 + 0.55 * column) * modifier, 0, -1.5 + 0.55 * row)
cluePos.y = cluePos.y + 0.05 local cluePos = card.positionToWorld(localPos) + Vector(0, 0.03, 0)
table.insert(cluePositions, cluePos) table.insert(cluePositions, cluePos)
end end
return cluePositions return cluePositions
end end
---@param card tts__Object Card object to be replenished ---@param card tts__Object Card object to be replenished
---@param uses table The already decoded metadata.uses (to avoid decoding again) ---@param useInfo table The already decoded subtable of metadata.uses (to avoid decoding again)
internal.replenishTokens = function(card, uses) internal.replenishTokens = function(card, useInfo)
-- get current amount of matching resource tokens on the card -- get current amount of matching resource tokens on the card
local clickableResourceCounter = nil local clickableResourceCounter = nil
local foundTokens = 0 local foundTokens = 0
local searchType = string.lower(uses[1].type)
for _, obj in ipairs(searchLib.onObject(card, "isTileOrToken")) do if useInfo.token == "clue" then
local memo = obj.getMemo() for _, obj in ipairs(searchLib.onObject(card, "isClue")) do
if searchType == memo then
foundTokens = foundTokens + math.abs(obj.getQuantity()) foundTokens = foundTokens + math.abs(obj.getQuantity())
obj.destruct() obj.destruct()
elseif memo == "resourceCounter" then end
foundTokens = obj.getVar("val") elseif useInfo.token == "doom" then
clickableResourceCounter = obj for _, obj in ipairs(searchLib.onObject(card, "isDoom")) do
break 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
end end
-- this is the theoretical new amount of uses (to be checked below) -- 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 there are already more uses than the replenish amount, keep them
if foundTokens > uses[1].count then if foundTokens > useInfo.count then
newCount = foundTokens newCount = foundTokens
-- only replenish up until the replenish amount -- only replenish up until the replenish amount
elseif newCount > uses[1].count then elseif newCount > useInfo.count then
newCount = uses[1].count newCount = useInfo.count
end end
-- update the clickable counter or spawn a group of tokens -- update the clickable counter or spawn a group of tokens
if clickableResourceCounter then if clickableResourceCounter then
clickableResourceCounter.call("updateVal", newCount) clickableResourceCounter.call("updateVal", newCount)
else else
TokenManager.spawnTokenGroup(card, uses[1].token, newCount, _, uses[1].type) TokenManager.spawnTokenGroup(card, useInfo.token, newCount, _, useInfo.type)
end end
end end

View File

@ -5,6 +5,7 @@ do
isDeck = function(x) return x.type == "Deck" end, isDeck = function(x) return x.type == "Deck" end,
isCardOrDeck = function(x) return x.type == "Card" or 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, 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, isTileOrToken = function(x) return x.type == "Tile" end,
isUniversalToken = function(x) return x.getMemo() == "universalActionAbility" end, isUniversalToken = function(x) return x.getMemo() == "universalActionAbility" end,
} }

View File

@ -1,4 +1,3 @@
local guidReferenceApi = require("core/GUIDReferenceApi")
local playermatApi = require("playermat/PlayermatApi") local playermatApi = require("playermat/PlayermatApi")
local searchLib = require("util/SearchLib") local searchLib = require("util/SearchLib")
local tokenManager = require("core/token/TokenManager") local tokenManager = require("core/token/TokenManager")
@ -24,35 +23,23 @@ function onScriptingButtonDown(index, playerColor)
-- check for subtype of resource based on card below -- check for subtype of resource based on card below
if tokenType == "resource" then if tokenType == "resource" then
local card local card = getTargetCard(playerColor, position)
local hoverObj = Player[playerColor].getHoverObject()
if hoverObj and hoverObj.type == "Card" then if card and not card.is_face_down then
card = hoverObj local status = addUseToCard(card, tokenType)
elseif hoverObj then if status == true then return end
-- 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
end end
-- get the metadata from the card and maybe replenish a use -- check hovered object for location data or 'uses (x clues)' and add one
if card and not card.is_face_down then elseif tokenType == "clue" then
local metadata = JSON.decode(card.getGMNotes()) or {} local card = getTargetCard(playerColor, position)
local uses = metadata.uses or {}
for _, useInfo in ipairs(uses) do if card and (not card.is_face_down or card.hasTag("Location")) then
if useInfo.token == "resource" then local status = addUseToCard(card, tokenType)
-- artifically create replenish data to re-use that existing functionality if status == true then return end
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
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 elseif tokenType == "resourceCounter" then
local hoverObj = Player[playerColor].getHoverObject() local hoverObj = Player[playerColor].getHoverObject()
if hoverObj then if hoverObj then
@ -61,7 +48,8 @@ function onScriptingButtonDown(index, playerColor)
return return
end end
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 elseif tokenType == "damage" or tokenType == "horror" then
local hoverObj = Player[playerColor].getHoverObject() local hoverObj = Player[playerColor].getHoverObject()
if hoverObj then if hoverObj then
@ -74,12 +62,74 @@ function onScriptingButtonDown(index, playerColor)
end end
end 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 elseif tokenType == "universalActionAbility" then
local matColor = playermatApi.getMatColorByPosition(position) local matColor = playermatApi.getMatColorByPosition(position)
local matRotation = playermatApi.returnRotation(matColor)
local class = playermatApi.returnInvestigatorClass(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 end
tokenManager.spawnToken(position, tokenType, rotation, callback) tokenManager.spawnToken(position, tokenType, rotation, callback)
end 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