Merge pull request #226 from argonui/chaos-token-updates

Chaos Token spawning, drawing and sealing // Custom Playercolors
This commit is contained in:
Chr1Z 2023-04-08 20:04:53 +02:00 committed by GitHub
commit 2e8e0aea4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 815 additions and 2897 deletions

View File

@ -63,7 +63,7 @@
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "require(\"chaosbag/ChaosBag\")",
"LuaScript": "",
"LuaScriptState": "",
"MaterialIndex": -1,
"MeasureMovement": false,

View File

@ -18,7 +18,7 @@
"Curse.16a9a7",
"Bless.8e3aab",
"ElderSign.0b1aca",
"Auto-Fail.e31821",
"Auto-fail.e31821",
"ElderThing.38609c",
"Tablet.1a1506",
"Cultist.7d6103",

View File

@ -37,7 +37,7 @@
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Custom_Tile",
"Nickname": "Auto-Fail",
"Nickname": "Auto-fail",
"Snap": true,
"Sticky": true,
"Tooltip": true,

View File

@ -15,8 +15,7 @@
},
"ContainedObjects_order": [
"ArkhamFantasy-PixelArtMini-Cards.e17c9e",
"DrawTokenButtonTooltipRenamer.cc77a8",
"WhimsicalsGenericDifficultySelector.05efb4",
"GenericDifficultySelector.8112ff",
"LuckyPenny.2ab443",
"Double-SidedResource.bc81cb",
"DescriptivePhaseTracker.b171c8",

View File

@ -1,45 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 0,
"g": 0,
"r": 1
},
"Description": "By Pyxel",
"DragSelectable": true,
"GMNotes": "",
"GUID": "cc77a8",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScriptState": "",
"LuaScript_path": "Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.ttslua",
"MeasureMovement": false,
"Name": "Checker_red",
"Nickname": "Draw Token Button Tooltip Renamer",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": 30.498,
"posY": 4.084,
"posZ": -20.929,
"rotX": 0,
"rotY": 270,
"rotZ": 0,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,455 +0,0 @@
function onLoad()
spawnButton("symbols", "Change Tooltips",
"Changes tooltip for 'draw chaos token' buttons.", 0, 0.5, 0, 600, 400, 70)
end
function spawnButton( func, text, tool_tip, xPosition, yPosition, zPosition, button_width, button_height, fontsize )
scale = self.getScale()
scale = scale[1]
params = {
click_function = func,
function_owner = self,
label = text,
position = {scale * xPosition, yPosition, scale * zPosition},
rotation = {0, 0, 0},
width = button_width * scale,
height = button_height * scale,
font_size = fontsize * scale,
color = {1, 1, 1},
font_color = {0, 0, 0},
tooltip = tool_tip
}
self.createButton(params)
end
function symbols()
local tool = "no scenario selected"
for _, scenario in ipairs(getObjectFromGUID("fe2ae4").getObjects()) do
if scenario.getDescription() == "The Gathering" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull] -2. If you fail, after this skill test, search the encounter deck and discard pile for a [[Ghoul]] enemy, and draw it. Shuffle the encounter deck.\n\n[cultist] Reveal another token. If you fail, take 2 horror.\n\n[tablet] -4. If there is a [[Ghoul]] enemy at your location, take 1 damage and 1 horror."
else
tool = "Easy / Standard\n\n[skull] -X. X is the number of [[Ghoul]] enemies at your location.\n\n[cultist] -1. If you fail, take 1 horror.\n\n[tablet] -2. If there is a [[Ghoul]] enemy at your location, take 1 damage.\n\n"
end
end
-- midnight masks
if scenario.getDescription() == "The Midnight Masks" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the total number of doom in play.\n\n[cultist]: -2. Place 1 doom on each [[Cultist]] enemy in play. If there are no [[Cultist]] enemies in play, reveal another token.\n\n[tablet]: -4. If you fail, place all your clues on your location."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the highest number of doom on a [[Cultist]] enemy in play.\n\n[cultist]: -2. Place 1 doom on the nearest [[Cultist]] enemy.\n\n[tablet]: -3. If you fail, place 1 of your clues on your location."
end
end
-- devourer below
if scenario.getDescription() == "The Devourer Below" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -3. If you fail, after this skill test, search the encounter deck and discard pile for a [[Monster]] enemy, and draw it. Shuffle the encounter deck.\n\n[cultist]: -4. Place 2 doom on the nearest enemy.\n\n[tablet]: -5. If there is a [[Monster]] enemy at your location, take 1 damage and 1 horror.\n\n[elder_thing]: -7. If there is an [[Ancient One]] enemy in play, reveal another token."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the number of [[Monster]] enemies in play.\n\n[cultist]: -2. Place 1 doom on the nearest enemy.\n\n[tablet]: -3. If there is a [[Monster]] enemy at your location, take 1 damage.\n\n[elder_thing]: -5. If there is an [[Ancient One]] enemy in play, reveal another token."
end
end
-- extracurricular activity symbols
if scenario.getDescription() == "Extracurricular Activity" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2. If you fail, discard the top 5 cards of your deck.\n\n[cultist]: -1 (-5 instead if there are 10 or more cards in your discard pile).\n\n[elder_thing]: -X. Discard the top 3 cards of your deck. X is the total printed cost of those discarded cards."
else
tool = "Easy / Standard\n\n[skull]: -1. If you fail, discard the top 3 cards of your deck.\n\n[cultist]: -1 (-3 instead if there are 10 or more cards in your discard pile).\n\n[elder_thing]: -X. Discard the top 2 cards of your deck. X is the total printed cost of those discarded cards."
end
end
-- The house always wins symbols
if scenario.getDescription() == "The House Always Wins" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -3. You may spend 3 resources to treat this token as a 0, instead.\n\n[cultist]: -3. If you fail, discard 3 resources.\n\n[tablet]: -2. Discard 3 resources."
else
tool = "Easy / Standard\n\n[skull]: -2. You may spend 2 resources to treat this token as a 0, instead.\n\n[cultist]: -3. If you succeed, gain 3 resources.\n\n[tablet]: -2. If you fail, discard 3 resources."
end
end
-- Miskatonic museum symbols
if scenario.getDescription() == "The Miskatonic Museum" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if Hunting Horror is at your location.)\n\n[cultist]: -3. If you fail, search the encounter deck, discard pile, and the void for Hunting Horror and spawn it at your location, if able.\n\n[tablet]: -4. If Hunting Horror is at your location, it immediately attacks you.\n\n[elder_thing]: -5. If you fail, discard an asset you control."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if Hunting Horror is at your location.)\n\n[cultist]: -1. If you fail, search the encounter deck, discard pile, and the void for Hunting Horror and spawn it at your location, if able.\n\n[tablet]: -2. Return 1 of your clues to your current location.\n\n[elder_thing]: -3. If you fail, discard an asset you control."
end
end
-- essex county express symbols
if scenario.getDescription() == "The Essex County Express" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 more than the current Agenda #. \n\n[cultist]: Reveal another token. If you fail and it is your turn, lose all remaining actions and end your turn immediately.\n\n[tablet]: -4. Add 1 doom token to each Cultist enemy in play.\n\n[elder_thing]: -3. If you fail, choose and discard a card from your hand for each point you failed by."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the current Agenda #.\n\n[cultist]: -1. If you fail and it is your turn, lose all remaining actions and end your turn immediately.\n\n[tablet]: -2. Add 1 doom token to the nearest Cultist enemy.\n\n[elder_thing]: -3. If you fail, choose and discard a card from your hand."
end
end
--blood on the Altar
if scenario.getDescription() == "Blood on the Altar" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -1 for each location in play with no encounter card underneath it.\n\n[cultist]: -4. If you fail, add 1 clue from the token pool to your location.\n\n[tablet]: -3. Reveal another token.\n\n[elder_thing]: -3. Place 1 doom on the current agenda."
else
tool = "Easy / Standard\n\n[skull]: -1 for each location in play with no encounter card underneath it (max -4).\n\n[cultist]: -2. If you fail, add 1 clue from the token pool to your location.\n\n[tablet]: -2. If you are in the Hidden Chamber, reveal another token.\n\n[elder_thing]: -3. If you fail, place 1 doom on the current agenda."
end
end
--undimensioned and unseen
if scenario.getDescription() == "Undimensioned and Unseen" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 for each Brood of Yog-Sothoth in play.\n\n[cultist]: Reveal another token. If you fail this test, take 1 horror and 1 damage.\n\n[tablet]: 0. You must either remove all clue tokens from a Brood of Yog-Sothoth in play, or this test automatically fails.\n\n[elder_thing]: -5. If this token is revealed during an attack or evasion attempt against a Brood of Yog-Sothoth, it immediately attacks you."
else
tool = "Easy / Standard\n\n[skull]: -1 for each Brood of Yog-Sothoth in play.\n\n[cultist]: Reveal another token. If you fail this test, take 1 horror.\n\n[tablet]: 0. You must either remove all clue tokens from a Brood of Yog-Sothoth in play, or this token's modifier is -4 instead.\n\n[elder_thing]: -3. If this token is revealed during an attack or evasion attempt against a Brood of Yog-Sothoth, it immediately attacks you."
end
end
-- where doom Awaits
if scenario.getDescription() == "Where Doom Awaits" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-5 instead if you are at an [[Altered]] location).\n\n[cultist]: Reveal another token. Cancel the effects and icons of each skill card committed to this test.\n\n[tablet]: -3. If it is Agenda 2, you automatically fail instead.\n\n[elder_thing]: -X. Discard the top 3 cards of your deck. X is the total printed cost of those discarded cards."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you are at an [[Altered]] location).\n\n[cultist]: Reveal another token. Cancel the effects and icons of each skill card committed to this test.\n\n[tablet]: -2 (-4 instead if it is Agenda 2).\n\n[elder_thing]: -X. Discard the top 2 cards of your deck. X is the total printed cost of those discarded cards."
end
end
--lost in time and space
if scenario.getDescription() == "Lost in Time and Space" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -1 for each [[Extradimensional]] location in play.\n\n[cultist]: Reveal another token. After this skill test, discard cards from the top of the encounter deck until a location is discarded. Put that location into play and move there.\n\n[tablet]: -5. If Yog-Sothoth is in play, it attacks you after this skill test.\n\n[elder_thing]: -X. X is twice the shroud value of your location. If you fail and your location is [[Extradimensional]], discard it."
else
tool = "Easy / Standard\n\n[skull]: -1 for each [[Extradimensional]] location in play (max -5).\n\n[cultist]: Reveal another token. If you fail, after this skill test, discard cards from the top of the encounter deck until a location is discarded. Put that location into play and move there.\n\n[tablet]: -3. If Yog-Sothoth is in play, it attacks you after this skill test.\n\n[elder_thing]: -X. X is the shroud value of your location. If you fail and your location is [[Extradimensional]], discard it."
end
end
-- curtain call
if scenario.getDescription() == "Curtain Call" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X, where X is the amount of horror on you. (If you have no horror on you, X is 1.)\n\n[cultist] [tablet] [elder_thing]: -5. If your location has at least 1 horror on it, take 1 horror <i>(from the token pool)</i>. If your location has no horror on it, place 1 horror on it instead."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you have 3 or more horror on you).\n\n[cultist] [tablet] [elder_thing]: -4. If your location has at least 1 horror on it, take 1 horror <i>(from the token pool)</i>. If your location has no horror on it, place 1 horror on it instead."
end
end
-- Last King
if scenario.getDescription() == "The Last King" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: Reveal another token. If you fail, place 1 doom on the [[Lunatic]] enemy in play with the most remaining health.\n\n[cultist]: -3. Place 1 of your clues on your location.\n\n[tablet]: -4. Take 1 horror.\n\n[elder_thing]: -X. X is the shroud value of your location. If you fail, take 1 damage."
else
tool = "Easy / Standard\n\n[skull]: Reveal another token. If you fail, place 1 doom on a [[Lunatic]] enemy in play.\n\n[cultist]: -2. If you fail, place 1 of your clues on your location.\n\n[tablet]: -4. If you fail, take 1 horror.\n\n[elder_thing]: -X. X is the shroud value of your location."
end
end
-- Echoes Past
if scenario.getDescription() == "Echoes of the Past" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the total number of doom on enemies in play.\n\n[cultist]: -4. Place 1 doom on the nearest enemy.\n\n[tablet]: -4. Discard a random card from your hand.\n\n[elder_thing]: -4. If there is an enemy at your location, take 1 horror."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the highest number of doom on an enemy in play.\n\n[cultist]: -2. If you fail, place 1 doom on the nearest enemy.\n\n[tablet]: -2. If you fail, discard a random card from your hand.\n\n[elder_thing]: -2. If you fail and there is an enemy at your location, take 1 horror."
end
end
-- Unspeakable Oath
if scenario.getDescription() == "The Unspeakable Oath" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: Reveal another token. If you fail, randomly choose an enemy from among the set-aside [[Monster]] enemies and place it beneath the act deck without looking at it. (Limit once per test.)\n\n[cultist]: -X. X is the amount of horror on you. If you fail, take 1 horror.\n\n[tablet]: -X. X is the base shroud value of your location. If you fail, take 1 horror.\n\n[elder_thing]: 0. Either randomly choose an enemy from among the set-aside [[Monster]] enemies and place it beneath the act deck without looking at it, or this test automatically fails instead."
else
tool = "Easy / Standard\n\n[skull]: -1. If you fail, randomly choose an enemy from among the set-aside [[Monster]] enemies and place it beneath the act deck without looking at it.\n\n[cultist]: -X. X is the amount of horror on you.\n\n[tablet]: -X. X is the base shroud value of your location.\n\n[elder_thing]: 0. Either randomly choose an enemy from among the set-aside [[Monster]] enemies and place it beneath the act deck without looking at it, or this test automatically fails instead."
end
end
-- A Phantom of Truth
if scenario.getDescription() == "A Phantom of Truth" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the amount of doom in play.\n\n[cultist]: -2. Move each unengaged [[Byakhee]] in play once toward the nearest investigator.\n\n[tablet]: -4. Cancel the effects and icons of each skill card committed to this test.\n\n[elder_thing]: -3. If you fail, lose 1 resource for each point you failed by."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the amount of doom in play (max 5).\n\n[cultist]: -2. If you fail, move each unengaged [[Byakhee]] in play once toward the nearest investigator.\n\n[tablet]: -3. Cancel the effects and icons of each skill card committed to this test.\n\n[elder_thing]: -2. If you fail, lose 1 resource for each point you failed by."
end
end
-- The Pallid Mask
if scenario.getDescription() == "The Pallid Mask" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the number of locations away from the starting location you are.\n\n[cultist]: -3. If this token is revealed during an attack and this skill test is successful, this attack deals no damage.\n\n[tablet]: -3. If there is a [[Ghoul]] or [[Geist]] enemy at your location, it readies and attacks you (if there is more than one, choose one).\n\n[elder_thing]: -4. If you fail, search the encounter deck and discard pile for a [[Ghoul]] or [[Geist]] enemy and draw it."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the number of locations away from the starting location you are (max 5).\n\n[cultist]: -2. If this token is revealed during an attack, and this skill test is successful, this attack deals 1 less damage.\n\n[tablet]: -2. If there is a ready [[Ghoul]] or [[Geist]] enemy at your location, it attacks you (if there is more than one, choose one).\n\n[elder_thing]: -3. If you fail, search the encounter deck and discard pile for a [[Ghoul]] or [[Geist]] enemy and draw it."
end
end
-- Dim Carcosa
if scenario.getDescription() == "Dim Carcosa" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the amount of horror on you.\n\n[cultist]: Reveal another token. If you fail, take 2 horror.\n\n[tablet]: -5. If you fail and Hastur is in play, place 1 clue on your location <i>(from the token bank)</i>.\n\n[elder_thing]: -5. If this token is revealed during an attack or evasion attempt against a [[Monster]] or [[Ancient One]] enemy, lose 1 action."
else
tool = "Easy / Standard\n\n[skull]: -2 (-4 instead if you have no sanity remaining).\n\n[cultist]: Reveal another token. If you fail, take 1 horror.\n\n[tablet]: -3. If you fail and Hastur is in play, place 1 clue on your location <i>(from the token bank)</i>.\n\n[elder_thing]: -3. If this token is revealed during an attack or evasion attempt against a [[Monster]] or [[Ancient One]] enemy, lose 1 action."
end
end
if scenario.getDescription() == "Black Stars Rise" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the total amount of doom on agendas in play.\n\n[cultist]: Reveal another token. If there is an enemy with 1 or more doom on it at your location, this test automatically fails instead.\n\n[tablet]: Reveal another token. If you do not succeed by at least 1, place 1 doom on each agenda.\n\n[elder_thing]: -3. If you fail, search the encounter deck and discard pile for a [[Byakhee]] enemy and draw it."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the highest amount of doom on an agenda in play.\n\n[cultist]: Reveal another token. If this token is revealed during an attack or evasion attempt against an enemy with doom on it, this skill test automatically fails instead.\n\n[tablet]: Reveal another token. If you fail, place 1 doom on each agenda.\n\n[elder_thing]: -2. If you fail, search the encounter deck and discard pile for a [[Byakhee]] enemy and draw it."
end
end
-- untamed Wilds
if scenario.getDescription() == "The Untamed Wilds" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 higher than the number of vengeance points in the victory display.\n\n[cultist]: -X. X is the number of locations in play.\n\n[tablet]: -X. X is the number of cards in the exploration deck (min 3).\n\n[elder_thing]: -3. If you are poisoned, this test automatically fails instead. If you are not poisoned and you fail, put a set-aside Poisoned weakness into play in your threat area."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the number of vengeance points in the victory display.\n\n[cultist]: -X. X is the number of locations in play (max 5).\n\n[tablet]: -X. X is the number of cards in the exploration deck (max 5).\n\n[elder_thing]: -2. If you are poisoned, this test automatically fails instead."
end
end
--The doom of Eztli
if scenario.getDescription() == "The Doom of Eztli" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if there is doom on your location).\n\n[cultist] [tablet]: -X. X is the total amount of doom on locations in play.\n\n[elder_thing]: Reveal another chaos token. Place 1 doom on your location."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if there is doom on your location).\n\n[cultist] [tablet]: -X. X is the number of locations with doom on them.\n\n[elder_thing]: Reveal another chaos token. If you fail, place 1 doom on your location."
end
end
--Threads of Fate
if scenario.getDescription() == "Threads of Fate" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull] : -X. X is the total number of doom in play.\n\n[cultist]: -2. If you do not succeed by at least 2, take 1 direct damage.\n\n[tablet]: -2. If you do not succeed by at least 2, place 1 doom on each [[cultist]] enemy.\n\n[elder_thing]: -3. If you fail, lose 1 of your clues."
else
tool = "Easy / Standard\n\n[skull] : -X. X is the highest number of doom on a [[cultist]] enemy.\n\n[cultist]: -2. If you do not succeed by at least 1, take 1 damage.\n\n[tablet]: -2. If you do not succeed by at least 1, place 1 doom on the nearest [[cultist]] enemy.\n\n[elder_thing]: -2. If you fail, lose 1 of your clues."
end
end
--The boundary beyond
if scenario.getDescription() == "The Boundary Beyond" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if you are at an [[Ancient]] location).\n\n[cultist]: Reveal another token. If you fail, place 1 doom on each [[Cultist]] enemy.\n\n[tablet]: Reveal another token. If you fail, each [[Serpent]] enemy at your location attacks you.\n\n[elder_thing]: -4. Place 1 clue <i>(from the token pool)</i> on the nearest [[Ancient]] location."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you are at an [[Ancient]] location).\n\n[cultist]: Reveal another token. If you fail, place 1 doom on a [[Cultist]] enemy.\n\n[tablet]: Reveal another token. If you fail and there is a [[Serpent]] enemy at your location, it attacks you.\n\n[elder_thing]: -4. If you fail, place 1 clue <i>(from the token pool)</i> on the nearest [[Ancient]] location."
end
end
--Heart of the elders p1
if scenario.getDescription() == "Heart of the Elders" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if you are in a [[Cave]] location).\n\n[cultist]: -3. If you fail, place 1 doom on your location.\n\n[tablet]: -3. If you are poisoned, this test automatically fails instead. If you are not poisoned and you fail, put a set-aside Poisoned weakness into play in your threat area.\n\n[elder_thing]: -4. If you fail, take 1 horror."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you are in a [[Cave]] location).\n\n[cultist]: -2. If you fail, place 1 doom on your location.\n\n[tablet]: -2. If you are poisoned, this test automatically fails instead.\n\n[elder_thing]: -3. If you fail, take 1 horror."
end
end
-- City of Archives
if scenario.getDescription() == "The City of Archives" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (if you have 5 or more cards in your hand, you automatically fail instead).\n\n[cultist] or [elder_thing]: -2. Place 1 of your clues on your location.\n\n[tablet]: -3. For each point you fail by, discard 1 random card from your hand."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you have 5 or more cards in your hand).\n\n[cultist] or [elder_thing]: -2. If you fail, place 1 of your clues on your location.\n\n[tablet]: -3. If you fail, discard 1 random card from your hand."
end
end
--Depths of Yoth
if scenario.getName() == "Scenario - Easy/Standard" then
tool = "Easy / Standard\n\n[skull]: -X. X is the current depth level.\n\n[cultist]: Reveal another token. If you fail, each [[Serpent]] enemy at your location or a connecting location heals 2 damage.\n\n[tablet]: Reveal another token. If you fail, place 1 clue on your location <i>(from the token pool)</i>.\n\n[elder_thing]: -2. If there are 3 or more vengeance points in the victory display, you automatically fail this test, instead."
end
--hard
if scenario.getName() == "Scenario - Hard/Expert" then
tool = "Hard / Expert\n\n[skull]: -X. X is the current depth level. If you fail, take 1 horror.\n\n[cultist]: Reveal another token. If you fail, each [[Serpent]] enemy at your location or a connecting location heals 2 damage.\n\n[tablet]: Reveal another token. If you fail, place 1 clue on your location <i>(from the token pool)</i>.\n\n[elder_thing]: -4. If there are 3 or more vengeance points in the victory display, you automatically fail this test, instead."
end
--Shattered Aeons
if scenario.getDescription() == "Shattered Aeons" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -3 (-5 instead if the Relic of Ages is at your location).\n\n[cultist]: -3. If you do not succeed by at least 1, place 1 doom on each [[Cultist]] enemy.\n\n[tablet]: -3. If you are poisoned, this test automatically fails instead. If you are not poisoned and you fail, put a set-aside Poisoned weakness into play in your threat area.\n\n[elder_thing]: -3. Shuffle the topmost [[Hex]] treachery in the encounter discard pile into the exploration deck."
else
tool = "Easy / Standard\n\n[skull]: -2 (-4 instead if the Relic of Ages is at your location).\n\n[cultist]: -2. If you do not succeed by at least 1, place 1 doom on the nearest [[Cultist]] enemy.\n\n[tablet]: -2. If you are poisoned, this test automatically fails instead.\n\n[elder_thing]: -2. If you fail, shuffle the topmost [[Hex]] treachery in the encounter discard pile into the exploration deck."
end
end
--secret scenario
if scenario.getDescription() == "Turn Back Time" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X . X is the total amount of doom on locations.\n\n[elder_thing]: -6. Place 1 doom on your location."
else
tool = "Easy / Standard\n\n[skull]: -X . X is the number of locations with doom on them.\n\n[elder_thing]: -4. If you fail, place 1 doom on your location."
end
end
--Dissappearance Twilight
if scenario.getDescription() == "Disappearance at the Twilight Estate" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -5. If you fail and this is an attack or evasion attempt, resolve each haunted ability on your location."
else
tool = "Easy / Standard\n\n[skull]: -3. If you fail and this is an attack or evasion attempt, resolve each haunted ability on your location."
end
end
--Witching Hour
if scenario.getDescription() == "The Witching Hour" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2. Discard cards from the top of the encounter deck equal to this test's difficulty.\n\n[tablet]: -2. If you fail, after this test resolves, draw the bottommost treachery in the encounter discard pile.\n\n[elder_thing]: -4. If you fail, ready each [[Witch]] enemy at your location and at each connecting location. Heal all damage from each of those enemies."
else
tool = "Easy / Standard\n\n[skull]: -1. For each point you fail by, discard the top card of the encounter deck.\n\n[tablet]: -1. If you fail, after this test resolves, draw the bottommost treachery in the encounter discard pile.\n\n[elder_thing]: -3. If you fail, choose an exhausted or damaged [[Witch]] enemy at your location or at a connecting location. Ready that enemy and heal all damage from it."
end
end
--Death's Doorstep
if scenario.getDescription() == "At Death's Doorstep" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if your location is haunted).\n\n[tablet]: -3. If this is an attack or evasion attempt, resolve each haunted ability on your location.\n\n[elder_thing]: -4. If there is a [[Spectral]] enemy at your location, take 1 damage and 1 horror."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if your location is haunted).\n\n[tablet]: -2. If you fail and this is an attack or evasion attempt, resolve each haunted ability on your location.\n\n[elder_thing]: -2. If there is a [[Spectral]] enemy at your location, take 1 damage."
end
end
--Secret Name
if scenario.getDescription() == "The Secret Name" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if you are at an [[Extradimensional]] location).\n\n[cultist]: Reveal another chaos token. If you fail, discard the top 5 cards of the encounter deck.\n\n[tablet]: -3. If you fail and Nahab is in play, she attacks you <i>(regardless of her current location)</i>.\n\n[elder_thing]: -4. Resolve the hunter keyword on each enemy in play."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you are at an [[Extradimensional]] location).\n\n[cultist]: Reveal another chaos token. If you fail, discard the top 3 cards of the encounter deck.\n\n[tablet]: -2. If you fail and Nahab is at your location, she attacks you.\n\n[elder_thing]: -3. If you fail, resolve the hunter keyword on each enemy in play."
end
end
--Wages of Sin
if scenario.getDescription() == "The Wages of Sin" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the number of copies of Unfinished Business in the victory display. Reveal another token.\n\n[cultist]: -4. Until the end of the rount, each Heretic enemy in play gets +1 fight and +1 evade.\n\n[tablet]: -4. If you fail, trigger the forced ability on a copy of Unfinished Business in your threat area as if it were the end of the round.\n\n[elder_thing]: -2. If this is an attack or evasion attempt, resolve each haunted ability on your location."
else
tool = "Easy / Standard\n\n[skull]: -X. X is 1 higher than the number of copies of Unfinished Business in the victory display.\n\n[cultist]: -3. Until the end of the round, each Heretic enemy in play gets +1 fight and +1 evade.\n\n[tablet]: -3. If you fail, trigger the forced ability on a copy of Unfinished Business in yout threat area as if it were the end of the round.\n\n[elder_thing]: -2. If you fail and this is an attack or evasion attempt, resolve each haunted ability on your location."
end
end
-- For The Greater Good
if scenario.getDescription() == "For the Greater Good" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the total number of doom among [[Cultist]] enemies in play.\n\n[cultist]: -2. Reveal another token.\n\n[tablet]: -3. If you fail, place 1 doom on each [[Cultist]] enemy in play. If there are no [[Cultist]] enemies in play, reveal another token.\n\n[elder_thing]: -3. If you fail, move all doom from the [[Cultist]] enemy with the most doom on it to the current agenda. If no [[Cultist]] enemies in play have doom on them, reveal another&nbsp;token."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the highest number of doom on a [[Cultist]] enemy in play.\n\n[cultist]: -2. Reveal another token.\n\n[tablet]: -3. If you fail, place 1 doom on the nearest [[Cultist]] enemy.\n\n[elder_thing]: -3. If you fail, move 1 doom from the nearest [[Cultist]] enemy to the current agenda."
end
end
--Union and Disillusion
if scenario.getDescription() == "Union and Disillusion" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -3. If this is a skill test during a <b>circle</b> action, reveal another token.\n\n[cultist]: -4. If you have no damage on you, take 1 damage. If you have no horror on you, take 1 horror.\n\n[tablet]: -4. If you fail, a [[Spectral]] enemy at your location attacks you <i>(even if it is exhausted).</i>\n\n[elder_thing]: -4. If this is a skill test during a <b>circle</b> action and you fail, resolve each haunted ability on your location."
else
tool = "Easy / Standard\n\n[skull]: -2. If this is a skill test during a <b>circle</b> action, reveal another token.\n\n[cultist]: -3. If you have no damage on you, take 1 damage. If you have no horror on you, take 1 horror.\n\n[tablet]: -3. If you fail, a [[Spectral]] enemy at your location attacks you <i>(even if it is exhausted).</i>\n\n[elder_thing]: -3. If this is a skill test during a <b>circle</b> action and you fail, resolve each haunted ability on your location."
end
end
--Clutches of Chaos
if scenario.getDescription() == "In the Clutches of Chaos" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 higher than the total amount of doom and breaches on your location.\n\n[cultist]: Reveal another token. If there are fewer than 3 breaches on your location, place 1 breach on your location.\n\n[tablet]: -3. For each point you fail by, remove 1 breach from the current act.\n\n[elder_thing]: -4. If you fail, place 1 breach on a random location."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the total amount of doom and breaches on your location.\n\n[cultist]: Reveal another token. If there are fewer than 3 breaches on your location, place 1 breach on your location.\n\n[tablet]: -2. For each point you fail by, remove 1 breach from the current act.\n\n[elder_thing]: -3. If you fail, place 1 breach on a random location."
end
end
--Before the Black Throne
if scenario.getDescription() == "Before the Black Throne" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the amount of doom on Azathoth, to a minimum of 2.\n\n[cultist]: Reveal another token. If you fail, search the encounter deck and discard pile for a [[Cultist]] enemy and draw it. Shuffle the encounter deck.\n\n[tablet]: -3. If you fail, Azathoth attacks you.\n\n[elder_thing]: -6. If your modified skill value for this test is 0, place 1 doom on Azathoth."
else
tool = "Easy / Standard\n\n[skull]: -X. X is half of the doom on Azathoth (rounded up), to a minimum of 2.\n\n[cultist]: Reveal another token. If you fail, search the encounter deck and discard pile for a [[Cultist]] enemy and draw it. Shuffle the encounter deck.\n\n[tablet]: -2. If you fail, Azathoth attacks you.\n\n[elder_thing]: -4. If your modified skill value for this test is 0, place 1 doom on Azathoth."
end
end
--Beyond the gates of sleep
if scenario.getDescription() == "Beyond the Gates of Sleep" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull] : -X. X is the number of cards in your hand.\n\n[cultist]: -X. X is the number of revealed [[Woods]] locations.\n\n[tablet]: -2. If this is an attack or evasion attempt against a swarming enemy, add 1 swarm card to it."
else
tool = "Easy / Standard\n\n[skull] : -X. X is half the number of cards in your hand (rounded up).\n\n[cultist]: -X. X is the number of revealed Enchanted Woods locations.\n\n[tablet]: -2. If you fail and this is an attack or evasion attempt against a swarming enemy, add 1 swarm card to it."
end
end
--Waking Nightmare
if scenario.getDescription() == "Waking Nightmare" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if you are engaged with a [[Staff]] enemy).\n\n[cultist]: Reveal another chaos token. If it is agenda 2 or 3, make an infestation test.\n\n[elder_thing]: -X. X is 1 higher than the number of infested locations."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you are engaged with a [[Staff]] enemy).\n\n[cultist]: Reveal another chaos token. If you fail and it is agenda 2 or 3, make an infestation test.\n\n[elder_thing]: -X. X is the number of infested locations."
end
end
--The search for kadath
if scenario.getDescription() == "The Search for Kadath" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 more than the number of Signs of the Gods the investigators have uncovered.\n\n[cultist]: Reveal another token. If this token is revealed during an investigation and this skill test fails, increase that location's shroud by 2 for the remainder of the round.\n\n[tablet]: -3. If you fail, either take 1 damage and 1 horror, or place 1 doom on the current agenda.\n\n[elder_thing]: +1. The black cat points you in the right direction. If this token is revealed during an investigation and you succeed, discover 1 additional clue."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the number of Signs of the Gods the investigators have uncovered.\n\n[cultist]: Reveal another token. If this token is revealed during an investigation and this skill test fails, increase that location's shroud by 1 for the remainder of the round.\n\n[tablet]: -2. If you fail, either take 1 damage and 1 horror, or place 1 doom on the current agenda.\n\n[elder_thing]: +2. The black cat points you in the right direction. If this token is revealed during an investigation and you succeed, discover 1 additional clue."
end
end
--A thousand shapes of horror
if scenario.getDescription() == "A Thousand Shapes of Horror" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if you are at a [[Graveyard]] location).\n\n[cultist]: Reveal another token. If you fail and The Unnamable is in play, it attacks you (regardless of its current location).\n\n[tablet]: +1. The black cat causes a distraction. If this test is successful, choose and evade an enemy at any location with a fight value of X or lower, where X is the amount you succeeded by.\n\n[elder_thing]: -3. If you fail, you must either place 1 of your clues on your location or take 1 damage."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if you are at a [[Graveyard]] location).\n\n[cultist]: Reveal another token. If you fail and The Unnamable is in play, it attacks you (regardless of its current location).\n\n[tablet]: +2. The black cat causes a distraction. If this test is successful, choose and evade an enemy at any location with a fight value of X or lower, where X is the amount you succeeded by.\n\n[elder_thing]: -2. If you fail, you must either place 1 of your clues on your location or take 1 damage."
end
end
--Dark Side of the moon
if scenario.getDescription() == "Dark Side of the Moon" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is your alarm level.\n\n[cultist]: Reveal another token. If you fail and your alarm level is higher than your modified skill value, after this skill test ends, draw the top card of the encounter deck.\n\n[tablet]: -2. If you fail, raise your alarm level by 1.\n\n[elder_thing]: 0. The black cat summons several other cats to help. If this token is revealed during an evasion attempt and you succeed, deal 2 damage to the evaded enemy."
else
tool = "Easy / Standard\n\n[skull]: -X. X is half your alarm level (rounded up).\n\n[cultist]: Reveal another token. If you fail and your alarm level is higher than your modified skill value, after this skill test ends, draw the top card of the encounter deck.\n\n[tablet]: -1. If you fail, raise your alarm level by 1.\n\n[elder_thing]: +1. The black cat summons several other cats to help. If this token is revealed during an evasion attempt and you succeed, deal 2 damage to the evaded enemy."
end
end
--point of no return
if scenario.getDescription() == "Point of No Return" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 more than the amount of damage on this card.\n\n[cultist]: Reveal another token. If you fail, after this skill test ends, draw the top card of the encounter deck.\n\n[tablet]: 0. The black cat helps you navigate through the death-fire. If this token is revealed during an investigation and you succeed, draw 1 card.\n\n[elder_thing]: -4. If you fail by 2 or more, choose a ready enemy at your location or a connecting location. That enemy moves to your location, engages you, and makes an immediate attack."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the amount of damage on this card.\n\n[cultist]: Reveal another token. If you fail, after this skill test ends, draw the top card of the encounter deck.\n\n[tablet]: +1. The black cat helps you navigate through the death-fire. If this token is revealed during an investigation and you succeed, draw 1 card.\n\n[elder_thing]: -3. If you fail by 2 or more, choose a ready enemy at your location or a connecting location. That enemy moves to your location, engages you, and makes an immediate attack."
end
end
--where the gods dwell
if scenario.getDescription() == "Where the Gods Dwell" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the number of the current act plus the number of the current agenda.\n\n[cultist]: Reveal another token. If you fail, place 1 doom on the current agenda. This effect may cause the current agenda to advance.\n\n[tablet]: -6. If you fail, choose and reveal a copy of Nyarlathotep in your hand. It attacks you and is shuffled into the encounter deck.\n\n[elder_thing]: -1. The black cat reminds you that it's all a dream."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the number of the current act.\n\n[cultist]: Reveal another token. If you fail, place 1 doom on the current agenda.\n\n[tablet]: -4. If you fail, choose and reveal a copy of Nyarlathotep in your hand. It attacks you and is shuffled into the encounter deck.\n\n[elder_thing]: 0. The black cat reminds you that it's all a dream."
end
end
--weaver of the cosmos
if scenario.getDescription() == "Weaver of the Cosmos" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is the amount of doom on locations in play.\n\n[cultist]: Reveal another token. If you fail, and there is an [[Ancient One]] enemy at your location, it attacks you.\n\n[tablet]: -1. The black cat tears at the web with its claws. If you succeed by 2 or more, remove 1 doom from your location.\n\n[elder_thing]: -4. If this skill test fails during an attack against a [[Spider]] enemy, place 1 doom on that enemy's location."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the highest amount of doom on a location in play.\n\n[cultist]: Reveal another token. If you fail, and there is an [[Ancient One]] enemy at your location, it attacks you.\n\n[tablet]: 0. The black cat tears at the web with its claws. If you succeed by 2 or more, remove 1 doom from your location.\n\n[elder_thing]: -3. If this skill test fails during an attack against a [[Spider]] enemy, place 1 doom on that enemy's location."
end
end
--pit of despair
if scenario.getDescription() == "The Pit of Despair" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-3 instead if your location is partially flooded; -4 instead if your location is fully flooded).\n\n[cultist]: -2. If your location is flooded, take 1 damage.\n\n[tablet]: -2. If you control a key, take 1 horror.\n\n[elder_thing]: -3. If The Amalgam is in the depths, put it into play engaged with you."
else
tool = "Easy / Standard\n\n[skull]: -1 (-2 instead if your location is partially flooded; -3 instead if your location is fully flooded).\n\n[cultist]: -2. If you fail and your location is flooded, take 1 damage.\n\n[tablet]: -2. If you fail and you control a key, take 1 horror.\n\n[elder_thing]: -3. If you fail and The Amalgam is in the depths, put it into play engaged with you."
end
end
--vanishing of elena harper
if scenario.getDescription() == "The Vanishing of Elina Harper" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 more than the current agenda number.\n\n[cultist]: -2. Place 1 doom on the nearest enemy (2 doom instead if you failed).\n\n[tablet]: -3. Take 1 horror (1 horror and 1 damage instead if you failed).\n\n[elder_thing]: -4. Place 1 of your clues on your location (2 clues instead if you failed)."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the current agenda number.\n\n[cultist]: -2. If you fail, place 1 doom on the nearest enemy.\n\n[tablet]: -3. If you fail, take 1 horror.\n\n[elder_thing]: -4. If you fail, place 1 of your clues on your location."
end
end
--in too deep
if scenario.getDescription() == "In Too Deep" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 for each location to the east of your location (on the same row).\n\n[cultist]: -4. If you fail, move to the connecting location to the east, ignoring all barriers.\n\n[tablet]: -5. If you fail, choose a connecting location with no barriers between it and your location. Place 1 barrier between the two locations.\n\n[elder_thing]: -X. X is twice the number of barriers between your location and all connecting locations."
else
tool = "Easy / Standard\n\n[skull]: -1 for each location to the east of your location (on the same row).\n\n[cultist]: -2. If you fail, move to the connecting location to the east, ignoring all barriers.\n\n[tablet]: -3. If you fail, choose a connecting location with no barriers between it and your location. Place 1 barrier between the two locations.\n\n[elder_thing]: -X. X is the number of barriers between your location and all connecting locations."
end
end
--devil reef
if scenario.getDescription() == "Devil Reef" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -X. X is 1 more than the number of keys the investigators control.\n\n[cultist]: -3. If this is an attack or evasion attempt against a [[Deep One]] enemy, it engages you. (If it is already engaged with you, it disengages first, then re-engages you.)\n\n[tablet]: -4. If you are not in a vehicle, take 1 damage.\n\n[elder_thing]: -5. If your location has a key on it, take 1 horror."
else
tool = "Easy / Standard\n\n[skull]: -X. X is the number of keys the investigators control.\n\n[cultist]: -2. If you fail and this is an attack or evasion attempt against a [[Deep One]] enemy, it engages you. (If it is already engaged with you, it disengages first, then re-engages you.)\n\n[tablet]: -3. If you fail and you are not in a vehicle, take 1 damage.\n\n[elder_thing]: -4. If you fail and your location has a key on it, take 1 horror."
end
end
--horror in high gear
if scenario.getDescription() == "Horror in High Gear" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if there are 6 or fewer locations remaining in the Road deck).\n\n[cultist]: -2. For each point you fail by, an investigator in your vehicle places 1 of their clues on your location.\n\n[tablet]: -3. For each point you fail by, an investigator in your vehicle loses 1 resource.\n\n[elder_thing]: -4. Resolve the hunter keyword on each enemy in play."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if there are 6 or fewer locations remaining in the Road deck).\n\n[cultist]: -1. For each point you fail by, an investigator in your vehicle places 1 of their clues on your location.\n\n[tablet]: -2. For each point you fail by, an investigator in your vehicle loses 1 resource.\n\n[elder_thing]: -4. If you fail, resolve the hunter keyword on each enemy in play."
end
end
--light in the fog
if scenario.getDescription() == "A Light in the Fog" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2. If your location is flooded, reveal an additional chaos token.\n\n[cultist]: -2. If you fail, after this test ends, increase the flood level of your location (if you cannot, take 1 horror instead).\n\n[tablet]: -3. If you fail this test and your location is flooded, take 2 damage.\n\n[elder_thing]: -4. Move the nearest unengaged enemy once toward your location. It loses aloof during this movement."
else
tool = "Easy / Standard\n\n[skull]: -1. If your location is flooded, reveal an additional chaos token.\n\n[cultist]: -2. If you fail, after this test ends, increase the flood level of your location.\n\n[tablet]: -3. If you fail this test and your location is flooded, take 1 damage.\n\n[elder_thing]: -4. If you fail, move the nearest ready unengaged enemy once toward your location. It loses aloof during this movement."
end
end
--lair of dagon
if scenario.getDescription() == "The Lair of Dagon" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 for each key on this card.\n\n[cultist]: -2. Reveal an additional chaos token. If you reveal 1 or more [curse] tokens during this test, you automatically fail.\n\n[tablet]: -3. Place each key you control on your location and take 1 damage.\n\n[elder_thing]: -4. Add 2 [curse] tokens to the chaos bag."
else
tool = "Easy / Standard\n\n[skull]: -1 for each key on this card.\n\n[cultist]: 0. Reveal an additional chaos token. If you reveal 1 or more [curse] tokens during this test, you automatically fail.\n\n[tablet]: -3. If you fail, place each key you control on your location.\n\n[elder_thing]: -4. If you fail, add 1 [curse] token to the chaos bag."
end
end
--into the maelstrom
if scenario.getDescription() == "Into the Maelstrom" then
if scenario.is_face_down == true then
tool = "Hard / Expert\n\n[skull]: -2 (-4 instead if there are 4 or more unflooded [[Y'ha-nthlei]] locations in play).\n\n[cultist]: -4. If you fail, place 1 doom on the current agenda (this may cause the current agenda to advance).\n\n[tablet]: -5. If you fail, you must either increase the flood level of your location or take 1 damage.\n\n[elder_thing]: -6. If you fail and there is a key on your location, take 1 horror."
else
tool = "Easy / Standard\n\n[skull]: -1 (-3 instead if there are 4 or more unflooded [[Y'ha-nthlei]] locations in play).\n\n[cultist]: -3. If you fail, place 1 doom on the current agenda (this may cause the current agenda to advance).\n\n[tablet]: -4. If you fail, you must either increase the flood level of your location or take 1 damage.\n\n[elder_thing]: -5. If you fail and there is a key on your location, take 1 horror."
end
end
end
getObjectFromGUID("8b081b").editButton({index = 6, tooltip = tool})
getObjectFromGUID("bd0ff4").editButton({index = 6, tooltip = tool})
getObjectFromGUID("383d8b").editButton({index = 6, tooltip = tool})
getObjectFromGUID("0840d5").editButton({index = 6, tooltip = tool})
end

