Merge pull request #538 from argonui/blurse-improvements

Update to blurse manager
This commit is contained in:
Entrox-Licher 2024-01-15 18:21:19 -05:00 committed by GitHub
commit 24bdde87f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 381 additions and 630 deletions

View File

@ -41,7 +41,7 @@
"Decoration-Coin.180a23",
"Panel.463022",
"TentacleStand.7bff34",
"ScriptingTrigger.83ef06",
"ChaosBagZone.83ef06",
"Cluetokens.11e0cf",
"Doomtokens.b015d8",
"DoomCounter.85c4c6",
@ -107,8 +107,6 @@
"AdditionalPlayerCards.2cba6b",
"BarkhamHorror.308439",
"ChaosBagStatTracker.766620",
"Blesstokens.afa06b",
"Cursetokens.bd0253",
"TokenSpawnTool.36b4ee",
"OfficialStandaloneChallengeScenarios.0ef5c8",
"TarotDeck.77f1e5",

View File

@ -22,7 +22,7 @@
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/2018214163835858903/EECF1C00C9A0C837DD40D7B5A3456B88DF0CEC08/",
"WidthScale": 0
},
"Description": "Left-Click: Add token\nRight-Click: Remove token\n\nContextmenu allows resetting the current state or removing all bless/curse tokens from play.\n\nSee Notebook for detailed instructions.",
"Description": "Left-Click: Add token\nRight-Click: Remove token\n\nContextmenu allows resetting the current state or removing all bless/curse tokens from play.\n\nCheck cards that seal tokens for a context menu.",
"DragSelectable": true,
"GMNotes": "",
"GUID": "5933fb",

View File

@ -1,87 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"Bag": {
"Order": 0
},
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"ContainedObjects_order": [
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be",
"Bless.b2b7be"
],
"ContainedObjects_path": "Blesstokens.afa06b",
"CustomMesh": {
"CastShadows": true,
"ColliderURL": "",
"Convex": true,
"CustomShader": {
"FresnelStrength": 0,
"SpecularColor": {
"b": 1,
"g": 1,
"r": 1
},
"SpecularIntensity": 0,
"SpecularSharpness": 2
},
"DiffuseURL": "http://cloud-3.steamusercontent.com/ugc/1655601092778623873/C9EF4B44CE708DFC5A804FF2912C9F9B47323287/",
"MaterialIndex": 3,
"MeshURL": "https://pastebin.com/raw/ALrYhQGb",
"NormalURL": "",
"TypeIndex": 6
},
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "afa06b",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": true,
"LuaScript": "",
"LuaScriptState": "",
"MaterialIndex": -1,
"MeasureMovement": false,
"MeshIndex": -1,
"Name": "Custom_Model_Bag",
"Nickname": "Bless tokens",
"Snap": true,
"Sticky": true,
"Tags": [
"CleanUpHelper_ignore",
"displacement_excluded"
],
"Tooltip": true,
"Transform": {
"posX": 2.842,
"posY": 1.644,
"posZ": -11.239,
"rotX": 0,
"rotY": 225,
"rotZ": 0,
"scaleX": 0.7,
"scaleY": 0.7,
"scaleZ": 0.7
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,57 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 0.04894,
"g": 0.32859,
"r": 0.37456
},
"CustomImage": {
"CustomTile": {
"Stackable": false,
"Stretch": true,
"Thickness": 0.1,
"Type": 2
},
"ImageScalar": 1,
"ImageSecondaryURL": "",
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/1655601092778627699/339FB716CB25CA6025C338F13AFDFD9AC6FA8356/",
"WidthScale": 0
},
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "b2b7be",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Custom_Tile",
"Nickname": "Bless",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": -1.465,
"posY": 1.703,
"posZ": -26.93,
"rotX": 0,
"rotY": 90,
"rotZ": 0,
"scaleX": 0.81,
"scaleY": 1,
"scaleZ": 0.81
},
"Value": 0,
"XmlUI": ""
}

View File

@ -26,21 +26,21 @@
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "ScriptingTrigger",
"Nickname": "",
"Nickname": "ChaosBagZone",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": 1.4,
"posY": 2.866,
"posZ": -13.4,
"posX": 1.6,
"posY": 4.5,
"posZ": -13.75,
"rotX": 0,
"rotY": 90,
"rotY": 0,
"rotZ": 0,
"scaleX": 6,
"scaleY": 2,
"scaleY": 7,
"scaleZ": 6
},
"Value": 0,
"XmlUI": ""
}
}

View File

