Merge pull request #742 from dscarpac/token-stack

Adds XML label to stacks of tokens
This commit is contained in:
Chr1Z 2024-07-08 01:10:38 +02:00 committed by GitHub
commit bf4fcd6051
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 155 additions and 41 deletions

View File

@ -47,8 +47,9 @@ do
-- returns a chaos token to the bag and calls all relevant functions -- returns a chaos token to the bag and calls all relevant functions
---@param token tts__Object Chaos token to return ---@param token tts__Object Chaos token to return
ChaosBagApi.returnChaosTokenToBag = function(token) ---@param fromBag boolean whether or not the token to return was in the middle of being drawn (true) or elsewhere (false)
Global.call("returnChaosTokenToBag", token) ChaosBagApi.returnChaosTokenToBag = function(token, fromBag)
Global.call("returnChaosTokenToBag", { token = token, fromBag = fromBag })
end end
-- spawns the specified chaos token and puts it into the chaos bag -- spawns the specified chaos token and puts it into the chaos bag

View File

@ -5,6 +5,7 @@ local navigationOverlayApi = require("core/NavigationOverlayApi")
local optionPanelApi = require("core/OptionPanelApi") local optionPanelApi = require("core/OptionPanelApi")
local playermatApi = require("playermat/PlayermatApi") local playermatApi = require("playermat/PlayermatApi")
local searchLib = require("util/SearchLib") local searchLib = require("util/SearchLib")
local tokenChecker = require("core/token/TokenChecker")
local victoryDisplayApi = require("core/VictoryDisplayApi") local victoryDisplayApi = require("core/VictoryDisplayApi")
function onLoad() function onLoad()
@ -265,7 +266,7 @@ function removeOneUse(playerColor, hoveredObject)
for _, obj in ipairs(searchLib.onObject(hoveredObject, "isTileOrToken")) do for _, obj in ipairs(searchLib.onObject(hoveredObject, "isTileOrToken")) do
if not obj.locked and obj.memo ~= "resourceCounter" then if not obj.locked and obj.memo ~= "resourceCounter" then
-- check for matching object, otherwise use the first hit -- check for matching object, otherwise use the first hit
if obj.memo == searchForType then if obj.memo and obj.memo == searchForType then
targetObject = obj targetObject = obj
break break
elseif not targetObject then elseif not targetObject then
@ -275,6 +276,15 @@ function removeOneUse(playerColor, hoveredObject)
end end
end end
-- release sealed token if card has one and no uses
if tokenChecker.isChaosToken(targetObject) and hoveredObject.hasTag("CardThatSeals") then
local func = hoveredObject.getVar("releaseOneToken") -- check if function exists
if func ~= nil then
hoveredObject.call("releaseOneToken", playerColor)
return
end
end
-- error handling -- error handling
if not targetObject then if not targetObject then
broadcastToColor("No tokens found!", playerColor, "Yellow") broadcastToColor("No tokens found!", playerColor, "Yellow")

View File