View File

@ -22,7 +22,7 @@
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/965354846165100486/3DC8FCEF364B30758B09EF96AF9458F2B8E64D56/",
"WidthScale": 0
},
"Description": "click to set chaos token difficulty",
"Description": "Define difficulties in this object's script.",
"DragSelectable": true,
"GMNotes": "",
"GUID": "8112ff",
@ -34,7 +34,7 @@
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScriptState": "",
"LuaScript_path": "Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.ttslua",
"LuaScript_path": "Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.ttslua",
"MeasureMovement": false,
"Name": "Custom_Tile",
"Nickname": "Generic Difficulty Selector",

View File

@ -0,0 +1,38 @@
-- edit the "tokenData" table to change the preset difficulties
-- list of valid ids: 'p1', '0', 'm1', 'm2', 'm3', 'm4', 'm5', 'm6', 'm7', 'm8',
-- 'skull', 'cultist', 'tablet', 'elder', 'red', 'blue', 'bless', 'curse', 'frost'
local tokenData = {
Easy = { 'p1', 'p1', '0', '0', '0', 'm1', 'm1', 'm1', 'm2', 'm2', 'skull', 'skull', 'cultist', 'red', 'blue' },
Standard = { 'p1', '0', '0', 'm1', 'm1', 'm1', 'm2', 'm2', 'm3', 'm4', 'skull', 'skull', 'cultist', 'red', 'blue' },
Hard = { '0', '0', '0', 'm1', 'm1', 'm2', 'm2', 'm3', 'm3', 'm4', 'm5', 'skull', 'skull', 'cultist', 'red', 'blue' },
Expert = { '0', 'm1', 'm1', 'm2', 'm2', 'm3', 'm3', 'm4', 'm4', 'm5', 'm6', 'm8', 'skull', 'skull', 'cultist', 'red', 'blue' }
}
-- create buttons on startup
function onLoad()
local z_offset = -0.15
for difficulty, _ in pairs(tokenData) do
local clickFunction = difficulty:lower() .. "Click"
self.setVar(clickFunction, function() clickFun(difficulty) end)
self.createButton({
label = difficulty,
function_owner = self,
click_function = clickFunction,
position = { 0, 0.1, z_offset },
rotation = { 0, 0, 0 },
scale = { 0.47, 1, 0.47 },
height = 200,
width = 1150,
font_size = 100,
color = { 0.87, 0.8, 0.70 },
font_color = { 0, 0, 0 }
})
z_offset = z_offset + 0.20
end
end
function clickFun(difficulty)
Global.call("setChaosBagState", tokenData[difficulty])
end