@ -1,87 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"Bag": {
"Order": 0
},
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"ContainedObjects_order": [
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891",
"Curse.678891"
],
"ContainedObjects_path": "Cursetokens.bd0253",
"CustomMesh": {
"CastShadows": true,
"ColliderURL": "",
"Convex": true,
"CustomShader": {
"FresnelStrength": 0,
"SpecularColor": {
"b": 1,
"g": 1,
"r": 1
},
"SpecularIntensity": 0,
"SpecularSharpness": 2
},
"DiffuseURL": "http://cloud-3.steamusercontent.com/ugc/1655601092778633181/7A00AF905BCD6EB5D866F2107CECBC0A49E360F7/",
"MaterialIndex": 3,
"MeshURL": "https://pastebin.com/raw/ALrYhQGb",
"NormalURL": "",
"TypeIndex": 6
},
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "bd0253",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": true,
"LuaScript": "",
"LuaScriptState": "",
"MaterialIndex": -1,
"MeasureMovement": false,
"MeshIndex": -1,
"Name": "Custom_Model_Bag",
"Nickname": "Curse tokens",
"Snap": true,
"Sticky": true,
"Tags": [
"CleanUpHelper_ignore",
"displacement_excluded"
],
"Tooltip": true,
"Transform": {
"posX": 4.053,
"posY": 1.642,
"posZ": -12.449,
"rotX": 0,
"rotY": 225,
"rotZ": 0,
"scaleX": 0.7,
"scaleY": 0.7,
"scaleZ": 0.7
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,57 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 0.44425,
"g": 0.00387,
"r": 0.27072
},
"CustomImage": {
"CustomTile": {
"Stackable": false,
"Stretch": true,
"Thickness": 0.1,
"Type": 2
},
"ImageScalar": 1,
"ImageSecondaryURL": "",
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/1655601092778636039/2A25BD38E8C44701D80DD96BF0121DA21843672E/",
"WidthScale": 0
},
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "678891",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Custom_Tile",
"Nickname": "Curse",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": -46.206,
"posY": 1.789,
"posZ": -3.483,
"rotX": 0,
"rotY": 270,
"rotZ": 0,
"scaleX": 0.81,
"scaleY": 1,
"scaleZ": 0.81
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,5 +1,5 @@
local chaosBagApi = require("chaosbag/ChaosBagApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi")
local chaosBagApi = require("chaosbag/ChaosBagApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi")
-- common button parameters
local buttonParamaters = {}
@ -8,424 +8,396 @@ buttonParamaters.color = { 0, 0, 0, 0 }
buttonParamaters.width = 700
buttonParamaters.height = 700
local altState = false
local MODE = {
[false] = "Add / Remove",
[true] = "Take / Return"
}
local BUTTON_COLOR = {
[false] = { 0.4, 0.4, 0.4 },
[true] = { 0.9, 0.9, 0.9 }
}
local FONT_COLOR = {
[false] = { 1, 1, 1 },
[true] = { 0, 0, 0 }
}
local whitespace = " "
local updating
---------------------------------------------------------
-- creating buttons and menus + initializing tables
---------------------------------------------------------
function onSave() return JSON.encode(altState) end
function onLoad()
-- index: 0 - bless
buttonParamaters.click_function = "clickBless"
buttonParamaters.position = { -1.03, 0, 0.46 }
buttonParamaters.tooltip = "Add / Remove Bless"
self.createButton(buttonParamaters)
function onLoad(saved_state)
if saved_state ~= nil then
altState = JSON.decode(saved_state)
end
-- index: 1 - curse
buttonParamaters.click_function = "clickCurse"
buttonParamaters.position[1] = -buttonParamaters.position[1]
buttonParamaters.tooltip = "Add / Remove Curse"
self.createButton(buttonParamaters)
-- index: 0 - bless
buttonParamaters.click_function = "clickBless"
buttonParamaters.position = { -1.03, 0.05, 0.46 }
self.createButton(buttonParamaters)
-- index: 2 - bless count
buttonParamaters.tooltip = ""
buttonParamaters.click_function = "none"
buttonParamaters.width = 0
buttonParamaters.height = 0
buttonParamaters.color = { 0.4, 0.4, 0.4 }
buttonParamaters.font_color = { 1, 1, 1 }
buttonParamaters.font_size = 235
buttonParamaters.position = { -1.03, 0.06, -0.8 }
self.createButton(buttonParamaters)
-- index: 1 - curse
buttonParamaters.click_function = "clickCurse"
buttonParamaters.position[1] = -buttonParamaters.position[1]
self.createButton(buttonParamaters)
-- index: 3 - curse count
buttonParamaters.position[1] = -buttonParamaters.position[1]
self.createButton(buttonParamaters)
-- index: 2 - alternative mode (take / return)
buttonParamaters.click_function = "enableAlt"
buttonParamaters.width = 900
buttonParamaters.height = 210
buttonParamaters.position = { -1.03, 0.05, -0.85 }
self.createButton(buttonParamaters)
-- context menu
self.addContextMenuItem("Remove all", doRemove)
self.addContextMenuItem("Reset", doReset)
-- index: 3 - default mode (add / remove)
buttonParamaters.click_function = "enableDefault"
buttonParamaters.position[1] = -buttonParamaters.position[1]
self.createButton(buttonParamaters)
-- load labels, tooltips and colors
updateButtons()
-- context menu
self.addContextMenuItem("Remove all", doRemove)
self.addContextMenuItem("Reset", doReset)
-- initializing tables
initializeState()
broadcastCount("Curse")
broadcastCount("Bless")
-- initializing tables
initializeState()
broadcastCount("Curse")
broadcastCount("Bless")
end
function resetTables()
numInPlay = { Bless = 0, Curse = 0 }
tokensTaken = { Bless = {}, Curse = {} }
sealedTokens = {}
numInPlay = { Bless = 0, Curse = 0 }
tokensTaken = { Bless = {}, Curse = {} }
sealedTokens = {}
end
function initializeState()
resetTables()
resetTables()
-- count tokens in the bag
local chaosbag = chaosBagApi.findChaosBag()
local tokens = {}
for _, v in ipairs(chaosbag.getObjects()) do
if v.name == "Bless" then
numInPlay.Bless = numInPlay.Bless + 1
elseif v.name == "Curse" then
numInPlay.Curse = numInPlay.Curse + 1
end
-- count tokens in the bag
local chaosBag = chaosBagApi.findChaosBag()
local tokens = {}
for _, v in ipairs(chaosBag.getObjects()) do
if v.name == "Bless" then
numInPlay.Bless = numInPlay.Bless + 1
elseif v.name == "Curse" then
numInPlay.Curse = numInPlay.Curse + 1
end
end
-- find tokens in the play area
for _, obj in ipairs(getObjects()) do
local pos = obj.getPosition()
if pos.x > -65 and pos.x < 10 and pos.z > -35 and pos.z < 35 then
if obj.getName() == "Bless" then
table.insert(tokensTaken.Bless, obj.getGUID())
numInPlay.Bless = numInPlay.Bless + 1
elseif obj.getName() == "Curse" then
table.insert(tokensTaken.Curse, obj.getGUID())
numInPlay.Curse = numInPlay.Curse + 1
end
end
-- find tokens in the play area
for _, obj in ipairs(getObjects()) do
local pos = obj.getPosition()
if pos.x > -65 and pos.x < 10 and pos.z > -35 and pos.z < 35 then
if obj.getName() == "Bless" then
table.insert(tokensTaken.Bless, obj.getGUID())
numInPlay.Bless = numInPlay.Bless + 1
elseif obj.getName() == "Curse" then
table.insert(tokensTaken.Curse, obj.getGUID())
numInPlay.Curse = numInPlay.Curse + 1
end
end
end
updateButtonLabels()
end
function updateButtonLabels()
self.editButton({ index = 2, label = formatTokenCount("Bless", true)})
self.editButton({ index = 3, label = formatTokenCount("Curse", true)})
end
function broadcastCount(token)
local count = formatTokenCount(token)
if count == "(0/0)" then return end
broadcastToAll(token .. " Tokens " .. count, "White")
local count = formatTokenCount(token)
if count == "(0 + 0)" then return end
broadcastToAll(token .. " Tokens " .. count, "White")
end
function broadcastStatus(color)
broadcastToColor("Curse Tokens " .. formatTokenCount("Curse"), color, "White")
broadcastToColor("Bless Tokens " .. formatTokenCount("Bless"), color, "White")
broadcastToColor("Curse Tokens " .. formatTokenCount("Curse"), color, "White")
broadcastToColor("Bless Tokens " .. formatTokenCount("Bless"), color, "White")
end
-- context menu function 1
---------------------------------------------------------
-- TTS event handling
---------------------------------------------------------
-- enable tracking of bag changes for 1 second
function onObjectDrop(_, object)
if not isBlurseToken(object) then return end
-- check if object was dropped in chaos bag area
for _, zone in ipairs(object.getZones()) do
if zone.getName() == "ChaosBagZone" then
trackBagChange = true
Wait.time(function() trackBagChange = false end, 1)
return
end
end
end
-- handle manual returning of bless / curse tokens
function onObjectEnterContainer(container, object)
if not (trackBagChange and isChaosbag(container) and isBlurseToken(object)) then return end
-- small delay to ensure token has entered bag
Wait.time(doReset, 0.5)
end
function isChaosbag(obj)
if obj.getDescription() ~= "Chaos Bag" then return end -- early exit for performance
return obj == chaosBagApi.findChaosBag()
end
function isBlurseToken(obj)
local name = obj.getName()
return name == "Bless" or name == "Curse"
end
---------------------------------------------------------
-- context menu functions
---------------------------------------------------------
function doRemove(color)
local chaosbag = chaosBagApi.findChaosBag()
local chaosBag = chaosBagApi.findChaosBag()
-- remove tokens from chaos bag
local count = { Bless = 0, Curse = 0 }
for _, v in ipairs(chaosbag.getObjects()) do
if v.name == "Bless" or v.name == "Curse" then
chaosbag.takeObject({
guid = v.guid,
position = { 0, 5, 0 },
callback_function = function(obj) obj.destruct() end
})
count[v.name] = count[v.name] + 1
end
-- remove tokens from chaos bag
local count = { Bless = 0, Curse = 0 }
for _, v in ipairs(chaosBag.getObjects()) do
if v.name == "Bless" or v.name == "Curse" then
chaosBag.takeObject({
guid = v.guid,
position = { 0, 5, 0 },
callback_function = function(obj) obj.destruct() end
})
count[v.name] = count[v.name] + 1
end
end
broadcastToColor("Removed " .. count.Bless .. " Bless and " ..
count.Curse .. " Curse tokens from the chaos bag.", color, "White")
broadcastToColor("Removed " .. removeTakenTokens("Bless") .. " Bless and " ..
removeTakenTokens("Curse") .. " Curse tokens from play.", color, "White")
broadcastToColor("Removed " .. count.Bless .. " Bless and " .. count.Curse .. " Curse tokens from the chaos bag.", color, "White")
broadcastToColor("Removed " .. removeTakenTokens("Bless") .. " Bless and " .. removeTakenTokens("Curse") .. " Curse tokens from play.", color, "White")
resetTables()
tokenArrangerApi.layout()
resetTables()
updateButtonLabels()
tokenArrangerApi.layout()
end
-- context menu function 2
function doReset(color)
initializeState()
broadcastCount("Curse")
broadcastCount("Bless")
tokenArrangerApi.layout()
end
-- removing tokens that were 'taken'
function removeTakenTokens(type)
local count = 0
for _, guid in ipairs(tokensTaken[type]) do
local token = getObjectFromGUID(guid)
if token ~= nil then
token.destruct()
count = count + 1
end
end
return count
function doReset()
initializeState()
broadcastCount("Curse")
broadcastCount("Bless")
tokenArrangerApi.layout()
end
---------------------------------------------------------
-- click functions
---------------------------------------------------------
-- click function 1
function clickBless(_, color, isRightClick)
playerColor = color
callFunctions("Bless", isRightClick)
playerColor = color
callFunctions("Bless", isRightClick)
end
-- click function 2
function clickCurse(_, color, isRightClick)
playerColor = color
callFunctions("Curse", isRightClick)
playerColor = color
callFunctions("Curse", isRightClick)
end
-- click function 3
function enableAlt()
if altState then return end
altState = not altState
updateButtons()
end
function callFunctions(type, isRightClick)
if not chaosBagApi.canTouchChaosTokens() then return end
-- click function 4
function enableDefault()
if not altState then return end
altState = not altState
updateButtons()
if isRightClick then
removeToken(type)
else
addToken(type)
end
tokenArrangerApi.layout()
end
---------------------------------------------------------
-- called functions
---------------------------------------------------------
function updateButtons()
self.editButton({
index = 0,
tooltip = MODE[altState] .. " Bless"
})
self.editButton({
index = 1,
tooltip = MODE[altState] .. " Curse"
})
self.editButton({
index = 2,
label = whitespace .. MODE[true] .. (altState and " ✓" or whitespace) .. " ",
color = BUTTON_COLOR[not altState],
font_color = FONT_COLOR[not altState]
})
self.editButton({
index = 3,
label = whitespace .. MODE[false] .. (altState and whitespace or " ✓") .. " ",
color = BUTTON_COLOR[altState],
font_color = FONT_COLOR[altState]
})
end
-- function that is called by click_functions 1+2 and calls the other functions
function callFunctions(token, isRightClick)
if not chaosBagApi.canTouchChaosTokens() then
return
end
local success
if not altState then
if isRightClick then
success = takeToken(token, true)
else
success = addToken(token)
end
else
if isRightClick then
success = returnToken(token)
else
success = takeToken(token, false)
end
end
if success ~= 0 then tokenArrangerApi.layout() end
end
-- returns a formatted string with information about the provided token type (bless / curse)
function formatTokenCount(type)
if type == nil then type = mode end
return "(" .. (numInPlay[type] - #tokensTaken[type]) .. "/" .. #tokensTaken[type] .. ")"
function formatTokenCount(type, omitBrackets)
if type == nil then type = mode end
if omitBrackets then
return (numInPlay[type] - #tokensTaken[type]) .. " + " .. #tokensTaken[type]
else
return "(" .. (numInPlay[type] - #tokensTaken[type]) .. " + " .. #tokensTaken[type] .. ")"
end
end
-- called by cards that seal bless/curse tokens
-- seals a token on a card (called by cards that seal bless/curse tokens)
---@param param Table This contains the type and guid of the sealed token
function sealedToken(param)
table.insert(tokensTaken[param.type], param.guid)
broadcastCount(param.type)
table.insert(tokensTaken[param.type], param.guid)
broadcastCount(param.type)
updateButtonLabels()
end
-- called by cards that seal bless/curse tokens
-- returns a token to the bag (called by cards that seal bless/curse tokens)
---@param param Table This contains the type and guid of the released token
function releasedToken(param)
for i, v in ipairs(tokensTaken[param.type]) do
if v == param.guid then
table.remove(tokensTaken[param.type], i)
break
end
for i, v in ipairs(tokensTaken[param.type]) do
if v == param.guid then
table.remove(tokensTaken[param.type], i)
break
end
if not updating then
updating = true
Wait.frames(function()
broadcastCount(param.type)
updating = false
end, 1)
end
updateDisplayAndBroadcast(param.type)
end
-- removes a token (called by cards that seal bless/curse tokens)
---@param param Table This contains the type and guid of the released token
function returnedToken(param)
for i, v in ipairs(tokensTaken[param.type]) do
if v == param.guid then
table.remove(tokensTaken[param.type], i)
numInPlay[param.type] = numInPlay[param.type] - 1
break
end
end
updateDisplayAndBroadcast(param.type)
end
function updateDisplayAndBroadcast(type)
if not updating then
updating = true
Wait.frames(function()
broadcastCount(type)
updateButtonLabels()
updating = false
end, 5)
end
end
---------------------------------------------------------
-- main functions: add, take and return
-- main functions: add and remove
---------------------------------------------------------
function addToken(type)
if numInPlay[type] == 10 then
printToColor("10 tokens already in play, not adding any.", playerColor)
return 0
end
numInPlay[type] = numInPlay[type] + 1
printToAll("Adding " .. type .. " token " .. formatTokenCount(type))
return chaosBagApi.spawnChaosToken(type)
if numInPlay[type] == 10 then
printToColor("10 tokens already in play, not adding any.", playerColor)
return
end
numInPlay[type] = numInPlay[type] + 1
printToAll("Adding " .. type .. " token " .. formatTokenCount(type))
updateButtonLabels()
return chaosBagApi.spawnChaosToken(type)
end
function takeToken(type, remove)
local chaosbag = chaosBagApi.findChaosBag()
if not remove and not SEAL_CARD_MESSAGE then
broadcastToColor("For sealing tokens on cards try right-clicking on the card for seal options.", playerColor)
SEAL_CARD_MESSAGE = true
function removeToken(type)
local chaosBag = chaosBagApi.findChaosBag()
local tokens = {}
for _, v in ipairs(chaosBag.getObjects()) do
if v.name == type then
table.insert(tokens, v.guid)
end
local tokens = {}
for _, v in ipairs(chaosbag.getObjects()) do
if v.name == type then
table.insert(tokens, v.guid)
end
end
if #tokens == 0 then
printToColor("No " .. type .. " tokens in the chaos bag.", playerColor)
return
end
chaosBag.takeObject({
guid = table.remove(tokens),
smooth = false,
callback_function = function(obj)
numInPlay[type] = numInPlay[type] - 1
printToAll("Removing " .. type .. " token " .. formatTokenCount(type))
updateButtonLabels()
obj.destruct()
end
if #tokens == 0 then
printToColor("No " .. type .. " tokens in the chaos bag.", playerColor)
return 0
end
local pos = self.getPosition() + Vector(2.25, 0, 0.85)
if type == "Curse" then pos[3] = pos[3] - 1.7 end
chaosbag.takeObject({
guid = table.remove(tokens),
position = pos,
smooth = false,
callback_function = function(obj)
if remove then
numInPlay[type] = numInPlay[type] - 1
printToAll("Removing " .. type .. " token " .. formatTokenCount(type))
obj.destruct()
else
table.insert(tokensTaken[type], obj.getGUID())
printToAll("Taking " .. type .. " token " .. formatTokenCount(type))
end
end
})
})
end
function returnToken(type)
local guid = table.remove(tokensTaken[type])
if guid == nil then
printToColor("No " .. type .. " tokens to return", playerColor)
return 0
end
-- removing tokens that were 'taken'
function removeTakenTokens(type)
local count = 0
for _, guid in ipairs(tokensTaken[type]) do
local token = getObjectFromGUID(guid)
if token == nil then
printToColor("Couldn't find token " .. guid .. ", not returning to bag", playerColor)
return 0
if token ~= nil then
token.destruct()
count = count + 1
end
local chaosbag = chaosBagApi.findChaosBag()
if chaosbag == nil then
return 0
end
chaosbag.putObject(token)
printToAll("Returning " .. type .. " token " .. formatTokenCount(type))
end
return count
end
---------------------------------------------------------
-- Wendy Menu (context menu for cards on hotkey press)
-- Wendy's Menu (context menu for cards on hotkey press)
---------------------------------------------------------
function addMenuOptions(parameters)
local playerColor = parameters.playerColor
local hoveredObject = parameters.hoveredObject
if hoveredObject == nil or hoveredObject.getVar("MENU_ADDED") == true then return end
if hoveredObject.tag ~= "Card" then
broadcastToColor("Right-click seal options can only be added to cards", playerColor)
return
end
local playerColor = parameters.playerColor
local hoveredObject = parameters.hoveredObject
if hoveredObject == nil or hoveredObject.type ~= "Card" then
broadcastToColor("Right-click seal options can only be added to cards.", playerColor)
return
elseif hoveredObject.hasTag("CardThatSeals") or hoveredObject.getVar("MENU_ADDED") == true then
broadcastToColor("This card already has a sealing context menu.", playerColor)
return
end
hoveredObject.addContextMenuItem("Seal Bless", function(color)
sealToken("Bless", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Seal Bless", function(color)
sealToken("Bless", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Release Bless", function(color)
releaseToken("Bless", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Release Bless", function(color)
releaseToken("Bless", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Seal Curse", function(color)
sealToken("Curse", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Seal Curse", function(color)
sealToken("Curse", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Release Curse", function(color)
releaseToken("Curse", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
hoveredObject.addContextMenuItem("Release Curse", function(color)
releaseToken("Curse", color, hoveredObject)
tokenArrangerApi.layout()
end, true)
broadcastToColor("Right-click seal options added to " .. hoveredObject.getName(), playerColor)
hoveredObject.setVar("MENU_ADDED", true)
sealedTokens[hoveredObject.getGUID()] = {}
broadcastToColor("Right-click seal options added to " .. hoveredObject.getName(), playerColor)
hoveredObject.setVar("MENU_ADDED", true)
sealedTokens[hoveredObject.getGUID()] = {}
end
function sealToken(type, playerColor, enemy)
local chaosbag = chaosBagApi.findChaosBag()
if chaosbag == nil then return end
local pos = enemy.getPosition()
function sealToken(type, playerColor, hoveredObject)
local chaosBag = chaosBagApi.findChaosBag()
for i, token in ipairs(chaosbag.getObjects()) do
if token.name == type then
chaosbag.takeObject({
position = { pos.x, pos.y + 1, pos.z },
index = i - 1,
smooth = false,
callback_function = function(obj)
Wait.frames(function()
table.insert(sealedTokens[enemy.getGUID()], obj)
table.insert(tokensTaken[type], obj.getGUID())
printToColor("Sealing " .. type .. " token " .. formatTokenCount(type), playerColor)
end, 1)
end
})
return
for i, token in ipairs(chaosBag.getObjects()) do
if token.name == type then
return chaosBag.takeObject({
position = hoveredObject.getPosition() + Vector(0, 1, 0),
index = i - 1,
smooth = false,
callback_function = function(obj)
table.insert(sealedTokens[hoveredObject.getGUID()], obj)
table.insert(tokensTaken[type], obj.getGUID())
tokenArrangerApi.layout()
updateDisplayAndBroadcast(type)
end
})
end
printToColor(type .. " token not found in bag", playerColor)
end
printToColor(type .. " token not found in bag", playerColor)
end
function releaseToken(type, playerColor, enemy)
local chaosbag = chaosBagApi.findChaosBag()
if chaosbag == nil then return end
local tokens = sealedTokens[enemy.getGUID()]
if tokens == nil or #tokens == 0 then return end
function releaseToken(type, playerColor, hoveredObject)
local chaosBag = chaosBagApi.findChaosBag()
local tokens = sealedTokens[hoveredObject.getGUID()]
if tokens == nil or #tokens == 0 then return end
for i, token in ipairs(tokens) do
if token ~= nil and token.getName() == type then
local guid = token.getGUID()
chaosbag.putObject(token)
for j, v in ipairs(tokensTaken[type]) do
if v == guid then
table.remove(tokensTaken[type], j)
table.remove(tokens, i)
printToColor("Releasing " .. type .. " token" .. formatTokenCount(type), playerColor)
return
end
end
for i, token in ipairs(tokens) do
if token ~= nil and token.getName() == type then
local guid = token.getGUID()
chaosBag.putObject(token)
for j, v in ipairs(tokensTaken[type]) do
if v == guid then
table.remove(tokensTaken[type], j)
table.remove(tokens, i)
tokenArrangerApi.layout()
updateDisplayAndBroadcast(type)
return
end
end
end
printToColor(type .. " token not sealed on " .. enemy.getName(), playerColor)
end
printToColor(type .. " token not sealed on " .. hoveredObject.getName(), playerColor)
end
function none() end

View File

@ -24,6 +24,11 @@ do
getManager().call("releasedToken", { type = type, guid = guid })
end
-- updates the internal count (called by cards that seal bless/curse tokens)
BlessCurseManagerApi.returnedToken = function(type, guid)
getManager().call("returnedToken", { type = type, guid = guid })
end
-- broadcasts the current status for bless/curse tokens
---@param playerColor String Color of the player to show the broadcast to
BlessCurseManagerApi.broadcastStatus = function(playerColor)
@ -36,9 +41,9 @@ do
getManager().call("doRemove", playerColor)
end
-- adds Wendy's menu to the hovered card (allows sealing of tokens)
-- adds bless / curse sealing to the hovered card
---@param color String Color of the player to show the broadcast to
BlessCurseManagerApi.addWendysMenu = function(playerColor, hoveredObject)
BlessCurseManagerApi.addBlurseSealingMenu = function(playerColor, hoveredObject)
getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject })
end

View File

@ -44,6 +44,12 @@ do
return Global.call("removeChaosToken", id)
end
-- returns a chaos token to the bag and calls all relevant functions
---@param token TTSObject Chaos Token to return
ChaosBagApi.returnChaosTokenToBag = function(token)
return Global.call("returnChaosTokenToBag", token)
end
-- spawns the specified chaos token and puts it into the chaos bag
---@param id String ID of the chaos token
ChaosBagApi.spawnChaosToken = function(id)

View File

@ -8,6 +8,7 @@ local victoryDisplayApi = require("core/VictoryDisplayApi")
function onLoad()
addHotkey("Add doom to agenda", addDoomToAgenda)
addHotkey("Add Bless/Curse context menu", addBlurseSealingMenu)
addHotkey("Discard object", discardObject)
addHotkey("Discard top card", discardTopDeck)
addHotkey("Display Bless/Curse status", showBlessCurseStatus)
@ -18,7 +19,6 @@ function onLoad()
addHotkey("Take clue from location", takeClueFromLocation)
addHotkey("Upkeep", triggerUpkeep)
addHotkey("Upkeep (Multi-handed)", triggerUpkeepMultihanded)
addHotkey("Wendy's Menu", addWendysMenu)
end
-- triggers the "Upkeep" function of the calling player's playmat
@ -325,8 +325,8 @@ function showBlessCurseStatus(playerColor)
end
-- adds Wendy's menu to the hovered card
function addWendysMenu(playerColor, hoveredObject)
blessCurseManagerApi.addWendysMenu(playerColor, hoveredObject)
function addBlurseSealingMenu(playerColor, hoveredObject)
blessCurseManagerApi.addBlurseSealingMenu(playerColor, hoveredObject)
end
-- Simple method to check if the given point is in a specified area

View File

@ -252,6 +252,18 @@ function returnChaosTokens()
chaosTokens = {}
end
-- returns a single chaos token to the bag and calls respective functions
function returnChaosTokenToBag(token)
local name = token.getName()
local guid = token.getGUID()
local chaosBag = findChaosBag()
chaosBag.putObject(token)
tokenArrangerApi.layout()
if name == "Bless" or name == "Curse" then
blessCurseManagerApi.releasedToken(name, guid)
end
end
-- Checks to see if the chaos bag can be manipulated. If a player is searching the bag when tokens
-- are drawn or replaced a TTS bug can cause those tokens to vanish. Any functions which change the
-- contents of the bag should check this method before doing so.

View File

@ -22,6 +22,11 @@ SHOW_MULTI_RELEASE --@type: number (amount of tokens to release at once)
- 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)
- enables an entry in the context menu
- this entry allows returning tokens to the token pool
- example usage: "Nephthys" (to return 3 bless tokens at once)
SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once)
- enables an entry in the context menu
- this entry allows sealing of multiple tokens at once
@ -96,6 +101,11 @@ function generateContextMenu()
self.addContextMenuItem("Release token(s)", releaseAllTokens)
end
-- conditional release option
if SHOW_MULTI_RETURN then
self.addContextMenuItem("Return " .. SHOW_MULTI_RETURN .. " token(s)", returnMultipleTokens)
end
-- main context menu options to seal tokens
for _, map in pairs(ID_URL_MAP) do
if (VALID_TOKENS[map.name] ~= nil) or (UPDATE_ON_HOVER and tokensInBag[map.name] and not INVALID_TOKENS[map.name]) then
@ -139,6 +149,10 @@ function readBag()
end
end
function resetSealedTokens()
sealedTokens = {}
end
-- native event from TTS - used to update the context menu for cards like "Unrelenting"
function onHover()
if UPDATE_ON_HOVER then
@ -211,6 +225,18 @@ function releaseAllTokens(playerColor)
end
end
-- returns multiple tokens at once to the token pool
function returnMultipleTokens(playerColor)
if SHOW_MULTI_RETURN <= #sealedTokens then
for i = 1, SHOW_MULTI_RETURN do
returnToken(table.remove(sealedTokens))
end
printToColor("Returning " .. SHOW_MULTI_RETURN .. " tokens", playerColor)
else
printToColor("Not enough tokens sealed.", playerColor)
end
end
-- returns the token (referenced by GUID) to the chaos bag
function putTokenAway(guid)
local token = getObjectFromGUID(guid)
@ -224,3 +250,15 @@ function putTokenAway(guid)
blessCurseManagerApi.releasedToken(name, guid)
end
end
-- returns the token to the pool (== removes it)
function returnToken(guid)
local token = getObjectFromGUID(guid)
if not token then return end
local name = token.getName()
token.destruct()
if name == "Bless" or name == "Curse" then
blessCurseManagerApi.returnedToken(name, guid)
end
end

View File

@ -3,5 +3,6 @@ VALID_TOKENS = {
}
SHOW_MULTI_RELEASE = 3
SHOW_MULTI_RETURN = 3
require("playercards/CardsThatSealTokens")

View File

@ -213,8 +213,7 @@ function discardListOfObjects(objList)
end
-- put chaos tokens back into bag (e.g. Unrelenting)
elseif tokenChecker.isChaosToken(obj) then
local chaosBag = chaosBagApi.findChaosBag()
chaosBag.putObject(obj)
chaosBagApi.returnChaosTokenToBag(obj)
-- don't touch locked objects (like the table etc.)
elseif not obj.getLock() then
ownedObjects.Trash.putObject(obj)
@ -660,15 +659,23 @@ end
-- removes tokens from the provided card/deck
function removeTokensFromObject(object)
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
for _, obj in ipairs(searchLib.onObject(object)) do
if obj.getGUID() ~= "4ee1f2" and -- table
if tokenChecker.isChaosToken(obj) then
chaosBagApi.returnChaosTokenToBag(obj)
elseif obj.getGUID() ~= "4ee1f2" and -- table
obj ~= self and
obj.type ~= "Deck" and
obj.type ~= "Card" and
obj.memo ~= nil and
obj.getLock() == false and
obj.getDescription() ~= "Action Token" and
not tokenChecker.isChaosToken(obj) then
obj.getDescription() ~= "Action Token" then
ownedObjects.Trash.putObject(obj)
end
end