diff --git a/objects/ChaosBag.fea079.json b/objects/ChaosBag.fea079.json index c754bb79..120a732f 100644 --- a/objects/ChaosBag.fea079.json +++ b/objects/ChaosBag.fea079.json @@ -63,7 +63,7 @@ "IgnoreFoW": false, "LayoutGroupSortIndex": 0, "Locked": false, - "LuaScript": "require(\"chaosbag/ChaosBag\")", + "LuaScript": "", "LuaScriptState": "", "MaterialIndex": -1, "MeasureMovement": false, diff --git a/objects/ChaosTokenReserve.106418.json b/objects/ChaosTokenReserve.106418.json index 909ac287..fde4514a 100644 --- a/objects/ChaosTokenReserve.106418.json +++ b/objects/ChaosTokenReserve.106418.json @@ -18,7 +18,7 @@ "Curse.16a9a7", "Bless.8e3aab", "ElderSign.0b1aca", - "Auto-Fail.e31821", + "Auto-fail.e31821", "ElderThing.38609c", "Tablet.1a1506", "Cultist.7d6103", diff --git a/objects/ChaosTokenReserve.106418/Auto-Fail.e31821.json b/objects/ChaosTokenReserve.106418/Auto-fail.e31821.json similarity index 97% rename from objects/ChaosTokenReserve.106418/Auto-Fail.e31821.json rename to objects/ChaosTokenReserve.106418/Auto-fail.e31821.json index c0d32e93..4825ddbe 100644 --- a/objects/ChaosTokenReserve.106418/Auto-Fail.e31821.json +++ b/objects/ChaosTokenReserve.106418/Auto-fail.e31821.json @@ -37,7 +37,7 @@ "LuaScriptState": "", "MeasureMovement": false, "Name": "Custom_Tile", - "Nickname": "Auto-Fail", + "Nickname": "Auto-fail", "Snap": true, "Sticky": true, "Tooltip": true, diff --git a/objects/Fan-MadeAccessories.aa8b38.json b/objects/Fan-MadeAccessories.aa8b38.json index 635e68be..205119ae 100644 --- a/objects/Fan-MadeAccessories.aa8b38.json +++ b/objects/Fan-MadeAccessories.aa8b38.json @@ -15,8 +15,7 @@ }, "ContainedObjects_order": [ "ArkhamFantasy-PixelArtMini-Cards.e17c9e", - "DrawTokenButtonTooltipRenamer.cc77a8", - "WhimsicalsGenericDifficultySelector.05efb4", + "GenericDifficultySelector.8112ff", "LuckyPenny.2ab443", "Double-SidedResource.bc81cb", "DescriptivePhaseTracker.b171c8", diff --git a/objects/Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.json b/objects/Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.json deleted file mode 100644 index 20e25995..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.json +++ /dev/null @@ -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": "" -} diff --git a/objects/Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.ttslua b/objects/Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.ttslua deleted file mode 100644 index f6c8e24d..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/DrawTokenButtonTooltipRenamer.cc77a8.ttslua +++ /dev/null @@ -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 (from the token pool). 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 (from the token pool). 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 (from the token bank).\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 (from the token bank).\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 (from the token pool) 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 (from the token pool) 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 (from the token pool).\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 (from the token pool).\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 (regardless of her current location).\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 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 circle 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 (even if it is exhausted).\n\n[elder_thing]: -4. If this is a skill test during a circle 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 circle 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 (even if it is exhausted).\n\n[elder_thing]: -3. If this is a skill test during a circle 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 diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.json b/objects/Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.json similarity index 85% rename from objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.json rename to objects/Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.json index 08d5f0b1..72f701d1 100644 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.json +++ b/objects/Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.json @@ -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", @@ -54,4 +54,4 @@ }, "Value": 0, "XmlUI": "" -} +} \ No newline at end of file diff --git a/objects/Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.ttslua b/objects/Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.ttslua new file mode 100644 index 00000000..522cac8a --- /dev/null +++ b/objects/Fan-MadeAccessories.aa8b38/GenericDifficultySelector.8112ff.ttslua @@ -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 diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4.json b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4.json deleted file mode 100644 index 6c44742b..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4.json +++ /dev/null @@ -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": "" -} diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.ttslua b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.ttslua deleted file mode 100644 index 054e3435..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelector.8112ff.ttslua +++ /dev/null @@ -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 -local sources - ----@type table -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 diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelectorInstructions13.c32992.json b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelectorInstructions13.c32992.json deleted file mode 100644 index a1ffa120..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/GenericDifficultySelectorInstructions13.c32992.json +++ /dev/null @@ -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": "" -} diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.json b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.json deleted file mode 100644 index 6da706bc..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.json +++ /dev/null @@ -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": "" -} diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.ttslua b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.ttslua deleted file mode 100644 index 415f1183..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenImageProvider.162580.ttslua +++ /dev/null @@ -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/"), -} diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.json b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.json deleted file mode 100644 index 4d8224b0..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.json +++ /dev/null @@ -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": "" -} diff --git a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.ttslua b/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.ttslua deleted file mode 100644 index 4a1559f2..00000000 --- a/objects/Fan-MadeAccessories.aa8b38/WhimsicalsGenericDifficultySelector.05efb4/TokenList.297f5e.ttslua +++ /dev/null @@ -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", "*"} -} diff --git a/objects/Playermat1White.8b081b.ttslua b/objects/Playermat1White.8b081b.ttslua index b2cd6913..7dd4c4fd 100644 --- a/objects/Playermat1White.8b081b.ttslua +++ b/objects/Playermat1White.8b081b.ttslua @@ -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" diff --git a/objects/Playermat2Orange.bd0ff4.ttslua b/objects/Playermat2Orange.bd0ff4.ttslua index 0d86c537..c30fe5af 100644 --- a/objects/Playermat2Orange.bd0ff4.ttslua +++ b/objects/Playermat2Orange.bd0ff4.ttslua @@ -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" diff --git a/objects/Playermat3Green.383d8b.ttslua b/objects/Playermat3Green.383d8b.ttslua index 17c1a652..35b55b38 100644 --- a/objects/Playermat3Green.383d8b.ttslua +++ b/objects/Playermat3Green.383d8b.ttslua @@ -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" diff --git a/objects/Playermat4Red.0840d5.ttslua b/objects/Playermat4Red.0840d5.ttslua index 024f8cdd..082fc126 100644 --- a/objects/Playermat4Red.0840d5.ttslua +++ b/objects/Playermat4Red.0840d5.ttslua @@ -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" diff --git a/src/accessories/ChaosBagManager.ttslua b/src/accessories/ChaosBagManager.ttslua index a64b2883..96fdced9 100644 --- a/src/accessories/ChaosBagManager.ttslua +++ b/src/accessories/ChaosBagManager.ttslua @@ -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 - - 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 + Global.call("removeChaosToken", tokenId) + else + local tokens = {} + local name = BUTTON_TOOLTIP[index] + local chaosbag = Global.call("findChaosBag") + + for _, v in ipairs(chaosbag.getObjects()) do + if v.name == name then table.insert(tokens, v.guid) end 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) - 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) + Global.call("spawnChaosToken", tokenId) + printToAll("Adding " .. name .. " token (in bag: " .. #tokens + 1 .. ")", "White") end end diff --git a/src/accessories/CleanUpHelper.ttslua b/src/accessories/CleanUpHelper.ttslua index d0b8f3f4..ebcd03ac 100644 --- a/src/accessories/CleanUpHelper.ttslua +++ b/src/accessories/CleanUpHelper.ttslua @@ -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 diff --git a/src/accessories/HandHelper.ttslua b/src/accessories/HandHelper.ttslua index 21e2e586..b72c5ef5 100644 --- a/src/accessories/HandHelper.ttslua +++ b/src/accessories/HandHelper.ttslua @@ -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 diff --git a/src/accessories/SearchAssistant.ttslua b/src/accessories/SearchAssistant.ttslua index efba521e..e1f305c4 100644 --- a/src/accessories/SearchAssistant.ttslua +++ b/src/accessories/SearchAssistant.ttslua @@ -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 diff --git a/src/accessories/TokenArranger.ttslua b/src/accessories/TokenArranger.ttslua index 5f5c955e..2f779006 100644 --- a/src/accessories/TokenArranger.ttslua +++ b/src/accessories/TokenArranger.ttslua @@ -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) diff --git a/src/accessories/TokenArrangerApi.ttslua b/src/accessories/TokenArrangerApi.ttslua index b61aee5f..47afb77f 100644 --- a/src/accessories/TokenArrangerApi.ttslua +++ b/src/accessories/TokenArrangerApi.ttslua @@ -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 diff --git a/src/arkhamdb/DeckImporterMain.ttslua b/src/arkhamdb/DeckImporterMain.ttslua index aa8be95c..66452455 100644 --- a/src/arkhamdb/DeckImporterMain.ttslua +++ b/src/arkhamdb/DeckImporterMain.ttslua @@ -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 diff --git a/src/chaosbag/BlessCurseManager.ttslua b/src/chaosbag/BlessCurseManager.ttslua index d7e64786..a11910a6 100644 --- a/src/chaosbag/BlessCurseManager.ttslua +++ b/src/chaosbag/BlessCurseManager.ttslua @@ -1,9 +1,4 @@ -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/" -} +local tokenArrangerApi = require("accessories/TokenArrangerApi") -- common button parameters local buttonParamaters = {} @@ -12,11 +7,21 @@ buttonParamaters.color = { 0, 0, 0, 0 } buttonParamaters.width = 700 buttonParamaters.height = 700 -local altState = false -local MODE = { [false] = "Add / Remove", [true] = "Take / Return" } -local BUTTON_COLOR = { [false] = { 0.4, 0.4, 0.4 }, [true] = { 0.9, 0.9, 0.9 } } -local FONT_COLOR = { [false] = { 1, 1, 1 }, [true] = { 0, 0, 0 } } -local whitespace = " " +local altState = false +local MODE = { + [false] = "Add / Remove", + [true] = "Take / Return" +} +local BUTTON_COLOR = { + [false] = { 0.4, 0.4, 0.4 }, + [true] = { 0.9, 0.9, 0.9 } +} +local FONT_COLOR = { + [false] = { 1, 1, 1 }, + [true] = { 0, 0, 0 } +} +local whitespace = " " +local updating --------------------------------------------------------- -- creating buttons and menus + initializing tables @@ -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,33 +132,16 @@ function doRemove(color) end end - broadcastToColor("Removed " .. count["Bless"] .. " Bless and " .. - count["Curse"] .. " Curse tokens from the chaos bag.", color, "White") - - -- removing tokens that were 'taken' - local function removeType(type) - local count = 0 - for _, guid in ipairs(tokensTaken[type]) do - local token = getObjectFromGUID(guid) - if token ~= nil then - token.destruct() - count = count + 1 - end - end - return count - end - - broadcastToColor("Removed " .. removeType("Bless") .. " Bless and " .. - removeType("Curse") .. " Curse tokens from play.", color, "White") + broadcastToColor("Removed " .. count.Bless .. " Bless and " .. + count.Curse .. " Curse tokens from the chaos bag.", color, "White") + broadcastToColor("Removed " .. removeTakenTokens("Bless") .. " Bless and " .. + removeTakenTokens("Curse") .. " Curse tokens from play.", color, "White") 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 = {} } @@ -166,6 +149,19 @@ function doReset(color) tokenArrangerApi.layout() end +-- removing tokens that were 'taken' +function removeTakenTokens(type) + local count = 0 + for _, guid in ipairs(tokensTaken[type]) do + local token = getObjectFromGUID(guid) + if token ~= nil then + token.destruct() + count = count + 1 + end + end + return count +end + --------------------------------------------------------- -- click functions --------------------------------------------------------- @@ -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 } + numInPlay[type] = numInPlay[type] + 1 + 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 diff --git a/src/chaosbag/BlessCurseManagerApi.ttslua b/src/chaosbag/BlessCurseManagerApi.ttslua new file mode 100644 index 00000000..0e535b6d --- /dev/null +++ b/src/chaosbag/BlessCurseManagerApi.ttslua @@ -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 diff --git a/src/chaosbag/ChaosBag.ttslua b/src/chaosbag/ChaosBag.ttslua deleted file mode 100644 index a9b40351..00000000 --- a/src/chaosbag/ChaosBag.ttslua +++ /dev/null @@ -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 - diff --git a/src/core/Global.ttslua b/src/core/Global.ttslua index 57792a9c..24c41cf2 100644 --- a/src/core/Global.ttslua +++ b/src/core/Global.ttslua @@ -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,87 +50,46 @@ 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 = { - ["8b081b"] = "White", - ["bd0ff4"] = "Orange", - ["383d8b"] = "Green", - ["0840d5"] = "Red" +local MAT_GUID_TO_COLOR = { + ["Overall"] = "Overall", + ["8b081b"] = "White", + ["bd0ff4"] = "Orange", + ["383d8b"] = "Green", + ["0840d5"] = "Red" } -local personalStats = { - ["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, +local tokenDrawingStats = { + ["Overall"] = {}, + ["8b081b"] = {}, + ["bd0ff4"] = {}, + ["383d8b"] = {}, + ["0840d5"] = {} } --------------------------------------------------------- @@ -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,64 +334,50 @@ 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 + local squidKing = "Nobody" + local maxSquid = 0 + local playerColor, playerName + + 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 -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 - - printToAll(playerName .. " Stats", playerColour) - printNonZeroTokenPairs(personalStats[matGUID]) - - 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)) + local playerSquidCount = personalStats["Auto-fail"] or 0 + if playerSquidCount > maxSquid then + squidKing = playerName + maxSquid = playerSquidCount + end + end + + 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,63 +438,44 @@ 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() + local data = getDataValue('modeData', args.key) + if data == nil then return end - 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 value = data[args.mode] - if value == nil or value.token == nil then return end + local tokenList = {} - local pos = chaosbag.getPosition() - if args.object ~= nil then - pos = args.object.getPosition() + for _, tokenId in ipairs(value.token) do + table.insert(tokenList, tokenId) + end + + if value.append ~= nil then + for _, tokenId in ipairs(value.append) do + table.insert(tokenList, tokenId) end + end - -- 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) + -- randomly choose tokens for specific Carcosa scenarios in standalone + if value.random then + local n = #value.random + if n > 0 then + for _, tokenId in ipairs(value.random[math.random(1, n)]) do + table.insert(tokenList, tokenId) end end + 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 - end - end + setChaosBagState(tokenList) - -- randomly choose tokens for specific Carcosa scenarios in standalone - 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 - end - end - end + if value.message then + broadcastToAll(value.message) + end - if value.message then - broadcastToAll(value.message) - end - - if value.warning then - broadcastToAll(value.warning, { 1, 0.5, 0.5 }) - end + if value.warning then + broadcastToAll(value.warning, { 1, 0.5, 0.5 }) end end @@ -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 --------------------------------------------------------- diff --git a/src/core/MythosArea.ttslua b/src/core/MythosArea.ttslua index 81bbd4f3..a28c42c8 100644 --- a/src/core/MythosArea.ttslua +++ b/src/core/MythosArea.ttslua @@ -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 diff --git a/src/playercards/CardsThatSealTokens.ttslua b/src/playercards/CardsThatSealTokens.ttslua new file mode 100644 index 00000000..aab8aebb --- /dev/null +++ b/src/playercards/CardsThatSealTokens.ttslua @@ -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 diff --git a/src/playercards/cards/CrystallineElderSign3.ttslua b/src/playercards/cards/CrystallineElderSign3.ttslua index ee47beff..f0652701 100644 --- a/src/playercards/cards/CrystallineElderSign3.ttslua +++ b/src/playercards/cards/CrystallineElderSign3.ttslua @@ -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") diff --git a/src/playercards/cards/DarkRitual.ttslua b/src/playercards/cards/DarkRitual.ttslua index 000cd744..540513c1 100644 --- a/src/playercards/cards/DarkRitual.ttslua +++ b/src/playercards/cards/DarkRitual.ttslua @@ -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") diff --git a/src/playercards/cards/DayofReckoning.ttslua b/src/playercards/cards/DayofReckoning.ttslua index 447d6743..78e4493c 100644 --- a/src/playercards/cards/DayofReckoning.ttslua +++ b/src/playercards/cards/DayofReckoning.ttslua @@ -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") diff --git a/src/playercards/cards/FavoroftheMoon1.ttslua b/src/playercards/cards/FavoroftheMoon1.ttslua index 000cd744..a1a7782a 100644 --- a/src/playercards/cards/FavoroftheMoon1.ttslua +++ b/src/playercards/cards/FavoroftheMoon1.ttslua @@ -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") diff --git a/src/playercards/cards/FavoroftheSun1.ttslua b/src/playercards/cards/FavoroftheSun1.ttslua index ab3fe3f1..fe5a27fc 100644 --- a/src/playercards/cards/FavoroftheSun1.ttslua +++ b/src/playercards/cards/FavoroftheSun1.ttslua @@ -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") diff --git a/src/playercards/cards/FluteoftheOuterGods4.ttslua b/src/playercards/cards/FluteoftheOuterGods4.ttslua index 000cd744..a1a7782a 100644 --- a/src/playercards/cards/FluteoftheOuterGods4.ttslua +++ b/src/playercards/cards/FluteoftheOuterGods4.ttslua @@ -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") diff --git a/src/playercards/cards/HolySpear5.ttslua b/src/playercards/cards/HolySpear5.ttslua index d34e5462..aa948b10 100644 --- a/src/playercards/cards/HolySpear5.ttslua +++ b/src/playercards/cards/HolySpear5.ttslua @@ -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") diff --git a/src/playercards/cards/Nephthys4.ttslua b/src/playercards/cards/Nephthys4.ttslua index ab3fe3f1..5d42b3f3 100644 --- a/src/playercards/cards/Nephthys4.ttslua +++ b/src/playercards/cards/Nephthys4.ttslua @@ -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") diff --git a/src/playercards/cards/ProtectiveIncantation1.ttslua b/src/playercards/cards/ProtectiveIncantation1.ttslua index e30935a6..33b6970f 100644 --- a/src/playercards/cards/ProtectiveIncantation1.ttslua +++ b/src/playercards/cards/ProtectiveIncantation1.ttslua @@ -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") diff --git a/src/playercards/cards/RadiantSmite1.ttslua b/src/playercards/cards/RadiantSmite1.ttslua index ab3fe3f1..79219afc 100644 --- a/src/playercards/cards/RadiantSmite1.ttslua +++ b/src/playercards/cards/RadiantSmite1.ttslua @@ -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") diff --git a/src/playercards/cards/RiteofSanctification.ttslua b/src/playercards/cards/RiteofSanctification.ttslua index ab3fe3f1..fe5a27fc 100644 --- a/src/playercards/cards/RiteofSanctification.ttslua +++ b/src/playercards/cards/RiteofSanctification.ttslua @@ -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") diff --git a/src/playercards/cards/SealoftheSeventhSign5.ttslua b/src/playercards/cards/SealoftheSeventhSign5.ttslua index b24b7903..736b14a8 100644 --- a/src/playercards/cards/SealoftheSeventhSign5.ttslua +++ b/src/playercards/cards/SealoftheSeventhSign5.ttslua @@ -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") diff --git a/src/playercards/cards/SerpentsofYig.ttslua b/src/playercards/cards/SerpentsofYig.ttslua index 447d6743..78e4493c 100644 --- a/src/playercards/cards/SerpentsofYig.ttslua +++ b/src/playercards/cards/SerpentsofYig.ttslua @@ -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") diff --git a/src/playercards/cards/ShardsoftheVoid3.ttslua b/src/playercards/cards/ShardsoftheVoid3.ttslua index c935705c..10645275 100644 --- a/src/playercards/cards/ShardsoftheVoid3.ttslua +++ b/src/playercards/cards/ShardsoftheVoid3.ttslua @@ -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") diff --git a/src/playercards/cards/ShieldofFaith2.ttslua b/src/playercards/cards/ShieldofFaith2.ttslua index ab3fe3f1..fe5a27fc 100644 --- a/src/playercards/cards/ShieldofFaith2.ttslua +++ b/src/playercards/cards/ShieldofFaith2.ttslua @@ -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") diff --git a/src/playercards/cards/TheChthonianStone.ttslua b/src/playercards/cards/TheChthonianStone.ttslua index b7aabf11..050c1176 100644 --- a/src/playercards/cards/TheChthonianStone.ttslua +++ b/src/playercards/cards/TheChthonianStone.ttslua @@ -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") diff --git a/src/playercards/cards/TheChthonianStone3.ttslua b/src/playercards/cards/TheChthonianStone3.ttslua index b7aabf11..050c1176 100644 --- a/src/playercards/cards/TheChthonianStone3.ttslua +++ b/src/playercards/cards/TheChthonianStone3.ttslua @@ -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") diff --git a/src/playercards/cards/TheCodexofAges.ttslua b/src/playercards/cards/TheCodexofAges.ttslua index 447d6743..78e4493c 100644 --- a/src/playercards/cards/TheCodexofAges.ttslua +++ b/src/playercards/cards/TheCodexofAges.ttslua @@ -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") diff --git a/src/playercards/cards/Unrelenting1.ttslua b/src/playercards/cards/Unrelenting1.ttslua index 16e20d25..c57efbf4 100644 --- a/src/playercards/cards/Unrelenting1.ttslua +++ b/src/playercards/cards/Unrelenting1.ttslua @@ -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") diff --git a/src/playermat/Playmat.ttslua b/src/playermat/Playmat.ttslua index 98dbb7e2..5a14b196 100644 --- a/src/playermat/Playmat.ttslua +++ b/src/playermat/Playmat.ttslua @@ -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) diff --git a/src/playermat/PlaymatApi.ttslua b/src/playermat/PlaymatApi.ttslua index df49ded4..d8975615 100644 --- a/src/playermat/PlaymatApi.ttslua +++ b/src/playermat/PlaymatApi.ttslua @@ -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