View File

@ -1,57 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"Bag": {
"Order": 0
},
"ColorDiffuse": {
"b": 0,
"g": 0.36652,
"r": 0.70588
},
"ContainedObjects_order": [
"GenericDifficultySelector.8112ff",
"TokenImageProvider.162580",
"TokenList.297f5e",
"GenericDifficultySelectorInstructions13.c32992"
],
"ContainedObjects_path": "WhimsicalsGenericDifficultySelector.05efb4",
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "05efb4",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "",
"LuaScriptState": "",
"MaterialIndex": -1,
"MeasureMovement": false,
"MeshIndex": -1,
"Name": "Bag",
"Nickname": "Whimsical's Generic Difficulty Selector",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": 29.073,
"posY": 3.901,
"posZ": -21.285,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,169 +0,0 @@
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by Whimsical.
--- DateTime: 2021-08-05 6:14 p.m.
---
local tags = {
bag = "chaosBag",
provider = "token_list_provider",
tokens = "token_image_provider"
}
---@class ChaosToken
---@field name string
---@field image string
local _ = {}
---@type table<string|number, ChaosToken>
local sources
---@type table<string, any[]>
local tokens
---@param source TTSObject
local LoadSource = function (source)
tokens = source:getTable("chaos_tokens")
end
---@param token ChaosToken
---@param position Vector
---@param chaosBag TTSObject
---@return number
local SpawnToken = function(token, position, chaosBag)
spawnObject {
type = "Custom_Tile",
position = position,
scale = { 0.81, 1.0, 0.81 },
rotation = { 0, 270, 0 },
---@param object TTSObject
callback_function = function(object, _, _)
object:setName(token.name)
chaosBag:putObject(object)
end
}:setCustomObject {
image = token.image,
type = 2,
thickness = 0.1
}
end
---@param chaosBag TTSObject
local emptyBag = function(chaosBag)
local object = chaosBag:getObjects()
local pos = self:getPosition()
pos.y = pos.y+1
for _, object in ipairs(object) do
chaosBag:takeObject {
guid = object.guid,
position = pos,
---@param item TTSObject
callback_function = function (item) item:destruct() end
}
end
end
---@param difficulty string
local clickFun= function (difficulty)
local chaosBag = getObjectsWithTag(tags.bag)[1]
emptyBag(chaosBag)
local loading = tokens[difficulty]
local pos = self:getPosition()
for _, token_id in ipairs(loading) do
if type(token_id)=="string" then token_id = token_id:lower() end
local token = sources[token_id]
if (token==nil) then
error("Could not find token \"" .. token_id .. "\".")
return
end
SpawnToken(token, pos, chaosBag)
end
end
---@param difficulty string
local MakeClickFun = function(difficulty)
return function ()
clickFun(difficulty)
end
end
---@param label string
---@param z_offset number|nil
local makeButton = function(label , z_offset)
z_offset = z_offset or -0.15
_G[label:lower() .. "Click"] = MakeClickFun(label)
self:createButton({
label = label,
function_owner = self,
click_function = label:lower() .. "Click",
position = {0, 0.1, z_offset},
rotation = {0, 0, 0},
scale = {0.47, 1, 0.47},
height = 200,
width = 1150,
font_size = 100,
color = {0.87, 0.8, 0.70},
font_color = {0, 0, 0}
})
return z_offset + 0.20
end
---@param source TTSObject
local CreateButtons= function(source)
self:clearButtons()
local z_offset
for difficulty,_ in pairs(tokens) do
z_offset = makeButton(difficulty, z_offset)
end
end
function LoadTokens()
---@type TTSObject[]
local image_sources = getObjectsWithTag(tags.tokens)
if (#image_sources<=0) then
error("Cannot find images_sources")
return
end
sources = image_sources[1]:getTable("sources")
if (tokens~=nil) then CreateButtons() return end
local source = getObjectsWithTag(tags.provider)
if (#source<=0) then return end
LoadSource(source[1])
CreateButtons()
end
function onload()
Timer.create {
identifier = self:getGUID(),
function_name = "LoadTokens"
}
end
---@param provider TTSObject
function onObjectSpawn(provider)
if (not provider:hasTag(tags.provider)) then return end
LoadSource(provider)
CreateButtons()
end

View File

@ -1,137 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"Description": "Tool for create custom chaos bag configurations.\n - The actual difficulty selector: click a difficulty to fill the chaos bag.\n- Token Image Provider: edit in scripting editor to add new token types. Cut and Paste to save.\n- Token List: edit in scripting editor to customize difficulties. Cut and Paste to save.\n\nReference of Token codes on following pages:",
"DragSelectable": true,
"GMNotes": "",
"GUID": "c32992",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Notecard",
"Nickname": "Generic Difficulty Selector Instructions 1/3",
"Snap": true,
"States": {
"2": {
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"Description": "1 — -8: Numeric tokens (Don't place quotes around these).\r\n\"S\": Skull.\r\n\"C\": Cultist.\r\n\"T\": Tablet.\r\n\"E\": Elder Thing.\r\n\"Fr\": Frost.",
"DragSelectable": true,
"GMNotes": "",
"GUID": "eab766",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Notecard",
"Nickname": "Generic Difficulty Selector Instructions 2/3",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": 3.29240441,
"posY": 1.33262193,
"posZ": 56.0586357,
"rotX": 359.9792,
"rotY": 89.99884,
"rotZ": 359.983215,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
},
"3": {
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"Description": "\"*\": Elder Sign.\r\n\"F\": Auto-fail.\r\n\"+\": Bless.\r\n\"-\": Curse.",
"DragSelectable": true,
"GMNotes": "",
"GUID": "309295",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScript": "",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Notecard",
"Nickname": "Generic Difficulty Selector Instructions 3/3",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": 3.29240441,
"posY": 1.33262193,
"posZ": 56.0586357,
"rotX": 359.9792,
"rotY": 89.9988556,
"rotZ": 359.983215,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
}
},
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": 13.505,
"posY": 3.625,
"posZ": 30.273,
"rotX": 0,
"rotY": 90,
"rotZ": 0,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,48 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "162580",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScriptState": "",
"LuaScript_path": "Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.ttslua",
"MeasureMovement": false,
"Name": "Checker_white",
"Nickname": "Token Image Provider",
"Snap": true,
"Sticky": true,
"Tags": [
"token_image_provider"
],
"Tooltip": true,
"Transform": {
"posX": 13.194,
"posY": 3.807,
"posZ": 28.835,
"rotX": 0,
"rotY": 270,
"rotZ": 180,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,37 +0,0 @@
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by Whimsical.
--- DateTime: 2021-08-05 6:51 p.m.
---
---@param name string
---@param image string
---@return ChaosToken
local MakeToken = function (name, image)
return {
name = name,
image = image
}
end
sources = {
[1] = MakeToken("+1", "https://i.imgur.com/uIx8jbY.png"),
[0] = MakeToken("0", "https://i.imgur.com/btEtVfd.png"),
[-1] = MakeToken("-1", "https://i.imgur.com/w3XbrCC.png"),
[-2] = MakeToken("-2", "https://i.imgur.com/bfTg2hb.png"),
[-3] = MakeToken("-3", "https://i.imgur.com/yfs8gHq.png"),
[-4] = MakeToken("-4", "https://i.imgur.com/qrgGQRD.png"),
[-5] = MakeToken("-5", "https://i.imgur.com/3Ym1IeG.png"),
[-6] = MakeToken("-6", "https://i.imgur.com/c9qdSzS.png"),
[-7] = MakeToken("-7", "https://i.imgur.com/4WRD42n.png"),
[-8] = MakeToken("-8", "https://i.imgur.com/9t3rPTQ.png"),
["s"] = MakeToken("Skull", "https://i.imgur.com/stbBxtx.png"),
["c"] = MakeToken("Cultist", "https://i.imgur.com/VzhJJaH.png"),
["t"] = MakeToken("Tablet", "https://i.imgur.com/1plY463.png"),
["e"] = MakeToken("Elder Thing", "https://i.imgur.com/ttnspKt.png"),
["*"] = MakeToken("Elder Sign", "https://i.imgur.com/nEmqjmj.png"),
["f"] = MakeToken("Auto-fail", "https://i.imgur.com/lns4fhz.png"),
["+"] = MakeToken("Bless", "http://cloud-3.steamusercontent.com/ugc/1655601092778627699/339FB716CB25CA6025C338F13AFDFD9AC6FA8356/"),
["-"] = MakeToken("Curse", "http://cloud-3.steamusercontent.com/ugc/1655601092778636039/2A25BD38E8C44701D80DD96BF0121DA21843672E/"),
["fr"] = MakeToken("Frost", "http://cloud-3.steamusercontent.com/ugc/1858293462583104677/195F93C063A8881B805CE2FD4767A9718B27B6AE/"),
}

View File

@ -1,49 +0,0 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 0,
"g": 0,
"r": 0
},
"Description": "",
"DragSelectable": true,
"GMNotes": "",
"GUID": "297f5e",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
"LuaScriptState": "",
"LuaScript_path": "Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.ttslua",
"MeasureMovement": false,
"Name": "Checker_black",
"Nickname": "Token List",
"Snap": true,
"Sticky": true,
"Tags": [
"mlc_memory_object",
"token_list_provider"
],
"Tooltip": true,
"Transform": {
"posX": 12.984,
"posY": 4.045,
"posZ": 29.828,
"rotX": 0,
"rotY": 270,
"rotZ": 206,
"scaleX": 1,
"scaleY": 1,
"scaleZ": 1
},
"Value": 0,
"XmlUI": ""
}

View File

@ -1,12 +0,0 @@
---
--- Generated by EmmyLua(https://github.com/EmmyLua)
--- Created by Whimsical.
--- DateTime: 2021-08-05 7:19 p.m.
---
chaos_tokens = {
Easy = {1, 1, 0, 0, -1, -1, -1, -2, -2, "S", "S", "C", "T", "F", "*"},
Standard = {1, 0, 0, -1, -1, -1, -2, -2, -3, -4, "S", "S", "C", "T", "F", "*"},
Hard = {0, 0, -1, -1, -2, -2, -3, -4, -4, -5, "Fr", "Fr", "S", "S", "C", "T", "F", "*"},
Expert = {0, -1, -2, -2, -3, -4, -4, -5, -7, "Fr", "Fr", "Fr", "S", "S", "C", "T", "F", "*"}
}

View File

@ -2,7 +2,6 @@
-- specific setup (different for each playmat)
---------------------------------------------------------
PLAYER_COLOR = "White"
PLAY_ZONE_POSITION = { x = -54.5, y = 4, z = 19 }
PLAY_ZONE_SCALE = { x = 32, y = 5, z = 12 }
TRASHCAN_GUID = "147e80"

View File

@ -2,7 +2,6 @@
-- specific setup (different for each playmat)
---------------------------------------------------------
PLAYER_COLOR = "Orange"
PLAY_ZONE_POSITION = { x = -54.5, y = 4, z = -19 }
PLAY_ZONE_SCALE = { x = 32, y = 5, z = 12 }
TRASHCAN_GUID = "f7b6c8"

View File

@ -2,7 +2,6 @@
-- specific setup (different for each playmat)
---------------------------------------------------------
PLAYER_COLOR = "Green"
PLAY_ZONE_POSITION = { x = -26.5, y = 4, z = 26.5 }
PLAY_ZONE_SCALE = { x = 32, y = 5, z = 12 }
TRASHCAN_GUID = "5f896a"

View File

@ -2,7 +2,6 @@
-- specific setup (different for each playmat)
---------------------------------------------------------
PLAYER_COLOR = "Red"
PLAY_ZONE_POSITION = { x = -26.5, y = 4, z = -26.5 }
PLAY_ZONE_SCALE = { x = 32, y = 5, z = 12 }
TRASHCAN_GUID = "4b8594"

View File

@ -1,30 +1,10 @@
local TOKEN_URL = {
ElderSign = "https://i.imgur.com/nEmqjmj.png",
plusOne = "https://i.imgur.com/uIx8jbY.png",
Zero = "https://i.imgur.com/btEtVfd.png",
minusOne = "https://i.imgur.com/w3XbrCC.png",
minusTwo = "https://i.imgur.com/bfTg2hb.png",
minusThree = "https://i.imgur.com/yfs8gHq.png",
minusFour = "https://i.imgur.com/qrgGQRD.png",
minusFive = "https://i.imgur.com/3Ym1IeG.png",
minusSix = "https://i.imgur.com/c9qdSzS.png",
minusSeven = "https://i.imgur.com/4WRD42n.png",
minusEight = "https://i.imgur.com/9t3rPTQ.png",
Skull = "https://i.imgur.com/stbBxtx.png",
Cultist = "https://i.imgur.com/VzhJJaH.png",
Tablet = "https://i.imgur.com/1plY463.png",
ElderThing = "https://i.imgur.com/ttnspKt.png",
AutoFail = "https://i.imgur.com/lns4fhz.png",
Frost = "http://cloud-3.steamusercontent.com/ugc/1858293462583104677/195F93C063A8881B805CE2FD4767A9718B27B6AE/"
}
local TOKEN_NAMES = {
local TOKEN_IDS = {
-- first row
"plusOne", "Zero", "minusOne", "minusTwo", "minusThree", "minusFour",
"p1", "0", "m1", "m2", "m3", "m4",
-- second row
"minusFive", "minusSix", "minusSeven", "minusEight", "Frost",
"m5", "m6", "m7", "m8", "frost",
-- third row
"ElderSign", "Skull", "Cultist", "Tablet", "ElderThing", "AutoFail"
"blue", "skull", "cultist", "tablet", "elder", "red"
}
local BUTTON_TOOLTIP = {
@ -52,9 +32,6 @@ buttonParameters.color = { 0, 0, 0, 0 }
buttonParameters.width = 300
buttonParameters.height = 300
local UPDATING = false
local tokenArranger
local name
local tokens = {}
@ -76,108 +53,30 @@ function onLoad()
self.createButton(buttonParameters)
end
self.addContextMenuItem("More Information", function()
printToAll("------------------------------", "White")
printToAll("Chaos Bag Manager by Chr1Z", "Orange")
end)
tokenArranger = getObjectFromGUID("022907")
end
-- get chaos bag from scripting zone and description
function getChaosBag()
local chaosbag = nil
local chaosbag_zone = getObjectFromGUID("83ef06")
-- error handling: scripting zone not found
if chaosbag_zone == nil then
printToAll("Zone for chaos bag detection couldn't be found.", "Red")
return nil
end
for _, v in ipairs(chaosbag_zone.getObjects()) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
-- error handling: chaos bag not found
if chaosbag == nil then
printToAll("Chaos bag couldn't be found.", "Red")
end
return chaosbag
end
-- click function for buttons
function buttonClick(index, isRightClick)
chaosbag = getChaosBag()
local tokenId = TOKEN_IDS[index]
-- error handling: chaos bag not found
if chaosbag == nil then return end
if isRightClick then
Global.call("removeChaosToken", tokenId)
else
local tokens = {}
local name = BUTTON_TOOLTIP[index]
local chaosbag = Global.call("findChaosBag")
name = BUTTON_TOOLTIP[index]
tokens = {}
for _, v in ipairs(chaosbag.getObjects()) do
if v.name == name then table.insert(tokens, v.guid) end
end
token = TOKEN_NAMES[index]
if isRightClick then
-- error handling: no matching token found
if #tokens == 0 then
printToAll("No " .. name .. " tokens in the chaos bag.", "Yellow")
return
end
-- remove token
chaosbag.takeObject({
guid = tokens[1],
position = self.getPosition(),
smooth = false,
callback_function = removeCallback
})
else
-- spawn token (only 8 frost tokens allowed)
if token == "Frost" and #tokens == 8 then
if tokenId == "frost" and #tokens == 8 then
printToAll("The maximum of 8 Frost tokens is already in the bag.", "Yellow")
return
end
local obj = spawnObject({
type = 'Custom_Tile',
position = chaosbag.getPosition() + Vector(0, 1, 0),
rotation = { x = 0, y = 260, z = 0 },
callback_function = spawnCallback
})
obj.setCustomObject({
type = 2,
image = TOKEN_URL[token],
thickness = 0.1
})
end
updateTokenArranger()
end
function removeCallback(obj)
printToAll("Removing " .. name .. " token (in bag: " .. #tokens - 1 .. ")", "White")
obj.destruct()
end
function spawnCallback(obj)
obj.scale { 0.81, 1, 0.81 }
obj.setName(name)
Global.call("spawnChaosToken", tokenId)
printToAll("Adding " .. name .. " token (in bag: " .. #tokens + 1 .. ")", "White")
end
function updateTokenArranger()
if tokenArranger and not UPDATING then
UPDATING = true
Wait.time(function()
UPDATING = false
tokenArranger.call("layout")
end, 1.5)
end
end

View File

@ -2,13 +2,10 @@
Cleans up the table for the next scenario in a campaign:
- sets counters to default values (resources and doom) or trauma values (health and sanity, if not disabled) from campaign log
- puts everything on playmats and hands into respective trashcans
- use the IGNORE_TAG to exclude objects from tidying (default: "CleanUpHelper_Ignore")
--]]
- use the IGNORE_TAG to exclude objects from tidying (default: "CleanUpHelper_Ignore")]]
local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi")
-- enable this for debugging
local SHOW_RAYS = false
local playmatApi = require("playermat/PlaymatApi")
-- these objects will be ignored
local IGNORE_GUIDS = {
@ -81,9 +78,7 @@ buttonParameters.function_owner = self
---------------------------------------------------------
function onSave()
return JSON.encode({
options = options
})
return JSON.encode({ options = options })
end
function onLoad(savedData)
@ -213,7 +208,7 @@ function resetSkillTrackers()
if obj ~= nil then
obj.call("updateStats", { 1, 1, 1, 1 })
else
printToAll("Skill tracker number " .. i .. " could not be found.", "Yellow")
printToAll("Skill tracker for " .. COLORS[i] .. " playmat could not be found.", "Yellow")
end
end
end
@ -296,7 +291,7 @@ function discardHands()
for i = 1, 4 do
local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[i])
if trashcan == nil then return end
local hand = Player[COLORS[i]].getHandObjects()
local hand = Player[playmatApi.getPlayerColor(COLORS[i])].getHandObjects()
for j = #hand, 1, -1 do
trashcan.putObject(hand[j])
end
@ -339,7 +334,7 @@ function tidyPlayerMatCoroutine()
local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[i])
if trashcan == nil then
printToAll("Trashcan for " .. COLORS[i] .. " playmat could not be found!", "Red")
return
return 1
end
for _, entry in ipairs(findObjects(i)) do
@ -390,7 +385,7 @@ function findObjects(num)
size = PHYSICS_SCALE[num],
origin = PHYSICS_POSITION[num],
orientation = { 0, PHYSICS_ROTATION[num], 0 },
debug = SHOW_RAYS
debug = false
})
end