@ -207,6 +207,12 @@ function onObjectEnterZone(zone, object)
object.clearContextMenu() object.clearContextMenu()
object.call("shutOff") object.call("shutOff")
end end
if object.hasTag("CardThatSeals") then
local func = object.getVar("resetSealedTokens") -- check if function exists (it won't for older custom content)
if func ~= nil then
object.call("resetSealedTokens")
end
end
end end
end end
@ -301,13 +307,13 @@ function returnChaosTokens()
end end
-- returns a single chaos token to the bag and calls respective functions -- returns a single chaos token to the bag and calls respective functions
function returnChaosTokenToBag(token) function returnChaosTokenToBag(params)
local name = token.getName() local name = params.token.getName()
local chaosBag = findChaosBag() local chaosBag = findChaosBag()
chaosBag.putObject(token) chaosBag.putObject(params.token)
tokenArrangerApi.layout() tokenArrangerApi.layout()
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.releasedToken(name, token.getGUID(), true) blessCurseManagerApi.releasedToken(name, params.token.getGUID(), params.fromBag)
end end
end end
@ -418,7 +424,8 @@ function returnAndRedraw(_, tokenGUID)
-- perform the actual token replacing -- perform the actual token replacing
trackChaosToken(tokenName, mat.getGUID(), true) trackChaosToken(tokenName, mat.getGUID(), true)
returnChaosTokenToBag(returnedToken) local params = {token = returnedToken, fromBag = true}
returnChaosTokenToBag(params)
chaosTokens[indexOfReturnedToken] = drawChaosToken({ chaosTokens[indexOfReturnedToken] = drawChaosToken({
mat = mat, mat = mat,

View File

@ -1,7 +1,16 @@
--[[ Library for cards that seal tokens --[[ Library for cards that seal tokens
This file is used to add sealing option to cards' context menu. This file is used to add sealing option to cards' context menu.
NOTE: all cards are allowed to release a single token to enable Hallow and A Watchful Peace,
and to release all sealed tokens to allow for cards that might leave play with sealed tokens on them.
Valid options (set before requiring this file): Valid options (set before requiring this file):
MAX_SEALED --@type: number (maximum number of tokens allowable by the card to be sealed)
- required for all cards
- if MAX_SEALED is more than 1, then an XML label is created for the topmost token indicating the number of sealed tokens
- gives an error if user tries to seal additional tokens on the card
- example usage: "The Chthonian Stone"
> MAX_SEALED = 1
UPDATE_ON_HOVER --@type: boolean UPDATE_ON_HOVER --@type: boolean
- automatically updates the context menu options when the card is hovered - automatically updates the context menu options when the card is hovered
- the "Read Bag" function reads the content of the chaos bag to update the context menu - the "Read Bag" function reads the content of the chaos bag to update the context menu
@ -12,19 +21,16 @@ KEEP_OPEN --@type: boolean
- makes the context menu stay open after selecting an option - makes the context menu stay open after selecting an option
- example usage: "Unrelenting" - example usage: "Unrelenting"
SHOW_SINGLE_RELEASE --@type: boolean SHOW_MULTI_RELEASE --@type: number (maximum amount of tokens to release at once)
- enables an entry in the context menu - enables an entry in the context menu
- this entry allows releasing a single token - this entry allows releasing of multiple tokens at once, to the maximum number
- example usage: "Holy Spear" (to keep the other tokens and just release one) - does not fail if there are fewer than the maximum sealed
- example usage: "Nephthys" (to release up to 3 bless tokens at once)
SHOW_MULTI_RELEASE --@type: number (amount of tokens to release at once)
- enables an entry in the context menu
- this entry allows releasing of multiple tokens at once
- example usage: "Nephthys" (to release 3 bless tokens at once)
SHOW_MULTI_RETURN --@type: number (amount of tokens to return to pool at once) SHOW_MULTI_RETURN --@type: number (amount of tokens to return to pool at once)
- enables an entry in the context menu - enables an entry in the context menu
- this entry allows returning tokens to the token pool - this entry allows returning tokens to the token pool
- fails if not enough tokens are sealed
- example usage: "Nephthys" (to return 3 bless tokens at once) - example usage: "Nephthys" (to return 3 bless tokens at once)
SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once) SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once)
@ -58,6 +64,7 @@ Thus it should be implemented like this:
> ["+1"] = true, > ["+1"] = true,
> ["Elder Sign"] = true > ["Elder Sign"] = true
> } > }
> MAX_SEALED = 1
> require... > require...
---------------------------------------------------------- ----------------------------------------------------------
Example 2: Holy Spear Example 2: Holy Spear
@ -68,8 +75,8 @@ Thus it should be implemented like this:
> VALID_TOKENS = { > VALID_TOKENS = {
> ["Bless"] = true > ["Bless"] = true
> } > }
> SHOW_SINGLE_RELEASE = true
> SHOW_MULTI_SEAL = 2 > SHOW_MULTI_SEAL = 2
> MAX_SEALED = 10
> require... > require...
----------------------------------------------------------]] ----------------------------------------------------------]]
@ -83,6 +90,20 @@ local sealedTokens = {}
local ID_URL_MAP = {} local ID_URL_MAP = {}
local tokensInBag = {} local tokensInBag = {}
-- XML background color for each token for label when stacked
local tokenColor = {
["Skull"] = "#4A0400E6",
["Cultist"] = "#173B0BE6",
["Tablet"] = "#1D2238E6",
["Elder Thing"] = "#4D2331E6",
["Auto-fail"] = "#9B0004E6",
["Bless"] = "#9D702CE6",
["Curse"] = "#633A84E6",
["Frost"] = "#404450E6",
["Elder Sign"] = "#50A8CEE6",
[""] = "#77674DE6"
}
function onSave() return JSON.encode(sealedTokens) end function onSave() return JSON.encode(sealedTokens) end
function onLoad(savedData) function onLoad(savedData)
@ -94,13 +115,15 @@ end
-- builds the context menu -- builds the context menu
function generateContextMenu() function generateContextMenu()
-- conditional single or multi release options self.addContextMenuItem("Release one token", releaseOneToken)
if SHOW_SINGLE_RELEASE then
self.addContextMenuItem("Release token", releaseOneToken) -- conditional release options
elseif SHOW_MULTI_RELEASE then if MAX_SEALED > 1 then
self.addContextMenuItem("Release all tokens", releaseAllTokens)
end
if SHOW_MULTI_RELEASE then
self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens) self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens)
else
self.addContextMenuItem("Release token(s)", releaseAllTokens)
end end
if RESOLVE_TOKEN then if RESOLVE_TOKEN then
@ -138,7 +161,7 @@ function generateContextMenu()
end end
if allowed then if allowed then
for i = 1, SHOW_MULTI_SEAL do for i = SHOW_MULTI_SEAL, 1, -1 do
sealToken(map.name, playerColor) sealToken(map.name, playerColor)
end end
else else
@ -175,6 +198,10 @@ end
-- seals the named token on this card -- seals the named token on this card
function sealToken(name, playerColor) function sealToken(name, playerColor)
if #sealedTokens >= MAX_SEALED then
printToColor("Cannot seal any more tokens on this card", playerColor, "Red")
return
end
if not chaosBagApi.canTouchChaosTokens() then return end if not chaosBagApi.canTouchChaosTokens() then return end
local chaosbag = chaosBagApi.findChaosBag() local chaosbag = chaosBagApi.findChaosBag()
for i, obj in ipairs(chaosbag.getObjects()) do for i, obj in ipairs(chaosbag.getObjects()) do
@ -191,6 +218,16 @@ function sealToken(name, playerColor)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.sealedToken(name, guid) blessCurseManagerApi.sealedToken(name, guid)
end end
-- destroy XML on just covered token
if #sealedTokens > 1 then
local coveredToken = getObjectFromGUID(sealedTokens[#sealedTokens - 1])
if coveredToken ~= nil then
coveredToken.UI.setXml("")
else
table.remove(sealedTokens, #sealedTokens - 1)
end
end
updateStackSize()
end end
}) })
return return
@ -210,16 +247,22 @@ function releaseOneToken(playerColor)
end end
end end
-- release multiple tokens at once -- release up to multiple tokens at once with no minimum
function releaseMultipleTokens(playerColor) function releaseMultipleTokens(playerColor)
if SHOW_MULTI_RELEASE <= #sealedTokens then if #sealedTokens == 0 then
for i = 1, SHOW_MULTI_RELEASE do
putTokenAway(table.remove(sealedTokens))
end
printToColor("Releasing " .. SHOW_MULTI_RELEASE .. " tokens", playerColor)
else
printToColor("Not enough tokens sealed.", playerColor) printToColor("Not enough tokens sealed.", playerColor)
return
end end
local numRemoved = SHOW_MULTI_RELEASE
if #sealedTokens < SHOW_MULTI_RELEASE then
numRemoved = #sealedTokens
end
for i = 1, numRemoved do
putTokenAway(table.remove(sealedTokens))
end
printToColor("Releasing " .. numRemoved .. " tokens", playerColor)
end end
-- releases all sealed tokens -- releases all sealed tokens
@ -260,6 +303,7 @@ function putTokenAway(guid)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.releasedToken(name, guid) blessCurseManagerApi.releasedToken(name, guid)
end end
updateStackSize()
end end
-- returns the token to the pool (== removes it) -- returns the token to the pool (== removes it)
@ -272,6 +316,7 @@ function returnToken(guid)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.returnedToken(name, guid) blessCurseManagerApi.returnedToken(name, guid)
end end
updateStackSize()
end end
-- resolves sealed token as if it came from the chaos bag -- resolves sealed token as if it came from the chaos bag
@ -283,5 +328,41 @@ function resolveSealed()
local closestMatColor = playermatApi.getMatColorByPosition(self.getPosition()) local closestMatColor = playermatApi.getMatColorByPosition(self.getPosition())
local mat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat") local mat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat")
local guidToBeResolved = table.remove(sealedTokens) local guidToBeResolved = table.remove(sealedTokens)
local resolvedToken = getObjectFromGUID(guidToBeResolved)
resolvedToken.UI.setXml("")
updateStackSize()
chaosBagApi.drawChaosToken(mat, true, _, guidToBeResolved) chaosBagApi.drawChaosToken(mat, true, _, guidToBeResolved)
end end
function updateStackSize()
if MAX_SEALED == 1 then return end
if #sealedTokens == 0 then return end
-- get topmost sealed token
local topToken = getObjectFromGUID(sealedTokens[#sealedTokens])
local name = topToken.getName()
topToken.UI.setXmlTable({
{
tag = "Panel",
attributes = {
height = 380,
width = 380,
rotation = "0 0 180",
scale = "0.2 0.2 1",
position = "0 0 -12",
color = tokenColor[name] or "#77674DE6"
},
children = {
tag = "Text",
attributes = {
fontSize = "380",
font = "font_teutonic-arkham",
color = "#ffffff",
outline = "#000000",
outlineSize = "8 -8",
text = "x" .. #sealedTokens
}
}
}
})
end

View File

@ -2,5 +2,6 @@ VALID_TOKENS = {
["+1"] = true, ["+1"] = true,
["Elder Sign"] = true ["Elder Sign"] = true
} }
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -3,5 +3,6 @@ VALID_TOKENS = {
} }
KEEP_OPEN = true KEEP_OPEN = true
MAX_SEALED = 5
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,4 +2,6 @@ VALID_TOKENS = {
["Elder Sign"] = true ["Elder Sign"] = true
} }
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,8 +2,8 @@ VALID_TOKENS = {
["Curse"] = true ["Curse"] = true
} }
SHOW_SINGLE_RELEASE = true
KEEP_OPEN = true KEEP_OPEN = true
MAX_SEALED = 3
RESOLVE_TOKEN = true RESOLVE_TOKEN = true
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,8 +2,8 @@ VALID_TOKENS = {
["Bless"] = true ["Bless"] = true
} }
SHOW_SINGLE_RELEASE = true
KEEP_OPEN = true KEEP_OPEN = true
MAX_SEALED = 3
RESOLVE_TOKEN = true RESOLVE_TOKEN = true
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,7 +2,7 @@ VALID_TOKENS = {
["Curse"] = true ["Curse"] = true
} }
SHOW_SINGLE_RELEASE = true MAX_SEALED = 10
KEEP_OPEN = true KEEP_OPEN = true
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,7 +2,7 @@ VALID_TOKENS = {
["Bless"] = true ["Bless"] = true
} }
SHOW_SINGLE_RELEASE = true
SHOW_MULTI_SEAL = 2 SHOW_MULTI_SEAL = 2
MAX_SEALED = 10
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,8 +2,9 @@ VALID_TOKENS = {
["Bless"] = true ["Bless"] = true
} }
SHOW_SINGLE_RELEASE = true KEEP_OPEN = true
SHOW_MULTI_RELEASE = 3 SHOW_MULTI_RELEASE = 3
SHOW_MULTI_RETURN = 3 SHOW_MULTI_RETURN = 3
MAX_SEALED = 10
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -5,5 +5,6 @@ INVALID_TOKENS = {
} }
UPDATE_ON_HOVER = true UPDATE_ON_HOVER = true
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -3,5 +3,6 @@ VALID_TOKENS = {
} }
KEEP_OPEN = true KEEP_OPEN = true
MAX_SEALED = 3
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -3,6 +3,6 @@ VALID_TOKENS = {
} }
KEEP_OPEN = true KEEP_OPEN = true
SHOW_SINGLE_RELEASE = true MAX_SEALED = 5
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,4 +2,6 @@ VALID_TOKENS = {
["Auto-fail"] = true ["Auto-fail"] = true
} }
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,4 +2,6 @@ VALID_TOKENS = {
["Elder Sign"] = true ["Elder Sign"] = true
} }
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -2,6 +2,6 @@ VALID_TOKENS = {
["0"] = true ["0"] = true
} }
SHOW_SINGLE_RELEASE = true MAX_SEALED = 4 -- Core Set is component-limited to 4 '0' tokens
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -3,6 +3,6 @@ VALID_TOKENS = {
} }
KEEP_OPEN = true KEEP_OPEN = true
SHOW_SINGLE_RELEASE = true MAX_SEALED = 5
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -5,4 +5,6 @@ VALID_TOKENS = {
["Elder Thing"] = true, ["Elder Thing"] = true,
} }
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -3,5 +3,6 @@ VALID_TOKENS = {
} }
RESOLVE_TOKEN = true RESOLVE_TOKEN = true
MAX_SEALED = 1
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -5,5 +5,6 @@ INVALID_TOKENS = {
UPDATE_ON_HOVER = true UPDATE_ON_HOVER = true
KEEP_OPEN = true KEEP_OPEN = true
MAX_SEALED = 3
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")

View File

@ -260,7 +260,7 @@ function discardListOfObjects(objList)
end end
elseif tokenChecker.isChaosToken(obj) then elseif tokenChecker.isChaosToken(obj) then
-- put chaos tokens back into bag (e.g. Unrelenting) -- put chaos tokens back into bag (e.g. Unrelenting)
chaosBagApi.returnChaosTokenToBag(obj) chaosBagApi.returnChaosTokenToBag(obj, false)
elseif not obj.getLock() and not obj.hasTag("DontDiscard") then elseif not obj.getLock() and not obj.hasTag("DontDiscard") then
-- don't touch locked objects (like the table etc.) or specific objects (like key tokens) -- don't touch locked objects (like the table etc.) or specific objects (like key tokens)
ownedObjects.Trash.putObject(obj) ownedObjects.Trash.putObject(obj)
@ -878,7 +878,7 @@ function removeTokensFromObject(object)
for _, obj in ipairs(searchLib.onObject(object)) do for _, obj in ipairs(searchLib.onObject(object)) do
if tokenChecker.isChaosToken(obj) then if tokenChecker.isChaosToken(obj) then
chaosBagApi.returnChaosTokenToBag(obj) chaosBagApi.returnChaosTokenToBag(obj, false)
elseif obj.getGUID() ~= "4ee1f2" and -- table elseif obj.getGUID() ~= "4ee1f2" and -- table
obj ~= self and obj ~= self and
obj.type ~= "Deck" and obj.type ~= "Deck" and