View File

@ -1,4 +1,6 @@
local playmatAPI = require("playermat/PlaymatApi")
local playmatApi = require("playermat/PlaymatApi")
-- forward declaration of variables that are used across functions
local matColor, handColor, loopId, hovering
function onLoad()
@ -62,8 +64,8 @@ end
-- updates the matcolor and handcolor variable
function updateColors()
matColor = playmatAPI.getMatColorByPosition(self.getPosition())
handColor = playmatAPI.getHandColor(matColor)
matColor = playmatApi.getMatColorByPosition(self.getPosition())
handColor = playmatApi.getPlayerColor(matColor)
self.setName(handColor .. " Hand Helper")
end
@ -78,7 +80,7 @@ function updateValue(toggle)
if Player[handColor].getHandCount() == 0 then return end
-- get state of "Dream-Enhancing Serum" from playermat and update button label
local des = playmatAPI.isDES(matColor)
local des = playmatApi.isDES(matColor)
if toggle then des = not des end
self.editButton({ index = 1, label = "DES: " .. (des and "✓" or "✗") })
@ -110,5 +112,5 @@ end
-- discards a random non-hidden card from hand
function discardRandom()
playmatAPI.doDiscardOne(matColor)
playmatApi.doDiscardOne(matColor)
end

View File

@ -1,10 +1,7 @@
local playmatAPI = require("playermat/PlaymatApi")
local playmatApi = require("playermat/PlaymatApi")
-- forward declaration of variables that are used across functions
local matColor
local setAsidePosition
local setAsideRotation
local drawDeckPosition
local matColor, handColor, setAsidePosition, setAsideRotation, drawDeckPosition
local quickParameters = {}
quickParameters.function_owner = self
@ -102,10 +99,11 @@ end
-- start the search (change UI, set handCards aside, draw cards)
function startSearch(messageColor, number)
matColor = playmatAPI.getMatColorByPosition(self.getPosition())
matColor = playmatApi.getMatColorByPosition(self.getPosition())
handColor = playmatApi.getPlayerColor(matColor)
-- get draw deck
local drawDeck = playmatAPI.getDrawDeck(matColor)
local drawDeck = playmatApi.getDrawDeck(matColor)
if drawDeck == nil then
printToColor(matColor .. " draw deck could not be found!", messageColor, "Red")
return
@ -121,8 +119,8 @@ function startSearch(messageColor, number)
end
-- get position and rotation for set aside cards
local handData = Player[matColor].getHandTransform()
local handCards = Player[matColor].getHandObjects()
local handData = Player[handColor].getHandTransform()
local handCards = Player[handColor].getHandObjects()
setAsidePosition = handData.position + offset * handData.right
setAsideRotation = { handData.rotation.x, handData.rotation.y + 180, 180 }
@ -136,18 +134,18 @@ function startSearch(messageColor, number)
local object = v.hit_object
if object.tag == "Card" and not object.is_face_down then
object.flip()
Wait.time(function() drawDeck = playmatAPI.getDrawDeck(matColor) end, 1)
Wait.time(function() drawDeck = playmatApi.getDrawDeck(matColor) end, 1)
break
end
end
Wait.time(function() drawDeck.deal(number, matColor) end, 1)
Wait.time(function() drawDeck.deal(number, handColor) end, 1)
searchView()
end
-- place handCards back into deck and optionally shuffle
function endSearch(_, _, isRightClick)
local handCards = Player[matColor].getHandObjects()
local handCards = Player[handColor].getHandObjects()
for i = #handCards, 1, -1 do
handCards[i].setPosition(drawDeckPosition + Vector(0, 6 - i * 0.3, 0))
@ -156,7 +154,7 @@ function endSearch(_, _, isRightClick)
if not isRightClick then
Wait.time(function()
local deck = playmatAPI.getDrawDeck(matColor)
local deck = playmatApi.getDrawDeck(matColor)
if deck ~= nil then
deck.shuffle()
end
@ -168,11 +166,11 @@ function endSearch(_, _, isRightClick)
local obj = v.hit_object
if obj.tag == "Deck" then
Wait.time(function()
obj.deal(#obj.getObjects(), matColor)
obj.deal(#obj.getObjects(), handColor)
end, 1)
break
elseif obj.tag == "Card" then
obj.setPosition(Player[matColor].getHandTransform().position)
obj.setPosition(Player[handColor].getHandTransform().position)
obj.flip()
break
end

View File

@ -374,7 +374,7 @@ end
function onTokenDataChanged(parameters)
local tokenData = parameters.tokenData or {}
local currentScenario = parameters.currentScenario or ""
local useFrontData = parameters.useFrontData or true
local useFrontData = parameters.useFrontData
-- only update if this data is new
local info = currentScenario .. tostring(useFrontData)

View File

@ -24,7 +24,7 @@ do
-- updates the laid out tokens
TokenArrangerApi.layout = function()
callIfExistent("layout")
Wait.time(function() callIfExistent("layout") end, 0.1)
end
return TokenArrangerApi

View File

@ -1,6 +1,7 @@
require("arkhamdb/DeckImporterUi")
require("playercards/PlayerCardSpawner")
local playmatApi = require("playermat/PlaymatApi")
local playAreaApi = require("core/PlayAreaApi")
local arkhamDb = require("arkhamdb/ArkhamDb")
local zones = require("playermat/Zones")
@ -184,7 +185,8 @@ end
---@param deck Object Callback-provided spawned deck object
---@param playerColor String Color of the player to draw the cards to
function deckSpawned(deck, playerColor)
local handPos = Player[playerColor].getHandTransform(1).position -- Only one hand zone per player
local player = Player[playmatApi.getPlayerColor(playerColor)]
local handPos = player.getHandTransform(1).position -- Only one hand zone per player
local deckCards = deck.getData().ContainedObjects
-- Process in reverse order so taking cards out doesn't upset the indexing
for i = #deckCards, 1, -1 do

View File

@ -1,10 +1,5 @@
local tokenArrangerApi = require("accessories/TokenArrangerApi")
local IMAGE_URL = {
Bless = "http://cloud-3.steamusercontent.com/ugc/1655601092778627699/339FB716CB25CA6025C338F13AFDFD9AC6FA8356/",
Curse = "http://cloud-3.steamusercontent.com/ugc/1655601092778636039/2A25BD38E8C44701D80DD96BF0121DA21843672E/"
}
-- common button parameters
local buttonParamaters = {}
buttonParamaters.function_owner = self
@ -13,10 +8,20 @@ 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 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
@ -71,8 +76,7 @@ end
function initializeState()
-- count tokens in the bag
local chaosbag = getChaosBag()
if chaosbag == nil then return end
local chaosbag = Global.call("findChaosBag")
local tokens = {}
for _, v in ipairs(chaosbag.getObjects()) do
if v.name == "Bless" then
@ -101,23 +105,19 @@ function initializeState()
end
function broadcastCount(token)
local count = getTokenCount(token)
local count = formatTokenCount(token)
if count == "(0/0)" then return end
broadcastToAll(token .. " Tokens " .. count, "White")
end
function printStatus(color)
broadcastToColor("Curse Tokens " .. getTokenCount("Curse"), color, "White")
broadcastToColor("Bless Tokens " .. getTokenCount("Bless"), color, "White")
broadcastToColor("Curse Tokens " .. formatTokenCount("Curse"), color, "White")
broadcastToColor("Bless Tokens " .. formatTokenCount("Bless"), color, "White")
end
-- context menu function 1
function doRemove(color)
local chaosbag = getChaosBag()
if chaosbag == nil then
broadcastToAll("Chaos bag not found!", "Red")
return
end
local chaosbag = Global.call("findChaosBag")
-- remove tokens from chaos bag
local count = { Bless = 0, Curse = 0 }
@ -132,11 +132,25 @@ function doRemove(color)
end
end
broadcastToColor("Removed " .. count["Bless"] .. " Bless and " ..
count["Curse"] .. " Curse tokens from the chaos bag.", 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")
-- removing tokens that were 'taken'
local function removeType(type)
doReset(color)
end
-- context menu function 2
function doReset(color)
playerColor = color
numInPlay = { Bless = 0, Curse = 0 }
tokensTaken = { Bless = {}, Curse = {} }
initializeState()
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)
@ -146,24 +160,6 @@ function doRemove(color)
end
end
return count
end
broadcastToColor("Removed " .. removeType("Bless") .. " Bless and " ..
removeType("Curse") .. " Curse tokens from play.", color, "White")
doReset(color)
end
-- context menu function 2
function doReset(color)
-- delete previously pulled out tokens by the token arranger
tokenArrangerApi.deleteCopiedTokens()
playerColor = color
numInPlay = { Bless = 0, Curse = 0 }
tokensTaken = { Bless = {}, Curse = {} }
initializeState()
tokenArrangerApi.layout()
end
---------------------------------------------------------
@ -248,25 +244,35 @@ function callFunctions(token, isRightClick)
if success ~= 0 then tokenArrangerApi.layout() end
end
function getChaosBag()
local zone = getObjectFromGUID("83ef06")
if zone == nil then printToAll("Zone for chaosbag not found!", "Red") return 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] .. ")"
end
local items = zone.getObjects()
local chaosbag = nil
for _, v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
-- 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)
end
-- 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
end
if chaosbag == nil then printToColor("No chaos bag found", playerColor) end
return chaosbag
end
function getTokenCount(type)
if type == nil then type = mode end
return "(" .. (numInPlay[type] - #tokensTaken[type]) .. "/" .. #tokensTaken[type] .. ")"
if not updating then
updating = true
Wait.frames(function()
broadcastCount(param.type)
updating = false
end, 1)
end
end
---------------------------------------------------------
@ -278,39 +284,13 @@ function addToken(type)
printToColor("10 tokens already in play, not adding any.", playerColor)
return 0
end
return spawnToken(type)
end
function spawnToken(type)
local chaosbag = getChaosBag()
if chaosbag == nil then
return 0
end
local pos = chaosbag.getPosition()
local obj = spawnObject({
type = 'Custom_Tile',
position = { pos.x, pos.y + 1, pos.z },
callback_function = function(obj)
obj.setName(type)
chaosbag.putObject(obj)
numInPlay[type] = numInPlay[type] + 1
printToAll("Adding " .. type .. " token " .. getTokenCount(type))
end
})
obj.setCustomObject({
type = 2,
image = IMAGE_URL[type],
thickness = 0.1,
})
obj.scale { 0.81, 1, 0.81 }
printToAll("Adding " .. type .. " token " .. formatTokenCount(type))
return Global.call("spawnChaosToken", type)
end
function takeToken(type, remove)
local chaosbag = getChaosBag()
if chaosbag == nil then
broadcastToAll("Chaos bag not found!", "Red")
return 0
end
local chaosbag = Global.call("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
@ -334,11 +314,11 @@ function takeToken(type, remove)
callback_function = function(obj)
if remove then
numInPlay[type] = numInPlay[type] - 1
printToAll("Removing " .. type .. " token " .. getTokenCount(type))
printToAll("Removing " .. type .. " token " .. formatTokenCount(type))
obj.destruct()
else
table.insert(tokensTaken[type], obj.getGUID())
printToAll("Taking " .. type .. " token " .. getTokenCount(type))
printToAll("Taking " .. type .. " token " .. formatTokenCount(type))
end
end
})
@ -355,12 +335,12 @@ function returnToken(type)
printToColor("Couldn't find token " .. guid .. ", not returning to bag", playerColor)
return 0
end
local chaosbag = getChaosBag()
local chaosbag = Global.call("findChaosBag")
if chaosbag == nil then
return 0
end
chaosbag.putObject(token)
printToAll("Returning " .. type .. " token " .. getTokenCount(type))
printToAll("Returning " .. type .. " token " .. formatTokenCount(type))
end
---------------------------------------------------------
@ -400,7 +380,7 @@ function addMenuOptions(playerColor, hoveredObject)
end
function sealToken(type, playerColor, enemy)
local chaosbag = getChaosBag()
local chaosbag = Global.call("findChaosBag")
if chaosbag == nil then return end
local pos = enemy.getPosition()
@ -414,7 +394,7 @@ function sealToken(type, playerColor, enemy)
Wait.frames(function()
table.insert(sealedTokens[enemy.getGUID()], obj)
table.insert(tokensTaken[type], obj.getGUID())
printToColor("Sealing " .. type .. " token " .. getTokenCount(type), playerColor)
printToColor("Sealing " .. type .. " token " .. formatTokenCount(type), playerColor)
end, 1)
end
})
@ -425,7 +405,7 @@ function sealToken(type, playerColor, enemy)
end
function releaseToken(type, playerColor, enemy)
local chaosbag = getChaosBag()
local chaosbag = Global.call("findChaosBag")
if chaosbag == nil then return end
local tokens = sealedTokens[enemy.getGUID()]
if tokens == nil or #tokens == 0 then return end
@ -438,7 +418,7 @@ function releaseToken(type, playerColor, enemy)
if v == guid then
table.remove(tokensTaken[type], j)
table.remove(tokens, i)
printToColor("Releasing " .. type .. " token" .. getTokenCount(type), playerColor)
printToColor("Releasing " .. type .. " token" .. formatTokenCount(type), playerColor)
return
end
end

View File

@ -0,0 +1,24 @@
do
local BlessCurseManagerApi = {}
local MANAGER_GUID = "5933fb"
-- removes all taken tokens and resets the counts
BlessCurseManagerApi.removeTakenTokensAndReset = function()
local BlessCurseManager = getObjectFromGUID(MANAGER_GUID)
Wait.time(function() BlessCurseManager.call("removeType", "Bless") end, 0.05)
Wait.time(function() BlessCurseManager.call("removeType", "Curse") end, 0.10)
Wait.time(function() BlessCurseManager.call("doReset", "White") end, 0.15)
end
-- updates the internal count (called by cards that seal bless/curse tokens)
BlessCurseManagerApi.sealedToken = function(type, guid)
getObjectFromGUID(MANAGER_GUID).call("sealedToken", { type = type, guid = guid })
end
-- updates the internal count (called by cards that seal bless/curse tokens)
BlessCurseManagerApi.releasedToken = function(type, guid)
getObjectFromGUID(MANAGER_GUID).call("releasedToken", { type = type, guid = guid })
end
return BlessCurseManagerApi
end

View File

@ -1,9 +0,0 @@
-- automatically add correct names to tokens that enter the chaos bag
function filterObjectEnter(obj)
local props = obj.getCustomObject()
if props ~= nil and props.image ~= nil then
obj.setName(Global.call("getTokenName", { url = props.image }))
end
return true
end

View File

@ -34,6 +34,9 @@ local hideTitleSplashWaitFunctionId = nil
local playmatApi = require("playermat/PlaymatApi")
local tokenManager = require("core/token/TokenManager")
local playAreaAPI = require("core/PlayAreaApi")
local mythosAreaApi = require("core/MythosAreaApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi")
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
---------------------------------------------------------
-- data for tokens
@ -47,89 +50,48 @@ TOKEN_DATA = {
clue = {image = "http://cloud-3.steamusercontent.com/ugc/1758068501357164917/1D06F1DC4D6888B6F57124BD2AFE20D0B0DA15A8/", scale = {0.15, 0.15, 0.15}}
}
IMAGE_TOKEN_MAP = {
["https://i.imgur.com/nEmqjmj.png"] = "Elder Sign",
["https://i.imgur.com/uIx8jbY.png"] = "+1",
["https://i.imgur.com/btEtVfd.png"] = "0",
["https://i.imgur.com/w3XbrCC.png"] = "-1",
["https://i.imgur.com/bfTg2hb.png"] = "-2",
["https://i.imgur.com/yfs8gHq.png"] = "-3",
["https://i.imgur.com/qrgGQRD.png"] = "-4",
["https://i.imgur.com/3Ym1IeG.png"] = "-5",
["https://i.imgur.com/c9qdSzS.png"] = "-6",
["https://i.imgur.com/4WRD42n.png"] = "-7",
["https://i.imgur.com/9t3rPTQ.png"] = "-8",
["https://i.imgur.com/stbBxtx.png"] = "Skull",
["https://i.imgur.com/VzhJJaH.png"] = "Cultist",
["https://i.imgur.com/1plY463.png"] = "Tablet",
["https://i.imgur.com/ttnspKt.png"] = "Elder Thing",
["https://i.imgur.com/lns4fhz.png"] = "Auto-fail",
["http://cloud-3.steamusercontent.com/ugc/1655601092778627699/339FB716CB25CA6025C338F13AFDFD9AC6FA8356/"] = "Bless",
["http://cloud-3.steamusercontent.com/ugc/1655601092778636039/2A25BD38E8C44701D80DD96BF0121DA21843672E/"] = "Curse",
["http://cloud-3.steamusercontent.com/ugc/1858293462583104677/195F93C063A8881B805CE2FD4767A9718B27B6AE/"] = "Frost"
ID_URL_MAP = {
['blue'] = {name = "Elder Sign", url = 'https://i.imgur.com/nEmqjmj.png'},
['p1'] = {name = "+1", url = 'https://i.imgur.com/uIx8jbY.png'},
['0'] = {name = "0", url = 'https://i.imgur.com/btEtVfd.png'},
['m1'] = {name = "-1", url = 'https://i.imgur.com/w3XbrCC.png'},
['m2'] = {name = "-2", url = 'https://i.imgur.com/bfTg2hb.png'},
['m3'] = {name = "-3", url = 'https://i.imgur.com/yfs8gHq.png'},
['m4'] = {name = "-4", url = 'https://i.imgur.com/qrgGQRD.png'},
['m5'] = {name = "-5", url = 'https://i.imgur.com/3Ym1IeG.png'},
['m6'] = {name = "-6", url = 'https://i.imgur.com/c9qdSzS.png'},
['m7'] = {name = "-7", url = 'https://i.imgur.com/4WRD42n.png'},
['m8'] = {name = "-8", url = 'https://i.imgur.com/9t3rPTQ.png'},
['skull'] = {name = "Skull", url = 'https://i.imgur.com/stbBxtx.png'},
['cultist'] = {name = "Cultist", url = 'https://i.imgur.com/VzhJJaH.png'},
['tablet'] = {name = "Tablet", url = 'https://i.imgur.com/1plY463.png'},
['elder'] = {name = "Elder Thing", url = 'https://i.imgur.com/ttnspKt.png'},
['red'] = {name = "Auto-fail", url = 'https://i.imgur.com/lns4fhz.png'},
['bless'] = {name = "Bless", url = 'http://cloud-3.steamusercontent.com/ugc/1655601092778627699/339FB716CB25CA6025C338F13AFDFD9AC6FA8356/'},
['curse'] = {name = "Curse", url = 'http://cloud-3.steamusercontent.com/ugc/1655601092778636039/2A25BD38E8C44701D80DD96BF0121DA21843672E/'},
['frost'] = {name = "Frost", url = 'http://cloud-3.steamusercontent.com/ugc/1858293462583104677/195F93C063A8881B805CE2FD4767A9718B27B6AE/'}
}
---------------------------------------------------------
-- data for chaos token stat tracker
---------------------------------------------------------
local maxSquid = 0
MAT_GUID_TO_COLOUR = {
local MAT_GUID_TO_COLOR = {
["Overall"] = "Overall",
["8b081b"] = "White",
["bd0ff4"] = "Orange",
["383d8b"] = "Green",
["0840d5"] = "Red"
}
local personalStats = {
local tokenDrawingStats = {
["Overall"] = {},
["8b081b"] = {},
["bd0ff4"] = {},
["383d8b"] = {},
["0840d5"] = {}
}
local overallStats = {
-- cultist
["https://i.imgur.com/VzhJJaH.png"] = 0,
-- skull
["https://i.imgur.com/stbBxtx.png"] = 0,
-- tablet
["https://i.imgur.com/1plY463.png"] = 0,
-- curse
["http://cloud-3.steamusercontent.com/ugc/1655601092778636039/2A25BD38E8C44701D80DD96BF0121DA21843672E/"] = 0,
-- tentacle
["https://i.imgur.com/lns4fhz.png"] = 0,
-- minus eight
["https://i.imgur.com/9t3rPTQ.png"] = 0,
-- minus seven
["https://i.imgur.com/4WRD42n.png"] = 0,
-- minus six
["https://i.imgur.com/c9qdSzS.png"] = 0,
-- minus five
["https://i.imgur.com/3Ym1IeG.png"] = 0,
-- minus four
["https://i.imgur.com/qrgGQRD.png"] = 0,
-- minus three
["https://i.imgur.com/yfs8gHq.png"] = 0,
-- minus two
["https://i.imgur.com/bfTg2hb.png"] = 0,
-- minus one
["https://i.imgur.com/w3XbrCC.png"] = 0,
-- zero
["https://i.imgur.com/btEtVfd.png"] = 0,
-- plus one
["https://i.imgur.com/uIx8jbY.png"] = 0,
-- elder thing
["https://i.imgur.com/ttnspKt.png"] = 0,
-- bless
["http://cloud-3.steamusercontent.com/ugc/1655601092778627699/339FB716CB25CA6025C338F13AFDFD9AC6FA8356/"] = 0,
-- elder sign
["https://i.imgur.com/nEmqjmj.png"] = 0,
-- frost
["http://cloud-3.steamusercontent.com/ugc/1858293462583104677/195F93C063A8881B805CE2FD4767A9718B27B6AE/"] = 0,
}
---------------------------------------------------------
-- general code
---------------------------------------------------------
@ -270,13 +232,24 @@ end
-- chaos token drawing
---------------------------------------------------------
-- checks scripting zone for chaos bag
-- checks scripting zone for chaos bag (also called by a lot of objects!)
function findChaosBag()
for _, item in ipairs(getObjectFromGUID("83ef06").getObjects()) do
local chaosbag_zone = getObjectFromGUID("83ef06")
-- error handling: scripting zone not found
if chaosbag_zone == nil then
printToAll("Zone for chaos bag detection couldn't be found.", "Red")
return
end
for _, item in ipairs(chaosbag_zone.getObjects()) do
if item.getDescription() == "Chaos Bag" then
return item
end
end
-- error handling: chaos bag not found
printToAll("Chaos bag couldn't be found.", "Red")
end
function returnChaosTokens()
@ -290,22 +263,20 @@ end
-- 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.
-- This method will broadcast a message to all players if the bag is being searched.
-- @return Boolean. True if the bag is manipulated, false if it should be blocked.
---@return Boolean. True if the bag is manipulated, false if it should be blocked.
function canTouchChaosTokens()
for color, searching in pairs(bagSearchers) do
if searching then
broadcastToAll("Someone is searching the chaos bag, can't touch the tokens", "Red")
broadcastToAll("Someone is searching the chaos bag, can't touch the tokens.", "Red")
return false
end
end
return true
end
function drawChaostoken(params)
if not canTouchChaosTokens() then
return
end
-- called by playermats (by the "Draw chaos token" button)
function drawChaosToken(params)
if not canTouchChaosTokens() then return end
local mat = params[1]
local tokenOffset = params[2]
@ -320,6 +291,7 @@ function drawChaostoken(params)
end
chaosTokensLastMat = mat
-- if we have left clicked and have no tokens OR if we have right clicked
if isRightClick or #chaosTokens == 0 then
if #chaosbag.getObjects() == 0 then return end
@ -328,11 +300,18 @@ function drawChaostoken(params)
-- add the token to the list, compute new position based on list length
tokenOffset[1] = tokenOffset[1] + (0.17 * #chaosTokens)
local token = chaosbag.takeObject({
index = 0,
position = mat.positionToWorld(tokenOffset),
rotation = mat.getRotation(),
callback_function = function(obj) trackChaosToken(obj, mat.getGUID()) end
rotation = mat.getRotation()
})
-- get data for token description
local name = token.getName()
local tokenData = mythosAreaApi.returnTokenData().tokenData or {}
local specificData = tokenData[name] or {}
token.setDescription(specificData.description or "")
-- track the chaos token (for stat tracker and future returning)
trackChaosToken(name, mat.getGUID())
chaosTokens[#chaosTokens + 1] = token
return
else
@ -355,65 +334,51 @@ end
-- chaos token stat tracker
---------------------------------------------------------
function trackChaosToken(token, matGUID)
local image = token.getCustomObject().image
overallStats[image] = (overallStats[image] or 0) + 1
personalStats[matGUID][image] = (personalStats[matGUID][image] or 0) + 1
function trackChaosToken(tokenName, matGUID)
tokenDrawingStats["Overall"][tokenName] = (tokenDrawingStats["Overall"][tokenName] or 0) + 1
tokenDrawingStats[matGUID][tokenName] = (tokenDrawingStats[matGUID][tokenName] or 0) + 1
end
-- Left-click: print stats, Right-click: reset stats
function handleStatTrackerClick(_, _, isRightClick)
if isRightClick then
resetChaosTokenStats()
for key, _ in pairs(tokenDrawingStats) do
tokenDrawingStats[key] = {}
end
else
printChaosTokenStats()
end
end
function resetChaosTokenStats()
for key, _ in pairs(overallStats) do
overallStats[key] = 0
end
for playerKey, _ in pairs(personalStats) do
for key, value in pairs(overallStats) do
personalStats[playerKey][key] = value
end
end
end
function printChaosTokenStats()
local squidKing = "Nobody"
printToAll("")
printToAll("Overall Stats")
printToAll("------------------------------")
printNonZeroTokenPairs(overallStats)
printToAll("")
printToAll("Individual Stats")
printToAll("------------------------------")
for matGUID, _ in pairs(personalStats) do
local playerColour = MAT_GUID_TO_COLOUR[matGUID]
local playerSquidCount = personalStats[matGUID]["https://i.imgur.com/lns4fhz.png"] or 0
local playerName = playerColour
if Player[playerColour].seated then
playerName = Player[playerColour].steam_name
end
local maxSquid = 0
local playerColor, playerName
printToAll(playerName .. " Stats", playerColour)
printNonZeroTokenPairs(personalStats[matGUID])
for key, personalStats in pairs(tokenDrawingStats) do
if personalStats ~= {} then
if key == "Overall" then
playerColor = "White"
playerName = "Overall"
else
playerColor = playmatApi.getPlayerColor(MAT_GUID_TO_COLOR[key])
playerName = Player[playerColor].steam_name or playerColor
local playerSquidCount = personalStats["Auto-fail"] or 0
if playerSquidCount > maxSquid then
squidKing = playerName
maxSquid = playerSquidCount
end
end
printToAll(squidKing .. " is an auto-fail magnet.", {255, 0, 0})
end
function printNonZeroTokenPairs(theTable)
for key, value in pairs(theTable) do
if value ~= 0 then
printToAll(IMAGE_TOKEN_MAP[key] .. ': ' .. tostring(value))
printToAll("------------------------------")
printToAll(playerName .. " Stats", playerColor)
for tokenName, value in pairs(personalStats) do
if value then
printToAll(tokenName .. ': ' .. tostring(value))
end
end
end
end
printToAll("------------------------------")
printToAll(squidKing .. " is an auto-fail magnet.", {255, 0, 0})
end
end
---------------------------------------------------------
@ -473,40 +438,23 @@ end
-- called for adding chaos tokens
---@param object object Usually "self"
---@param key string Name of the scenario
---@param mode string diffculty (e.g. "hard" or "expert")
---@param mode string difficulty (e.g. "hard" or "expert")
function fillContainer(args)
chaosbag = findChaosBag()
if chaosbag ~= nil then
local data = getDataValue('modeData', args.key)
if data == nil then return end
local value = data[args.mode]
if value == nil or value.token == nil then return end
local pos = chaosbag.getPosition()
if args.object ~= nil then
pos = args.object.getPosition()
end
local tokenList = {}
-- empty the chaos bag
for _, item in ipairs(chaosbag.getObjects()) do
destroyObject(chaosbag.takeObject({}))
end
for _, token in ipairs(value.token) do
local obj = spawnChaosToken(token, pos)
if obj ~= nil then
chaosbag.putObject(obj)
end
for _, tokenId in ipairs(value.token) do
table.insert(tokenList, tokenId)
end
if value.append ~= nil then
for _, token in ipairs(value.append) do
local obj = spawnChaosToken(token, pos)
if obj ~= nil then
chaosbag.putObject(obj)
end
for _, tokenId in ipairs(value.append) do
table.insert(tokenList, tokenId)
end
end
@ -514,15 +462,14 @@ function fillContainer(args)
if value.random then
local n = #value.random
if n > 0 then
for _, token in ipairs(value.random[math.random(1, n)]) do
local obj = spawnChaosToken(token, pos)
if obj ~= nil then
chaosbag.putObject(obj)
end
for _, tokenId in ipairs(value.random[math.random(1, n)]) do
table.insert(tokenList, tokenId)
end
end
end
setChaosBagState(tokenList)
if value.message then
broadcastToAll(value.message)
end
@ -530,7 +477,6 @@ function fillContainer(args)
if value.warning then
broadcastToAll(value.warning, { 1, 0.5, 0.5 })
end
end
end
function getDataValue(storage, key)
@ -554,52 +500,112 @@ function getDataValue(storage, key)
end
end
function spawnChaosToken(id, pos)
local url = getChaosTokenImageURL(id)
if url ~= '' then
local obj = spawnObject({
-- respawns the chaos bag with a new state of tokens
---@param tokenList Table List of chaos token ids
function setChaosBagState(tokenList)
if not canTouchChaosTokens() then return end
local chaosbag = findChaosBag()
local chaosbagData = chaosbag.getData()
local reserveData = getObjectFromGUID("106418").getData()
local tokenCache = {}
local containedObjects = {}
-- create a temporary copy of the data for each chaos token
for _, objData in ipairs(reserveData.ContainedObjects) do
tokenCache[objData.Nickname] = objData
end
-- iterate over tokenlist and insert specified tokens into new table
for _, tokenId in ipairs(tokenList) do
local tokenName = ID_URL_MAP[tokenId].name
table.insert(containedObjects, tokenCache[tokenName])
end
-- overwrite chaos bag content and respawn it
chaosbagData.ContainedObjects = containedObjects
chaosbag.destruct()
spawnObjectData({data = chaosbagData})
-- remove tokens that are still in play
for _, token in pairs(chaosTokens) do
if token ~= nil then token.destruct() end
end
chaosTokens = {}
chaosTokensLastMat = nil
-- reset bless / curse manager
blessCurseManagerApi.removeTakenTokensAndReset()
printToAll("Chaos bag set to chosen difficulty.", "Green")
end
-- spawns the specified chaos token and puts it into the chaos bag
---@param id String ID of the chaos token
function spawnChaosToken(id)
if not canTouchChaosTokens() then return end
id = id:lower()
local chaosbag = findChaosBag()
local url = ID_URL_MAP[id].url or ""
if url ~= "" then
return spawnObject({
type = 'Custom_Tile',
position = {pos.x, pos.y + 3, pos.z},
rotation = {0, 260, 0}
})
obj.setCustomObject({
position = { 0.49, 3, 0 },
scale = { 0.81, 1.0, 0.81 },
rotation = {0, 270, 0},
callback_function = function(obj)
obj.setName(ID_URL_MAP[id].name)
chaosbag.putObject(obj)
tokenArrangerApi.layout()
end
}).setCustomObject({
type = 2,
image = url,
thickness = 0.1
})
obj.scale {0.81, 1, 0.81}
obj.setName(getTokenName({ url=url }))
return obj
end
end
-- chaos bag needs this for renaming chaos tokens
function getTokenName(params)
local name = IMAGE_TOKEN_MAP[params.url]
if name == nil then name = "" end
return name
-- removes the specified chaos token from the chaos bag
---@param id String ID of the chaos token
function removeChaosToken(id)
if not canTouchChaosTokens() then return end
local tokens = {}
local chaosbag = findChaosBag()
local name = ID_URL_MAP[id].name
for _, v in ipairs(chaosbag.getObjects()) do
if v.name == name then table.insert(tokens, v.guid) end
end
-- error handling: no matching token found
if #tokens == 0 then
printToAll("No " .. name .. " tokens in the chaos bag.", "Yellow")
return
end
chaosbag.takeObject({
guid = tokens[1],
smooth = false,
callback_function = function(obj)
obj.destruct()
tokenArrangerApi.layout()
end
})
printToAll("Removing " .. name .. " token (in bag: " .. #tokens - 1 .. ")", "White")
end
-- returns the image url for a chaos token (identified by the "id")
function getChaosTokenImageURL(id)
if id == 'p1' then return 'https://i.imgur.com/uIx8jbY.png' end
if id == '0' then return 'https://i.imgur.com/btEtVfd.png' end
if id == 'm1' then return 'https://i.imgur.com/w3XbrCC.png' end
if id == 'm2' then return 'https://i.imgur.com/bfTg2hb.png' end
if id == 'm3' then return 'https://i.imgur.com/yfs8gHq.png' end
if id == 'm4' then return 'https://i.imgur.com/qrgGQRD.png' end
if id == 'm5' then return 'https://i.imgur.com/3Ym1IeG.png' end
if id == 'm6' then return 'https://i.imgur.com/c9qdSzS.png' end
if id == 'm7' then return 'https://i.imgur.com/4WRD42n.png' end
if id == 'm8' then return 'https://i.imgur.com/9t3rPTQ.png' end
if id == 'skull' then return 'https://i.imgur.com/stbBxtx.png' end
if id == 'cultist' then return 'https://i.imgur.com/VzhJJaH.png' end
if id == 'tablet' then return 'https://i.imgur.com/1plY463.png' end
if id == 'elder' then return 'https://i.imgur.com/ttnspKt.png' end
if id == 'red' then return 'https://i.imgur.com/lns4fhz.png' end
if id == 'blue' then return 'https://i.imgur.com/nEmqjmj.png' end
if id == 'frost' then return 'http://cloud-3.steamusercontent.com/ugc/1858293462583104677/195F93C063A8881B805CE2FD4767A9718B27B6AE/' end
return ''
-- empty the chaos bag
function emptyChaosBag()
if not canTouchChaosTokens() then return end
local chaosbag = findChaosBag()
for _, object in ipairs(chaosbag.getObjects()) do
chaosbag.takeObject({callback_function = function(item) item.destruct() end})
end
end
---------------------------------------------------------

View File

@ -62,7 +62,10 @@ function onCollisionEnter(collisionInfo)
-- trigger update if a change was detected and push new data
if updateNeeded then
local metadata = JSON.decode(object.getGMNotes()) or {}
if not metadata["tokens"] then return end
if not metadata["tokens"] then
tokenData = {}
return
end
tokenData = metadata["tokens"][(useFrontData and "front" or "back")]
fireTokenDataChangedEvent()
end

View File

@ -0,0 +1,219 @@
--[[ Library for cards that seal tokens
This file is used to add sealing option to cards' context menu.
Valid options (set before requiring this file):
UPDATE_ON_HOVER --@type: boolean
- 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
- example usage: "Unrelenting" (to only display valid tokens)
SHOW_SINGLE_RELEASE --@type: boolean
- enables an entry in the context menu
- this entry allows releasing a single token
- example usage: "Holy Spear" (to keep the other tokens and just release one)
SHOW_MULTI_RELEASE --@type: number (amount of tokens to release at once)
- enables an entry in the context menu
- this enty allows releasing of multiple tokens at once
- example usage: "Nephthys" (to release 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
- example usage: "Holy Spear" (to seal two bless tokens at once)
VALID_TOKENS --@type: table ([tokenName] = true)
- this table defines which tokens should be abled to be sealed
- needs to be defined for each card -> even if empty
- example usage: "The Chthonian Stone"
> VALID_TOKENS = {
> ["Skull"] = true,
> ["Cultist"] = true,
> ["Tablet"] = true,
> ["Elder Thing"] = true,
> }
INVALID_TOKENS --@type: table ([tokenName] = true)
- this table defines which tokens are invalid for sealing
- only needs to be defined if needed
- usually combined with empty "VALID_TOKENS" table
- example usage: "Protective Incantation" (not allowed to seal Auto-fail)
----------------------------------------------------------
Example 1: Crystalline Elder Sign
This card can only seal the "+1" or "Elder Sign" token,
it does not need specific options for multi-sealing or releasing.
Thus it should be implemented like this:
> VALID_TOKENS = {
> ["+1"] = true,
> ["Elder Sign"] = true
> }
> require ("playercards/CardsThatSealTokens") -- includes a space after "require" to not executing bundling
----------------------------------------------------------
Example 2: Holy Spear
This card features the following abilities (just listing the relevant parts):
- releasing a single bless token
- sealing two bless tokens
Thus it should be implemented like this:
> VALID_TOKENS = {
> ["Bless"] = true
> }
> SHOW_SINGLE_RELEASE = true
> SHOW_MULTI_SEAL = 2
> require ("playercards/CardsThatSealTokens") -- includes a space after "require" to not executing bundling
----------------------------------------------------------]]
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi")
local sealedTokens = {}
local ID_URL_MAP = {}
local tokensInBag = {}
function onSave() return JSON.encode(sealedTokens) end
function onLoad(savedData)
sealedTokens = JSON.decode(savedData) or {}
ID_URL_MAP = Global.getTable("ID_URL_MAP")
generateContextMenu()
end
-- builds the context menu
function generateContextMenu()
-- conditional single or multi release options
if SHOW_SINGLE_RELEASE then
self.addContextMenuItem("Release token", releaseOneToken)
elseif SHOW_MULTI_RELEASE then
self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens)
else
self.addContextMenuItem("Release token(s)", releaseAllTokens)
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
if not SHOW_MULTI_SEAL then
self.addContextMenuItem("Seal " .. map.name, function(playerColor)
sealToken(map.name, playerColor)
end)
else
self.addContextMenuItem("Seal " .. SHOW_MULTI_SEAL .. " " .. map.name, function(playerColor)
readBag()
local allowed = true
local notFound
for name, _ in pairs(VALID_TOKENS) do
if (tokensInBag[name] or 0) < SHOW_MULTI_SEAL then
allowed = false
notFound = name
end
end
if allowed then
for i = 1, SHOW_MULTI_SEAL do
sealToken(map.name, playerColor)
end
else
printToColor("Not enough " .. notFound .. " tokens in the chaos bag.", playerColor)
end
end)
end
end
end
end
-- generates a list of chaos tokens that is in the chaos bag
function readBag()
local chaosbag = Global.call("findChaosBag")
tokensInBag = {}
for _, token in ipairs(chaosbag.getObjects()) do
tokensInBag[token.name] = (tokensInBag[token.name] or 0) + 1
end
end
-- native event from TTS - used to update the context menu for cards like "Unrelenting"
function onHover()
if UPDATE_ON_HOVER then
readBag()
self.clearContextMenu()
generateContextMenu()
end
end
-- seals the named token on this card
function sealToken(name, playerColor)
if not Global.call("canTouchChaosTokens") then return end
local chaosbag = Global.call("findChaosBag")
for i, obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position = self.getPosition() + Vector(0, 0.5 + 0.1 * #sealedTokens, 0),
rotation = self.getRotation(),
index = i - 1,
smooth = false,
callback_function = function(token)
local guid = token.getGUID()
table.insert(sealedTokens, guid)
tokenArrangerApi.layout()
if name == "Bless" or name == "Curse" then
blessCurseManagerApi.sealedToken(name, guid)
end
end
})
return
end
end
printToColor(name .. " token not found in chaos bag", playerColor)
end
-- release the last sealed token
function releaseOneToken(playerColor)
if not Global.call("canTouchChaosTokens") then return end
if #sealedTokens == 0 then
printToColor("No sealed token(s) found", playerColor)
else
printToColor("Releasing token", playerColor)
putTokenAway(table.remove(sealedTokens))
end
end
-- release multiple tokens at once
function releaseMultipleTokens(playerColor)
if SHOW_MULTI_RELEASE >= #sealedTokens then
for i = 1, SHOW_MULTI_RELEASE do
releaseOneToken(playerColor)
end
else
printToColor("Not enough " .. name .. " tokens sealed.", playerColor)
end
end
-- releases all sealed tokens
function releaseAllTokens(playerColor)
if not Global.call("canTouchChaosTokens") then return end
if #sealedTokens == 0 then
printToColor("No sealed token(s) found", playerColor)
else
printToColor("Releasing token(s)", playerColor)
for _, guid in ipairs(sealedTokens) do
putTokenAway(guid)
end
sealedTokens = {}
end
end
-- returns the token (referenced by GUID) to the chaos bag
function putTokenAway(guid)
local token = getObjectFromGUID(guid)
if not token then return end
local name = token.getName()
local chaosbag = Global.call("findChaosBag")
chaosbag.putObject(token)
tokenArrangerApi.layout()
if name == "Bless" or name == "Curse" then
blessCurseManagerApi.releasedToken(name, guid)
end
end

View File

@ -1,65 +1,6 @@
VALID_TOKENS = {
["+1"]=true,
["Elder Sign"]=true
["+1"] = true,
["Elder Sign"] = true
}
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if VALID_TOKENS[name] ~= nil then
self.addContextMenuItem("Seal " .. name, function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,5 @@
function onload()
mode = "Curse"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Curse"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,60 +1,5 @@
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Elder Sign"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == "Elder Sign" then
self.addContextMenuItem("Seal Elder Sign", function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,7 @@
function onload()
mode = "Curse"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Curse"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_SINGLE_RELEASE = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,7 @@
function onload()
mode = "Bless"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Bless"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_SINGLE_RELEASE = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,7 @@
function onload()
mode = "Curse"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Curse"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_SINGLE_RELEASE = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,93 +1,8 @@
function onload()
mode = "Bless"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Bless"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal 2 " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_SINGLE_RELEASE = true
SHOW_MULTI_SEAL = 2
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
local indexes = {}
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
table.insert(indexes, obj.index)
end
end
if #indexes < 2 then
printToColor("Fewer than 2 " .. name .. " tokens in bag", playerColor)
return
end
table.sort(indexes)
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=indexes[#indexes],
smooth=false,
callback_function=_sealToken
})
chaosbag.takeObject({
position={ pos.x, pos.y + 2, pos.z },
index=indexes[#indexes-1],
smooth=false,
callback_function=_sealToken
})
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,7 @@
function onload()
mode = "Bless"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Bless"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_MULTI_RELEASE = 3
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,75 +1,9 @@
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
readBag()
end
VALID_TOKENS = {}
function readBag(playerColor)
if playerColor ~= nil then
printToColor("Reading chaos bag", playerColor)
end
INVALID_TOKENS = {
["Auto-fail"] = true
}
local tokensInBag = { }
for i,token in ipairs(chaosbag.getObjects()) do
if token.name ~= nil and token.name ~= "" then
tokensInBag[token.name] = true
end
end
UPDATE_ON_HOVER = true
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if tokensInBag[name] and name ~= "Auto-fail" then
self.addContextMenuItem("Seal " .. name, function(playerColor) sealToken(url, playerColor) end)
end
end
self.addContextMenuItem("Read Chaos Bag", readBag)
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,5 @@
function onload()
mode = "Bless"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Bless"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,7 @@
function onload()
mode = "Bless"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Bless"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_SINGLE_RELEASE = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,60 +1,5 @@
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Auto-fail"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == "Auto-fail" then
self.addContextMenuItem("Seal Auto-fail", function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,60 +1,5 @@
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Elder Sign"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == "Elder Sign" then
self.addContextMenuItem("Seal Elder Sign", function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,45 +1,7 @@
function onload()
chaosbag = getChaosBag()
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["0"] = true
}
-- add menu items
self.clearContextMenu()
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == "0" then
self.addContextMenuItem("Seal 0", function(playerColor) sealToken(url, playerColor) end)
end
end
end
SHOW_SINGLE_RELEASE = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,81 +1,7 @@
function onload()
mode = "Bless"
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Bless"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens, true)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == mode then
self.addContextMenuItem("Seal " .. mode, function(playerColor) sealToken(url, playerColor) end, true)
end
end
end
SHOW_SINGLE_RELEASE = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[mode], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Sealing " .. mode .. " token " .. manager.call("getTokenCount"))
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
local token = sealedTokens[#sealedTokens]
if token ~= nil then
local guid = token.getGUID()
chaosbag.putObject(token)
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[mode]) do
if v == guid then
table.remove(tokensTaken[mode], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", mode)
printToAll("Releasing " .. mode .. " token" .. manager.call("getTokenCount"))
end
table.remove(sealedTokens)
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,67 +1,8 @@
VALID_TOKENS = {
Skull=true,
Cultist=true,
Tablet=true,
["Elder Thing"]=true
["Skull"] = true,
["Cultist"] = true,
["Tablet"] = true,
["Elder Thing"] = true,
}
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if VALID_TOKENS[name] ~= nil then
self.addContextMenuItem("Seal " .. name, function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,67 +1,8 @@
VALID_TOKENS = {
Skull=true,
Cultist=true,
Tablet=true,
["Elder Thing"]=true
["Skull"] = true,
["Cultist"] = true,
["Tablet"] = true,
["Elder Thing"] = true,
}
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if VALID_TOKENS[name] ~= nil then
self.addContextMenuItem("Seal " .. name, function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,60 +1,5 @@
function onload()
chaosbag = getChaosBag()
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {
["Elder Sign"] = true
}
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Token", releaseTokens)
for url,name in pairs(IMAGE_TOKEN_MAP) do
if name == "Elder Sign" then
self.addContextMenuItem("Seal Elder Sign", function(playerColor) sealToken(url, playerColor) end)
end
end
end
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
end
function releaseTokens(playerColor)
printToColor("Releasing token", playerColor)
for i,obj in ipairs(sealedTokens) do
chaosbag.putObject(obj)
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
require("playercards/CardsThatSealTokens")

View File

@ -1,97 +1,6 @@
function onload()
chaosbag = getChaosBag()
manager = getObjectFromGUID("5933fb")
sealedTokens = { }
IMAGE_TOKEN_MAP = { }
for i,v in pairs(Global.getVar("IMAGE_TOKEN_MAP")) do
IMAGE_TOKEN_MAP[i] = v
end
VALID_TOKENS = {}
INVALID_TOKENS = {}
readBag()
end
UPDATE_ON_HOVER = true
function sealToken(url, playerColor)
local pos = self.getPosition()
local name = IMAGE_TOKEN_MAP[url]
for i,obj in ipairs(chaosbag.getObjects()) do
if obj.name == name then
chaosbag.takeObject({
position={ pos.x, pos.y + 1, pos.z },
index=i-1,
smooth=false,
callback_function=_sealToken
})
return
end
end
printToColor(name .. " token not found in bag", playerColor)
end
function _sealToken(obj)
table.insert(sealedTokens, obj)
local guid = obj.getGUID()
local name = obj.getName()
if name == "Bless" or name == "Curse" then
local tokensTaken = manager.getVar("tokensTaken")
table.insert(tokensTaken[name], guid)
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", name)
printToAll("Sealing " .. name .. " token " .. manager.call("getTokenCount"))
end
end
function releaseTokens(playerColor)
if #sealedTokens == 0 then return end
for i,token in ipairs(sealedTokens) do
local guid = token.getGUID()
local name = token.getName()
chaosbag.putObject(token)
if name == "Bless" or name == "Curse" then
local tokensTaken = manager.getVar("tokensTaken")
for i,v in ipairs(tokensTaken[name]) do
if v == guid then
table.remove(tokensTaken[name], i)
break
end
end
manager.setVar("tokensTaken", tokensTaken)
manager.setVar("mode", name)
printToAll("Releasing " .. name .. " token" .. manager.call("getTokenCount"))
end
end
sealedTokens = { }
end
function getChaosBag()
local items = getObjectFromGUID("83ef06").getObjects()
local chaosbag = nil
for i,v in ipairs(items) do
if v.getDescription() == "Chaos Bag" then
chaosbag = getObjectFromGUID(v.getGUID())
break
end
end
if chaosbag == nil then printToAll("No chaos bag found") end
return chaosbag
end
function readBag()
-- add menu items
self.clearContextMenu()
self.addContextMenuItem("Release Tokens", releaseTokens)
local bagTokens = { }
local tokens = chaosbag.getObjects()
for i,token in ipairs(tokens) do
bagTokens[token.name] = true
end
for url,token in pairs(IMAGE_TOKEN_MAP) do
if bagTokens[token] then
self.addContextMenuItem("Seal " .. token, function(playerColor) sealToken(url, playerColor) end, true)
end
end
self.addContextMenuItem("Refresh Seal Options", readBag)
end
require("playercards/CardsThatSealTokens")

View File

@ -67,7 +67,7 @@ isDES = false
function onSave()
return JSON.encode({
zoneID = zoneID,
playerColor = PLAYER_COLOR,
playerColor = playerColor,
activeInvestigatorId = activeInvestigatorId,
isDrawButtonVisible = isDrawButtonVisible
})
@ -80,6 +80,7 @@ function onLoad(save_state)
STAT_TRACKER = getObjectFromGUID(STAT_TRACKER_GUID)
RESOURCE_COUNTER = getObjectFromGUID(RESOURCE_COUNTER_GUID)
-- button creation
for i = 1, 6 do
makeDiscardButton(DISCARD_BUTTON_OFFSETS[i], {-3.85, 3, 10.38}, i)
end
@ -94,7 +95,7 @@ function onLoad(save_state)
})
self.createButton({
click_function = "drawChaostokenButton",
click_function = "drawChaosTokenButton",
function_owner = self,
position = {1.85, 0, -0.74},
rotation = {0, -45, 0},
@ -113,10 +114,11 @@ function onLoad(save_state)
font_size = 180
})
-- save state loading
local state = JSON.decode(save_state)
if state ~= nil then
zoneID = state.zoneID
PLAYER_COLOR = state.playerColor
playerColor = state.playerColor
activeInvestigatorId = state.activeInvestigatorId
isDrawButtonVisible = state.isDrawButtonVisible
end
@ -133,15 +135,6 @@ end
-- utility functions
---------------------------------------------------------
function log(message)
if DEBUG then print(message) end
end
-- send messages to player who clicked button if no seated player found
function setMessageColor(color)
messageColor = Player[PLAYER_COLOR].seated and PLAYER_COLOR or color
end
function spawnDeckZone()
spawnObject({
position = self.positionToWorld({-1.4, 0, 0.3 }),
@ -165,7 +158,9 @@ function searchArea(origin, size)
})
end
function doNotReady(card) return card.getVar("do_not_ready") or false end
function doNotReady(card)
return card.getVar("do_not_ready") or false
end
---------------------------------------------------------
-- Discard buttons
@ -231,19 +226,18 @@ end
-- Upkeep button
---------------------------------------------------------
function doUpkeep(_, color, alt_click)
setMessageColor(color)
-- right-click binds to new player color
if alt_click then
PLAYER_COLOR = color
printToColor("Upkeep button bound to " .. color, color)
function doUpkeep(_, clickedByColor, isRightClick)
-- right-click allow color changing
if isRightClick then
changeColor(clickedByColor)
return
end
local forcedLearning = false
-- send messages to player who clicked button if no seated player found
messageColor = Player[playerColor].seated and playerColor or clickedByColor
-- unexhaust cards in play zone, flip action tokens and find forcedLearning
local forcedLearning = false
for _, v in ipairs(searchArea(PLAY_ZONE_POSITION, PLAY_ZONE_SCALE)) do
local obj = v.hit_object
if obj.getDescription() == "Action Token" and obj.is_face_down then
@ -304,7 +298,7 @@ function doUpkeep(_, color, alt_click)
if forcedLearning then
printToColor("Wow, did you really take 'Versatile' to play Patrice with 'Forced Learning'? Choose which draw replacement effect takes priority and draw cards accordingly.", messageColor)
else
local handSize = #Player[PLAYER_COLOR].getHandObjects()
local handSize = #Player[playerColor].getHandObjects()
if handSize < 5 then
local cardsToDraw = 5 - handSize
printToColor("Drawing " .. cardsToDraw .. " cards (Patrice)", messageColor)
@ -328,7 +322,8 @@ end
-- function for "draw 1 button" (that can be added via option panel)
function doDrawOne(_, color)
setMessageColor(color)
-- send messages to player who clicked button if no seated player found
messageColor = Player[playerColor].seated and playerColor or color
drawCardsWithReshuffle(1)
end
@ -351,7 +346,7 @@ function drawCardsWithReshuffle(numCards)
end
if topCard ~= nil then
topCard.deal(numCards, PLAYER_COLOR)
topCard.deal(numCards, playerColor)
numCards = numCards - 1
if numCards == 0 then return end
end
@ -402,7 +397,7 @@ end
function drawCards(numCards)
if drawDeck == nil then return end
drawDeck.deal(numCards, PLAYER_COLOR)
drawDeck.deal(numCards, playerColor)
end
function shuffleDiscardIntoDeck()
@ -415,8 +410,7 @@ end
-- discard a random non-hidden card from hand
function doDiscardOne()
local handColor = getHandColor()
local hand = Player[handColor].getHandObjects()
local hand = Player[playerColor].getHandObjects()
if #hand == 0 then
broadcastToAll("Cannot discard from empty hand!", "Red")
else
@ -444,18 +438,72 @@ function doDiscardOne()
end
end
-- gets the hand color of the closest seated player (by roughly cutting the table into quarters)
function getHandColor()
for _, handColor in ipairs(Player.getAvailableColors()) do
local handPosition = Player[handColor].getHandTransform().position
---------------------------------------------------------
-- color related functions
---------------------------------------------------------
if (PLAYER_COLOR == "White" and handPosition.x < -42 and handPosition.z > 0)
or (PLAYER_COLOR == "Orange" and handPosition.x < -42 and handPosition.z < 0)
or (PLAYER_COLOR == "Green" and handPosition.x > -42 and handPosition.z > 0)
or (PLAYER_COLOR == "Red" and handPosition.x > -42 and handPosition.z < 0) then
return handColor
-- changes the player color
function changeColor(clickedByColor)
local colorList = {
"White",
"Brown",
"Red",
"Orange",
"Yellow",
"Green",
"Teal",
"Blue",
"Purple",
"Pink"
}
-- remove existing colors from the list of choices
for _, existingColor in ipairs(Player.getAvailableColors()) do
for i, newColor in ipairs(colorList) do
if existingColor == newColor then
table.remove(colorList, i)
end
end
end
-- show the option dialog for color selection to the player that triggered this
Player[clickedByColor].showOptionsDialog("Select a new color:", colorList, _, function(color)
local HAND_ZONE_GUIDS = {
"a70eee", -- White
"5fe087", -- Orange
"0285cc", -- Green
"be2f17" -- Red
}
local index
local startPos = self.getPosition()
-- get respective hand zone by position
if startPos.x < -42 then
if startPos.z > 0 then
index = 1
else
index = 2
end
else
if startPos.z > 0 then
index = 3
else
index = 4
end
end
-- update the color of the hand zone
local handZone = getObjectFromGUID(HAND_ZONE_GUIDS[index])
handZone.setValue(color)
-- if the seated player clicked this, reseat him to the new color
if clickedByColor == playerColor then
Player[playerColor].changeColor(color)
end
-- update the internal variable
playerColor = color
end)
end
---------------------------------------------------------
@ -722,8 +770,8 @@ end
-- calls to 'Global' / functions for calls from outside
---------------------------------------------------------
function drawChaostokenButton(_, _, isRightClick)
Global.call("drawChaostoken", {self, DRAWN_CHAOS_TOKEN_OFFSET, isRightClick})
function drawChaosTokenButton(_, _, isRightClick)
Global.call("drawChaosToken", {self, DRAWN_CHAOS_TOKEN_OFFSET, isRightClick})
end
function drawEncountercard(_, _, isRightClick)

View File

@ -43,9 +43,9 @@ do
-- Returns the color of the player's hand that is seated next to the playermat
---@param matColor String Color of the playermat
PlaymatApi.getHandColor = function(matColor)
PlaymatApi.getPlayerColor = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor])
return mat.call("getHandColor")
return mat.getVar("playerColor")
end
-- Returns if there is the card "Dream-Enhancing Serum" on the requested playermat