This commit is contained in:
Adam Goldsmith 2024-07-27 21:47:52 -04:00
parent d41b69bf4a
commit e5aea0cf67
703 changed files with 59908 additions and 93546 deletions

File diff suppressed because it is too large Load Diff

View File

@ -628,7 +628,7 @@
<Panel class="doubleColumn-wrapper" <Panel class="doubleColumn-wrapper"
padding="0 17 3 3"/> padding="0 17 3 3"/>
<Button class="optionToggle" <Button class="optionToggle"
image="option-off" image="option_off"
rectAlignment="MiddleRight" rectAlignment="MiddleRight"
offsetXY="-30 0" offsetXY="-30 0"
colors="#FFFFFF|#dfdfdf" colors="#FFFFFF|#dfdfdf"
@ -727,6 +727,21 @@
</Cell> </Cell>
</Row> </Row>
<!-- Option: Enable all card helpers -->
<Row class="option-text"
tooltip="Enable all card helpers (usually enabled via context menu entries).&#xA;Examples: False Covenant and Book of Living Myths">
<Cell class="option-text">
<Panel class="singleColumn-wrapper">
<Text class="option-header">Enable all card helpers</Text>
</Panel>
</Cell>
<Cell class="option-button">
<Button class="optionToggle"
id="enableCardHelpers"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
<!-- Group: play area settings --> <!-- Group: play area settings -->
<Row class="group-header"> <Row class="group-header">
<Cell class="group-header"> <Cell class="group-header">
@ -839,6 +854,21 @@
</Cell> </Cell>
</Row> </Row>
<!-- Option: use class-specific texture -->
<Row class="option-text"
tooltip="Controls whether a class-specific playermat texture should be automatically loaded.">
<Cell class="option-text">
<Panel class="singleColumn-wrapper">
<Text class="option-header">Use class-specific texture</Text>
</Panel>
</Cell>
<Cell class="option-button">
<Button class="optionToggle"
id="useClassTexture"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
<!-- Option: use clickable clue-counters --> <!-- Option: use clickable clue-counters -->
<Row class="option-text" <Row class="option-text"
tooltip="Instead of automatically counting clues in the respective area on your playermat,&#xA;this displays a clickable counter for clues."> tooltip="Instead of automatically counting clues in the respective area on your playermat,&#xA;this displays a clickable counter for clues.">
@ -874,7 +904,7 @@
</Cell> </Cell>
</Row> </Row>
<!-- Option: remove a player mat --> <!-- Option: remove a playermat -->
<Row class="option-text" <Row class="option-text"
tooltip="Remove an unused playermat for more table space.&#xA;Displayed are the default colors."> tooltip="Remove an unused playermat for more table space.&#xA;Displayed are the default colors.">
<Cell class="option-singleColumn"> <Cell class="option-singleColumn">

View File

@ -71,14 +71,14 @@ ComponentTags:
normalized: investigator normalized: investigator
- displayed: chaosBag - displayed: chaosBag
normalized: chaosbag normalized: chaosbag
- displayed: ActionToken
normalized: actiontoken
- displayed: LargeBox - displayed: LargeBox
normalized: largebox normalized: largebox
- displayed: CampaignBox - displayed: CampaignBox
normalized: campaignbox normalized: campaignbox
- displayed: CameraZoom_ignore - displayed: CameraZoom_ignore
normalized: camerazoom_ignore normalized: camerazoom_ignore
- displayed: UniversalToken
normalized: universaltoken
CustomUIAssets: CustomUIAssets:
- Name: close - Name: close
Type: 0 Type: 0
@ -98,10 +98,10 @@ CustomUIAssets:
- Name: option-gear - Name: option-gear
Type: 0 Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2026086584372569912/5CB461AEAE2E59D3064D90A776EB86C46081EC78/ URL: http://cloud-3.steamusercontent.com/ugc/2026086584372569912/5CB461AEAE2E59D3064D90A776EB86C46081EC78/
- Name: option-on - Name: option_on
Type: 0 Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2462982115668997008/2178787B67B3C96F3419EDBAB8420E39893756BC/ URL: http://cloud-3.steamusercontent.com/ugc/2462982115668997008/2178787B67B3C96F3419EDBAB8420E39893756BC/
- Name: option-off - Name: option_off
Type: 0 Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2462982115668996901/D6438ECBB11DECC6DB9987589FF526FBAD4D2368/ URL: http://cloud-3.steamusercontent.com/ugc/2462982115668996901/D6438ECBB11DECC6DB9987589FF526FBAD4D2368/
- Name: font_arkhamicons - Name: font_arkhamicons
@ -122,12 +122,6 @@ CustomUIAssets:
- Name: header_olive - Name: header_olive
Type: 0 Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2280574378889753733/F67B7B37FF7AA253B6D697E577DF54A3E76030C2/ URL: http://cloud-3.steamusercontent.com/ugc/2280574378889753733/F67B7B37FF7AA253B6D697E577DF54A3E76030C2/
- Name: option_on
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2024962321889555728/22ABD35CBB49A001F3A5318E4AFCFB22D24FEA39/
- Name: option_off
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2024962321889555661/6643E5CC9160FF4624672C255D0DF7B313DA00A5/
- Name: SpeechBubble - Name: SpeechBubble
Type: 0 Type: 0
URL: https://i.imgur.com/6MReiEO.png URL: https://i.imgur.com/6MReiEO.png
@ -227,10 +221,13 @@ CustomUIAssets:
- Name: token-auto-fail - Name: token-auto-fail
Type: 0 Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2510267932886739653/CB7AA2D73777EF5938A6E6CD664B2ABA52B6E20A/ URL: http://cloud-3.steamusercontent.com/ugc/2510267932886739653/CB7AA2D73777EF5938A6E6CD664B2ABA52B6E20A/
- Name: token-eldersign
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2540675016035917168/C0D6F531A85FD94C2F54825DFC50781B5B499A1D/
- Name: token-custom-token - Name: token-custom-token
Type: 0 Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650571/E4C2B2B69282A4EE15FE290FF6B08BEFC8FCA65C/ URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650571/E4C2B2B69282A4EE15FE290FF6B08BEFC8FCA65C/
Date: Sun May 12 13:10:46 CEST 2024 Date: Mon Jul 8 20:19:48 CEST 2024
DecalPallet: DecalPallet:
- ImageURL: http://cloud-3.steamusercontent.com/ugc/1474319121424323663/BC5570ECF747F1B30224461B576E8B0FE7FA5F33/ - ImageURL: http://cloud-3.steamusercontent.com/ugc/1474319121424323663/BC5570ECF747F1B30224461B576E8B0FE7FA5F33/
Name: Achivement Checkmark Name: Achivement Checkmark
@ -239,7 +236,7 @@ DecalPallet:
Name: Victory Display Name: Victory Display
Size: 15 Size: 15
Decals: [] Decals: []
EpochTime: 1715512246 EpochTime: 1720462788
GameComplexity: '' GameComplexity: ''
GameMode: Arkham Horror LCG - Super Complete Edition GameMode: Arkham Horror LCG - Super Complete Edition
GameType: '' GameType: ''
@ -290,7 +287,7 @@ Lighting:
LutIndex: 0 LutIndex: 0
ReflectionIntensity: 1 ReflectionIntensity: 1
LuaScript: !include 'unpacked.ttslua' LuaScript: !include 'unpacked.ttslua'
LuaScriptState: '{"acknowledgedUpgradeVersions":[],"chaosTokensGUID":[],"optionPanel":{"cardLanguage":"en","changePlayAreaImage":false,"playAreaConnectionColor":{"a":1,"b":0.4,"g":0.4,"r":0.4},"playAreaConnections":true,"playAreaSnapTags":true,"showAttachmentHelper":false,"showCleanUpHelper":false,"showCYOA":false,"showDisplacementTool":false,"showDrawButton":false,"showHandHelper":false,"showSearchAssistant":false,"showTitleSplash":true,"useClueClickers":false,"useResourceCounters":"disabled","useSnapTags":true}}' LuaScriptState: '{"acknowledgedUpgradeVersions":[],"chaosTokensGUID":[],"optionPanel":{"cardLanguage":"en","changePlayAreaImage":false,"enableCardHelpers":true,"playAreaConnectionColor":{"a":1,"b":0.4,"g":0.4,"r":0.4},"playAreaConnections":true,"playAreaSnapTags":true,"showAttachmentHelper":false,"showCleanUpHelper":false,"showCYOA":false,"showDisplacementTool":false,"showDrawButton":false,"showHandHelper":false,"showSearchAssistant":false,"showTitleSplash":true,"useClassTexture":true,"useClueClickers":false,"useResourceCounters":"disabled","useSnapTags":true}}'
MusicPlayer: MusicPlayer:
AudioLibrary: AudioLibrary:
- Item1: http://cloud-3.steamusercontent.com/ugc/784110538847453001/4481D1CC5684FCF04AB143954DEFE09E94BF5CEB/ - Item1: http://cloud-3.steamusercontent.com/ugc/784110538847453001/4481D1CC5684FCF04AB143954DEFE09E94BF5CEB/
@ -348,11 +345,14 @@ MusicPlayer:
Note: '' Note: ''
ObjectStates: ObjectStates:
- !include 'unpacked/go_game_piece_white GUID Reference Handler 123456.yaml' - !include 'unpacked/go_game_piece_white GUID Reference Handler 123456.yaml'
- !include 'unpacked/go_game_piece_white Game Key Handler fce69c.yaml'
- !include 'unpacked/Checker_white Token Spawn Tracker e3ffc9.yaml' - !include 'unpacked/Checker_white Token Spawn Tracker e3ffc9.yaml'
- !include 'unpacked/HandTrigger 5fe087.yaml' - !include 'unpacked/HandTrigger 5fe087.yaml'
- !include 'unpacked/HandTrigger be2f17.yaml' - !include 'unpacked/HandTrigger be2f17.yaml'
- !include 'unpacked/HandTrigger 0285cc.yaml' - !include 'unpacked/HandTrigger 0285cc.yaml'
- !include 'unpacked/HandTrigger a70eee.yaml' - !include 'unpacked/HandTrigger a70eee.yaml'
- !include 'unpacked/ScriptingTrigger a2f932.yaml'
- !include 'unpacked/FogOfWarTrigger 3aab97.yaml'
- !include 'unpacked/Custom_Assetbundle TableLegBottomRight afc863.yaml' - !include 'unpacked/Custom_Assetbundle TableLegBottomRight afc863.yaml'
- !include 'unpacked/Custom_Assetbundle TableLegBottomLeft c8edca.yaml' - !include 'unpacked/Custom_Assetbundle TableLegBottomLeft c8edca.yaml'
- !include 'unpacked/Custom_Assetbundle TableLegTopLeft 393bf7.yaml' - !include 'unpacked/Custom_Assetbundle TableLegTopLeft 393bf7.yaml'
@ -408,7 +408,9 @@ ObjectStates:
- !include 'unpacked/Custom_Model_Bag Trash 5f896a.yaml' - !include 'unpacked/Custom_Model_Bag Trash 5f896a.yaml'
- !include 'unpacked/Custom_Model_Bag Trash 147e80.yaml' - !include 'unpacked/Custom_Model_Bag Trash 147e80.yaml'
- !include 'unpacked/Custom_Model_Bag Trash f7b6c8.yaml' - !include 'unpacked/Custom_Model_Bag Trash f7b6c8.yaml'
- !include 'unpacked/Custom_Tile Patch Notes f47225.yaml'
- !include 'unpacked/Custom_PDF Rules Reference d99993.yaml' - !include 'unpacked/Custom_PDF Rules Reference d99993.yaml'
- !include 'unpacked/Custom_PDF Latest FAQ faqfaq.yaml'
- !include 'unpacked/Custom_Model_Infinite_Bag Doom tokens 16724b.yaml' - !include 'unpacked/Custom_Model_Infinite_Bag Doom tokens 16724b.yaml'
- !include 'unpacked/Custom_Model_Infinite_Bag Clue tokens fae2f6.yaml' - !include 'unpacked/Custom_Model_Infinite_Bag Clue tokens fae2f6.yaml'
- !include 'unpacked/Custom_Model_Infinite_Bag Clue tokens 3b2550.yaml' - !include 'unpacked/Custom_Model_Infinite_Bag Clue tokens 3b2550.yaml'
@ -422,8 +424,6 @@ ObjectStates:
- !include 'unpacked/Custom_Model_Infinite_Bag Damage tokens 480bda.yaml' - !include 'unpacked/Custom_Model_Infinite_Bag Damage tokens 480bda.yaml'
- !include 'unpacked/Custom_Model_Infinite_Bag Resource tokens 9fadf9.yaml' - !include 'unpacked/Custom_Model_Infinite_Bag Resource tokens 9fadf9.yaml'
- !include 'unpacked/Custom_Model_Infinite_Bag Connection markers 170f10.yaml' - !include 'unpacked/Custom_Model_Infinite_Bag Connection markers 170f10.yaml'
- !include 'unpacked/FogOfWarTrigger 3aab97.yaml'
- !include 'unpacked/Custom_Model_Bag Leaked Items 42cd6e.yaml'
- !include 'unpacked/Custom_Model_Bag Chaos Token Reserve 106418.yaml' - !include 'unpacked/Custom_Model_Bag Chaos Token Reserve 106418.yaml'
- !include 'unpacked/Custom_Model Clue Counter 37be78.yaml' - !include 'unpacked/Custom_Model Clue Counter 37be78.yaml'
- !include 'unpacked/Custom_Model Clue Counter 1769ed.yaml' - !include 'unpacked/Custom_Model Clue Counter 1769ed.yaml'
@ -432,8 +432,7 @@ ObjectStates:
- !include 'unpacked/Custom_Token Master Clue Counter 4a3aa4.yaml' - !include 'unpacked/Custom_Token Master Clue Counter 4a3aa4.yaml'
- !include 'unpacked/Custom_Model_Bag Legacy Assets 7165a9.yaml' - !include 'unpacked/Custom_Model_Bag Legacy Assets 7165a9.yaml'
- !include 'unpacked/Custom_Token Play Area 721ba2.yaml' - !include 'unpacked/Custom_Token Play Area 721ba2.yaml'
- !include 'unpacked/Bag Additional Player Cards 2cba6b.yaml' - !include 'unpacked/Custom_Model_Bag Additional Player Cards 2cba6b.yaml'
- !include 'unpacked/Custom_Assetbundle_Bag Barkham Horror 308439.yaml'
- !include 'unpacked/Custom_Token Chaos Bag Stat Tracker 766620.yaml' - !include 'unpacked/Custom_Token Chaos Bag Stat Tracker 766620.yaml'
- !include 'unpacked/Checker_white Token Spawn Tool 36b4ee.yaml' - !include 'unpacked/Checker_white Token Spawn Tool 36b4ee.yaml'
- !include 'unpacked/Custom_Model_Bag Standalone Scenarios 77a5f9.yaml' - !include 'unpacked/Custom_Model_Bag Standalone Scenarios 77a5f9.yaml'
@ -454,8 +453,6 @@ ObjectStates:
- !include 'unpacked/Custom_Model_Bag Chaos Bag fea079.yaml' - !include 'unpacked/Custom_Model_Bag Chaos Bag fea079.yaml'
- !include 'unpacked/Custom_Tile Data Helper 708279.yaml' - !include 'unpacked/Custom_Tile Data Helper 708279.yaml'
- !include 'unpacked/Custom_Token BlessCurse Manager 5933fb.yaml' - !include 'unpacked/Custom_Token BlessCurse Manager 5933fb.yaml'
- !include 'unpacked/Notecard d8d357.yaml'
- !include 'unpacked/ScriptingTrigger a2f932.yaml'
- !include 'unpacked/Custom_Model Edge of the Earth 895eaa.yaml' - !include 'unpacked/Custom_Model Edge of the Earth 895eaa.yaml'
- !include 'unpacked/Custom_Model The Dream-Eaters a16a1a.yaml' - !include 'unpacked/Custom_Model The Dream-Eaters a16a1a.yaml'
- !include 'unpacked/Custom_Model The Feast of Hemlock Vale c740af.yaml' - !include 'unpacked/Custom_Model The Feast of Hemlock Vale c740af.yaml'
@ -465,27 +462,11 @@ ObjectStates:
- !include 'unpacked/Custom_Tile Playermat 2 Orange bd0ff4.yaml' - !include 'unpacked/Custom_Tile Playermat 2 Orange bd0ff4.yaml'
- !include 'unpacked/Custom_Tile Playermat 3 Green 383d8b.yaml' - !include 'unpacked/Custom_Tile Playermat 3 Green 383d8b.yaml'
- !include 'unpacked/Custom_Tile Playermat 4 Red 0840d5.yaml' - !include 'unpacked/Custom_Tile Playermat 4 Red 0840d5.yaml'
- !include 'unpacked/Custom_Tile Neutral 2691e1.yaml'
- !include 'unpacked/Custom_Tile Neutral 748245.yaml'
- !include 'unpacked/Custom_Tile Neutral 271b17.yaml'
- !include 'unpacked/Custom_Tile Neutral 5bafdf.yaml'
- !include 'unpacked/Custom_Tile Neutral 012577.yaml'
- !include 'unpacked/Custom_Tile Neutral 04765b.yaml'
- !include 'unpacked/Custom_Tile Neutral b71036.yaml'
- !include 'unpacked/Custom_Tile Neutral 1cb302.yaml'
- !include 'unpacked/Custom_Tile Neutral bbc5d4.yaml'
- !include 'unpacked/Custom_Tile Neutral 429bb3.yaml'
- !include 'unpacked/Custom_Tile Neutral 183dbe.yaml'
- !include 'unpacked/Custom_Tile Neutral b80db6.yaml'
- !include 'unpacked/Custom_Tile Neutral af1927.yaml'
- !include 'unpacked/Custom_Tile Neutral 0329cc.yaml'
- !include 'unpacked/Custom_Tile Neutral 5bec40.yaml'
- !include 'unpacked/Custom_Tile Neutral 5825ca.yaml'
- !include 'unpacked/Custom_Token Lead Investigator acaa93.yaml' - !include 'unpacked/Custom_Token Lead Investigator acaa93.yaml'
- !include 'unpacked/Custom_Tile ArkhamDB Deck Importer a28140.yaml' - !include 'unpacked/Custom_Tile ArkhamDB Deck Importer a28140.yaml'
- !include 'unpacked/Checker_white Configuration 03804b.yaml' - !include 'unpacked/Checker_white Configuration 03804b.yaml'
- !include 'unpacked/Custom_Token Drawing Tool 280086.yaml' - !include 'unpacked/Custom_Token Drawing Tool 280086.yaml'
- !include 'unpacked/Custom_Token Playmat Image Swapper b7b45b.yaml' - !include 'unpacked/Custom_Token PlayArea Image Swapper b7b45b.yaml'
- !include 'unpacked/Bag All Player Cards 15bb07.yaml' - !include 'unpacked/Bag All Player Cards 15bb07.yaml'
- !include 'unpacked/Custom_Token Investigator Skill Tracker af7ed7.yaml' - !include 'unpacked/Custom_Token Investigator Skill Tracker af7ed7.yaml'
- !include 'unpacked/Custom_Token Investigator Skill Tracker e598c2.yaml' - !include 'unpacked/Custom_Token Investigator Skill Tracker e598c2.yaml'
@ -511,7 +492,6 @@ ObjectStates:
- !include 'unpacked/ScriptingTrigger TokenDiscardZone 457de5.yaml' - !include 'unpacked/ScriptingTrigger TokenDiscardZone 457de5.yaml'
- !include 'unpacked/ScriptingTrigger TokenDiscardZone 457de6.yaml' - !include 'unpacked/ScriptingTrigger TokenDiscardZone 457de6.yaml'
- !include 'unpacked/Custom_Tile Decoration - Map 6161b4.yaml' - !include 'unpacked/Custom_Tile Decoration - Map 6161b4.yaml'
- !include 'unpacked/Custom_Model_Bag Rulebooks, Guides and Tablets fcfa7f.yaml'
- !include 'unpacked/BlockRectangle Table Divider 612072.yaml' - !include 'unpacked/BlockRectangle Table Divider 612072.yaml'
- !include 'unpacked/BlockRectangle Table Divider 975c39.yaml' - !include 'unpacked/BlockRectangle Table Divider 975c39.yaml'
- !include 'unpacked/BlockRectangle Table Divider 75937e.yaml' - !include 'unpacked/BlockRectangle Table Divider 75937e.yaml'
@ -524,7 +504,6 @@ ObjectStates:
- !include 'unpacked/Custom_Tile Fan-Made Expansion Overview de7cae.yaml' - !include 'unpacked/Custom_Tile Fan-Made Expansion Overview de7cae.yaml'
- !include 'unpacked/Bag OptionPanel Source 830bd0.yaml' - !include 'unpacked/Bag OptionPanel Source 830bd0.yaml'
- !include 'unpacked/Custom_Assetbundle SoundCube 3c988f.yaml' - !include 'unpacked/Custom_Assetbundle SoundCube 3c988f.yaml'
- !include 'unpacked/go_game_piece_white Game Key Handler fce69c.yaml'
- !include 'unpacked/Custom_Tile Token Spawning Reference f8b3a7.yaml' - !include 'unpacked/Custom_Tile Token Spawning Reference f8b3a7.yaml'
- !include 'unpacked/3DText d628cc.yaml' - !include 'unpacked/3DText d628cc.yaml'
- !include 'unpacked/go_game_piece_black Navigation Overlay Handler 797ede.yaml' - !include 'unpacked/go_game_piece_black Navigation Overlay Handler 797ede.yaml'
@ -532,7 +511,7 @@ ObjectStates:
- !include 'unpacked/Custom_Token Token Arranger 022907.yaml' - !include 'unpacked/Custom_Token Token Arranger 022907.yaml'
- !include 'unpacked/Custom_Tile Chaos Bag Manager 023240.yaml' - !include 'unpacked/Custom_Tile Chaos Bag Manager 023240.yaml'
- !include 'unpacked/BlockRectangle Placeholder Box Dummy a93466.yaml' - !include 'unpacked/BlockRectangle Placeholder Box Dummy a93466.yaml'
- !include 'unpacked/Custom_Model The Matter of Britain 194cc5.yaml' - !include 'unpacked/Custom_Model Lovecrafter 3077 b08d20.yaml'
- !include 'unpacked/Custom_Tile Tokencache_+1 a15273.yaml' - !include 'unpacked/Custom_Tile Tokencache_+1 a15273.yaml'
- !include 'unpacked/Custom_Tile Tokencache_0 0a8592.yaml' - !include 'unpacked/Custom_Tile Tokencache_0 0a8592.yaml'
- !include 'unpacked/Custom_Tile Tokencache_-1 b644d2.yaml' - !include 'unpacked/Custom_Tile Tokencache_-1 b644d2.yaml'
@ -553,7 +532,18 @@ ObjectStates:
- !include 'unpacked/Custom_Tile Tokencache_Curse 16a9a7.yaml' - !include 'unpacked/Custom_Tile Tokencache_Curse 16a9a7.yaml'
- !include 'unpacked/Custom_Tile Tokencache_Frost b2b7be.yaml' - !include 'unpacked/Custom_Tile Tokencache_Frost b2b7be.yaml'
- !include 'unpacked/BlockSquare Physics Detector b300d8.yaml' - !include 'unpacked/BlockSquare Physics Detector b300d8.yaml'
- !include 'unpacked/Notecard Arkham SCE 3.8.0 - 5122024 - Page 1 bd6b3e.yaml' - !include 'unpacked/Custom_Tile Neutral 834ad5.yaml'
- !include 'unpacked/Custom_Tile Neutral a84ae4.yaml'
- !include 'unpacked/Custom_Tile Neutral 762df8.yaml'
- !include 'unpacked/Custom_Tile Neutral 589c7d.yaml'
- !include 'unpacked/Custom_Tile Neutral 642585.yaml'
- !include 'unpacked/Custom_Tile Neutral 6441b3.yaml'
- !include 'unpacked/Custom_Tile Neutral f2d25a.yaml'
- !include 'unpacked/Custom_Tile Neutral a5476b.yaml'
- !include 'unpacked/Custom_Tile Neutral 06ee01.yaml'
- !include 'unpacked/Custom_Tile Neutral 88d9ff.yaml'
- !include 'unpacked/Custom_Tile Neutral 42ec66.yaml'
- !include 'unpacked/Custom_Tile Neutral f94579.yaml'
PlayArea: 1 PlayArea: 1
PlayerCounts: PlayerCounts:
- 0 - 0
@ -561,7 +551,7 @@ PlayerCounts:
PlayingTime: PlayingTime:
- 0 - 0
- 0 - 0
SaveName: Arkham SCE - 3.8.0 SaveName: Arkham SCE - 3.9.0
Sky: Sky_Museum Sky: Sky_Museum
SkyURL: https://i.imgur.com/GkQqaOF.jpg SkyURL: https://i.imgur.com/GkQqaOF.jpg
SnapPoints: SnapPoints:
@ -749,182 +739,80 @@ SnapPoints:
x: -26 x: -26
y: 1.48 y: 1.48
z: -87 z: -87
- Position:
x: 60
y: 1.48
z: 48
- Position:
x: -42
y: 1.48
z: 89
- Position:
x: -42
y: 1.48
z: 71
- Position:
x: -62
y: 1.48
z: 71
- Position:
x: -62
y: 1.48
z: 89
TabStates: TabStates:
'10': '1':
body: "Created by Whimsical\n\nAnything that passes over the remover that isn't body: 'Welcome to Arkham Horror LCG - Super Complete Edition!
a card, deck or chaos token will be deleted.\r\nTo use the remover, right click
on it, choose the \"Enable\" option, and take your card with resources/horror/damage
and swipe it over the remover. You may wish to unlock and/or copy the remover Make sure to take the tour that can be started with the token in the middle
to your play area first." of the main playarea. Some basic notes:
DECKBUILDING
- All currently existing investigators and player cards are accessible via the
player card panel in the upper left corner of the table.
- On the leftside underneath the Investigators, you will find the ArkhamDB Deckimporter.
Insert your deck ID and it will build the deck automatically for you.
SCENARIOS & SETUP
- Arkham Horror LCG comes with a core campaign (Night of the Zealot) and several
expansions. Within each box you will find all the cards required for each scenario
setup, as well as a the official campaign guide PDF.
2. Each scenario is setup differently, and while some of the work has been prepared
beforehand (such as building encounter decks), you will have to refer to the
Campaign Guide for specific instructions on how to set up each scenario.
INVESTIGATOR PLAYMAT AND GAMEPLAY
- Playermats are scripted to automate most of the gameplay for you.'
color: Grey color: Grey
id: 10 id: 1
title: Token Remover title: Basic Intro
visibleColor: visibleColor:
b: 0.5 b: 0.5
g: 0.5 g: 0.5
r: 0.5 r: 0.5
'11': '2':
body: 'By Whimsical. Requires Numlock set to On.
Numpad 1: Cut top 3 cards of deck
Numpad 2: Cut top 6 cards of deck
Numpad 3: Cut top 9 cards of deck
Numpad 4: Spawn Damage
Numpad 5: Spawn Connection Marker
Numpad 6: Spawn Horror
Numpad 7: Spawn Doom
Numpad 8: Spawn Clue
Numpad 9: Spawn Resource
Numpad 0: Draw lines between selected objects. Hold to draw lines from mouseover
object to other selected objects.'
color: Grey
id: 11
title: Numpad Hotkeys
visibleColor:
b: 0.5
g: 0.5
r: 0.5
'7':
body: The server host can enable or disable cards in hands being hidden from other body: The server host can enable or disable cards in hands being hidden from other
players by going to the menu at the top of the TTS screen, clicking options, players by going to the menu at the top of the TTS screen, clicking options,
and choosing Hands. The "Disable" setting reveals all player hands to all players, and choosing Hands. The "Disable" setting reveals all player hands to all players,
while the "Default" setting means that each player can only see the cards in while the "Default" setting means that each player can only see the cards in
their own hand. their own hand.
color: Grey color: Grey
id: 7 id: 2
title: How to Hide Hands title: How to Hide Hands
visibleColor: visibleColor:
b: 0.5 b: 0.5
g: 0.5 g: 0.5
r: 0.5 r: 0.5
'8':
body: "Welcome to Arkham Horror LCG - Super Complete Edition!\r\n\r\nBelow you
will find all the features and instructions this mod is loaded with, that will
make your AH LCG experience easier.\r\n\r\nDECKBUILDING\r\n1. All current existing
investigators are on the right-hand side, and within each chest you will find
their investigator-specific assets and weaknesses. Also included is a basic
starter deck which only requires you to add a basic random weakness to get going.\r\n\r\n2.
On the left-hand side you will find both the weakness decks as well as lvl 0
cards sorted by class. To reveal the cards, click on each corresponding token
to deal the cards onto the table. Cards are sorted by order of Skill, event
and Asset cards top-down and increasing resource cost, left to right. \r\n\r\n3.
On the upper side you have the upgrade cards. Similarly, click each token to
deal the cards out onto the table. Cards are arranged both in increasing xp
cost and resource cost, left to right. Typically, these are the cards you will
be spending XP on between scenarios to purchase and improve your deck.\r\n4.
On the right-hand side underneath the Investigators, you will find the automated
ArkhamDB Deckbuilder (coded and maintained by Grabben). Click the load cards
button to activate the Deckbuilder, check ArkhamDB for your chosen deck\u2019s
URL and insert its number code following the instructions on the deckbuilder,
and it will build the deck automatically for you.\r\n\r\nSCENARIOS & SETUP\r\n1.
\tArkham Horror LCG comes with a core campaign (Night of the Zealot) and several
expansions (The Dunwich Legacy, The Path to Carcosa & The Forgotten Age). Within
each box you will find the volumes that contain all the cards required for each
scenario setup, as well as a tablet linking to the official campaign guide PDF.
Also included are chaos token cards and a Campaign Log.\r\n\r\n2. \tEach scenario
is setup differently, and while some of the work has been prepared beforehand
(such as building encounter decks), you will have to refer to the Campaign Guide
for specific instructions on how to set up each scenario.\r\n\r\n3. \tThe chaos
bag is always placed on the scenario setup mat in the upper right-hand corner
onto a snap point that tilts it at a 45-degree angle. Each scenario volume will
contain a difficulty card, where you will have the choice of four difficulties.
Press the button on the card according to the difficulty of your choosing and
the chaos bag will automatically be configured with the tokens specific to that
difficulty. In campaign play it is recommended to save your decks and chaos
bag at the end of your scenario to carry over onto the next, as often tokens
are added or removed from the chaos bag depending on actions or decisions made
during the game. These additional tokens can be drawn from the token reserve
book resting next to the newspaper in the middle of the main table \u2013 right
click it and search for the token you need.\n\r\n4. \tWhen placing location
cards, always place them face down on the main play area (the dark map of Arkham,
Massachusetts) with the number of clues per location unrevealed. The mod is
scripted so that when you flip said location cards (usually when entering the
location with an investigator), if it contains clues, the number of clues specific
to that location will automatically spawn. Note, that the mod only spawns tokens
in relation to the number of players currently set on the playmat player settings.
To set the number of players, left-click on the \"Investigators Playing\" number
to increase, or right-click to decrease.\r\n\n5. To make location mapping easier,
you can draw location connector tokens from the arrow-shaped container below
the main play area. Each token has three states (one way, two way and four way)
to use them accordingly to better visualize how your locations connect.\n\r\n6.
\tIf you require additional doom or clue tokens, these are located on the scenario
playmat in their corresponding containers. A handy Doom counter has been also
been added to track the doom on the agenda \u2013 left-click to add to add,
and right-click to deduct. Keep in mind that any doom spawned on enemies, locations
or assets needs to be mentally added to the doom in play on the agenda to account
for total doom.\r\n\r\nINVESTIGATOR PLAYMAT AND GAMEPLAY\r\n1. \tInvestigator
mats are scripted to automate most of the gameplay for you. wdw\n\r\n2. \tEach
mat has slots for inventory, where if you play an asset (for example you put
a gun that has 4 ammo into your right-hand slot), the mod will automatically
spawn the 4 resource tokens onto your equipped card.\n\r\n3.\tThe draw encounter
button on the left-hand side will draw the topmost card from the encounter deck
and put it in your threat area. Left-clicking will draw the card face-down,
and right-clicking will draw the card face-up. When you draw a weakness, or
engage an enemy, it is recommended you put it in your threat area, and once
you defeat the enemy or treachery, you can send it to the encounter discard
pile by clicking the discard button. If you defeat an enemy with a victory point,
make sure not to discard to the discard pile, but pick the card up and drop
it at the victory display.\n\r\n4. \tThe Click for Chaos button does just that,
draw a random chaos token from your chaos bag. Clicking a second time, sends
your chaos token back into the bag, which is then shuffled. If one player clicks
to draw a token and doesn\u2019t click a second time to send it back, the click
from another player on his personal mat will send the token back first, and
the next click will draw the token. Additionally, right-clicking the button
will continue drawing tokens and line them up next to each other, which is useful
for specific draw conditions the game may require from you. Left-clicking again
will send all drawn tokens back to the bag.\n\nADDITIONAL FEATURES:\n1. Over
20 Fan-made scenarios created by the thriving community of Arkham Horror LCG
have been included. Some of these are one-scenario missions, others are long
involved campaigns spanning multiple scenarios. These are all contained in \"The
Side Missions\". This boxset also includes the official FFG-created sidemissions
Curse of the Rougarou, Carnevale of Horrors, Labrynths of Lunacy and The Eternal
Slumber. Read the rulebook on including a side-mission into an ongoing campaign,
or play it as a one-off adventure! Setup instructions are included in each
volume.\n\n2. If you are not a fan of the dark themed Arkham map for the playmat,
you can change the image on it to any you like. At the top left hand side of
the playmat is an image icon, which when clicked will reveal a image swap panel.
Input the URL for the image you want to repalce the playmat with, and the panel
will apply the image for you. Keep in mind this will not change the existing
snap points on the current playmat.\n\r\nAs a final comment, please be sure
to let me know on the mod page in steam workshop if you find any bugs, issues
or have any suggestions for improvement!\r\n\r\n\r\n \r\n\r\n"
color: Grey
id: 8
title: Basic Intro
visibleColor:
b: 0.5
g: 0.5
r: 0.5
'9':
body: "Implemented by Tikatoy\nIdea conceived by Cadentia\n\nVersion 3.3\n\nTop
buttons manage bless tokens, bottom buttons manage curse tokens\nADD - creates
a new token and adds it to the chaos bag\nREMOVE - removes a token from the
chaos bag and destroys it\nTAKE - takes a token from the chaos bag and places
it below the manager (for sealing)\nRETURN - returns the last token taken from
the chaos bag to the chaos bag\n\nTo use Parallel Wendy, go to Options -> Game
Keys, then bind a key or mouse button to Wendy's Menu. Hover over any card (won't
work on decks) then press the bound key. Right-click seal/release options will
be added to the card.\n\n---Other Notes---\n\nOnly use ONE token manager at
a time\nTokens are limited to 10 of each type in play\nBless and curse tokens
should be in the chaos bag before trying to REMOVE or TAKE them\nEach action
logs a message which ends with (# in bag/# taken); hit enter to view log\n**WARNING**:
Tracking # of tokens in bag and in play will NOT persist between saves\n\r"
color: Grey
id: 9
title: Bless / Curse Manager
visibleColor:
b: 0.5
g: 0.5
r: 0.5
Table: Table_None Table: Table_None
Tags: [] Tags: []
Turns: Turns:

View File

@ -1,55 +0,0 @@
AltLookAngle:
x: 0
y: 0
z: 0
Autoraise: true
Bag:
Order: 0
ColorDiffuse:
b: 0
g: 0.36652
r: 0.70588
Description: 'Put any cards in here to add them to the indices for the player card
panel and deck importer.
Select the ''update index'' entry in the context menu of this bag once you''ve added
all cards.
This can be used for custom cards too.'
DragSelectable: true
GMNotes: ''
GUID: 2cba6b
Grid: true
GridProjection: false
Hands: false
HideWhenFaceDown: false
IgnoreFoW: false
LayoutGroupSortIndex: 0
Locked: true
LuaScript: !include 'Bag Additional Player Cards 2cba6b.ttslua'
LuaScriptState: ''
MaterialIndex: -1
MeasureMovement: false
MeshIndex: -1
Name: Bag
Nickname: Additional Player Cards
Number: 0
Snap: true
Sticky: true
Tags:
- AllCardsHotfix
Tooltip: true
Transform:
posX: 60
posY: 1.2
posZ: 48
rotX: 0
rotY: 0
rotZ: 0
scaleX: 1.5
scaleY: 1.5
scaleZ: 1.5
Value: 0
XmlUI: ''

View File

@ -45,13 +45,16 @@ __bundle_register("__root", function(require, _LOADED, __bundle_register, __bund
require("playercards/AllCardsBag") require("playercards/AllCardsBag")
end) end)
__bundle_register("playercards/AllCardsBag", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("playercards/AllCardsBag", function(require, _LOADED, __bundle_register, __bundle_modules)
local cardIdIndex = { } local guidReferenceApi = require("core/GUIDReferenceApi")
local classAndLevelIndex = { }
local basicWeaknessList = { } local cardIdIndex = {}
local uniqueWeaknessList = { } local classAndLevelIndex = {}
local cycleIndex = { } local basicWeaknessList = {}
local uniqueWeaknessList = {}
local cycleIndex = {}
local indexingDone = false local indexingDone = false
local otherCardsDetected = false
function onLoad() function onLoad()
self.addContextMenuItem("Rebuild Index", startIndexBuild) self.addContextMenuItem("Rebuild Index", startIndexBuild)
@ -65,7 +68,7 @@ end
-- called once indexing is complete it means the hotfix bag has been added -- called once indexing is complete it means the hotfix bag has been added
-- later, and we should rebuild the index to integrate the hotfix bag. -- later, and we should rebuild the index to integrate the hotfix bag.
function rebuildIndexForHotfix() function rebuildIndexForHotfix()
if (indexingDone) then if indexingDone then
startIndexBuild() startIndexBuild()
end end
end end
@ -73,23 +76,23 @@ end
-- Resets all current bag indexes -- Resets all current bag indexes
function clearIndexes() function clearIndexes()
indexingDone = false indexingDone = false
cardIdIndex = { } cardIdIndex = {}
classAndLevelIndex = { } classAndLevelIndex = {}
classAndLevelIndex["Guardian-upgrade"] = { } classAndLevelIndex["Guardian-upgrade"] = {}
classAndLevelIndex["Seeker-upgrade"] = { } classAndLevelIndex["Seeker-upgrade"] = {}
classAndLevelIndex["Mystic-upgrade"] = { } classAndLevelIndex["Mystic-upgrade"] = {}
classAndLevelIndex["Survivor-upgrade"] = { } classAndLevelIndex["Survivor-upgrade"] = {}
classAndLevelIndex["Rogue-upgrade"] = { } classAndLevelIndex["Rogue-upgrade"] = {}
classAndLevelIndex["Neutral-upgrade"] = { } classAndLevelIndex["Neutral-upgrade"] = {}
classAndLevelIndex["Guardian-level0"] = { } classAndLevelIndex["Guardian-level0"] = {}
classAndLevelIndex["Seeker-level0"] = { } classAndLevelIndex["Seeker-level0"] = {}
classAndLevelIndex["Mystic-level0"] = { } classAndLevelIndex["Mystic-level0"] = {}
classAndLevelIndex["Survivor-level0"] = { } classAndLevelIndex["Survivor-level0"] = {}
classAndLevelIndex["Rogue-level0"] = { } classAndLevelIndex["Rogue-level0"] = {}
classAndLevelIndex["Neutral-level0"] = { } classAndLevelIndex["Neutral-level0"] = {}
cycleIndex = { } cycleIndex = {}
basicWeaknessList = { } basicWeaknessList = {}
uniqueWeaknessList = { } uniqueWeaknessList = {}
end end
-- Clears the bag indexes and starts the coroutine to rebuild the indexes -- Clears the bag indexes and starts the coroutine to rebuild the indexes
@ -110,6 +113,7 @@ end
function buildIndex() function buildIndex()
local cardCount = 0 local cardCount = 0
indexingDone = false indexingDone = false
otherCardsDetected = false
-- process the allcardsbag itself -- process the allcardsbag itself
for _, cardData in ipairs(self.getData().ContainedObjects) do for _, cardData in ipairs(self.getData().ContainedObjects) do
@ -124,11 +128,15 @@ function buildIndex()
-- process hotfix bags (and the additional playercards bag) -- process hotfix bags (and the additional playercards bag)
for _, hotfixBag in ipairs(getObjectsWithTag("AllCardsHotfix")) do for _, hotfixBag in ipairs(getObjectsWithTag("AllCardsHotfix")) do
local hotfixData = hotfixBag.getData() local hotfixData = hotfixBag.getData()
if not hotfixData.ContainedObjects then break end
-- if the bag is empty, continue with the next bag
if not hotfixData.ContainedObjects then
goto nextBag
end
for _, cardData in ipairs(hotfixData.ContainedObjects) do for _, cardData in ipairs(hotfixData.ContainedObjects) do
-- process containers
if cardData.ContainedObjects then if cardData.ContainedObjects then
-- process containers
for _, deepCardData in ipairs(cardData.ContainedObjects) do for _, deepCardData in ipairs(cardData.ContainedObjects) do
addCardToIndex(deepCardData) addCardToIndex(deepCardData)
cardCount = cardCount + 1 cardCount = cardCount + 1
@ -137,8 +145,8 @@ function buildIndex()
coroutine.yield(0) coroutine.yield(0)
end end
end end
-- process single cards
else else
-- process single cards
addCardToIndex(cardData) addCardToIndex(cardData)
cardCount = cardCount + 1 cardCount = cardCount + 1
if cardCount > 19 then if cardCount > 19 then
@ -147,9 +155,11 @@ function buildIndex()
end end
end end
end end
::nextBag::
end end
buildSupplementalIndexes() buildSupplementalIndexes()
updatePlayerCardPanel()
indexingDone = true indexingDone = true
return 1 return 1
end end
@ -158,11 +168,23 @@ end
---@param cardData table TTS object data for the card ---@param cardData table TTS object data for the card
function addCardToIndex(cardData) function addCardToIndex(cardData)
-- using the more efficient 'json.parse()' to speed this process up -- using the more efficient 'json.parse()' to speed this process up
local cardMetadata = json.parse(cardData.GMNotes) local status, cardMetadata = pcall(function() return json.parse(cardData.GMNotes) end)
if not cardMetadata then return end
-- if an error happens, fallback to the regular parser
if status ~= true or cardMetadata == nil then
log("Fast parser failed for " .. cardData.Nickname .. ", using old parser instead.")
cardMetadata = JSON.decode(cardData.GMNotes)
end
-- if metadata was not valid JSON or empty, don't add the card
if not cardMetadata == nil then
log("Error parsing " .. cardData.Nickname)
return
end
-- use the ZoopGuid as fallback if no id present -- use the ZoopGuid as fallback if no id present
cardIdIndex[cardMetadata.id or cardMetadata.TtsZoopGuid] = { data = cardData, metadata = cardMetadata } cardMetadata.id = cardMetadata.id or cardMetadata.TtsZoopGuid
cardIdIndex[cardMetadata.id] = { data = cardData, metadata = cardMetadata }
-- also add data for alternate ids -- also add data for alternate ids
if cardMetadata.alternate_ids ~= nil then if cardMetadata.alternate_ids ~= nil then
@ -172,37 +194,35 @@ function addCardToIndex(cardData)
end end
end end
-- Creates the supplemental indexes for classes, weaknesses etc.
function buildSupplementalIndexes() function buildSupplementalIndexes()
for cardId, card in pairs(cardIdIndex) do for cardId, card in pairs(cardIdIndex) do
local cardMetadata = card.metadata
-- If the ID key and the metadata ID don't match this is a duplicate card created by an alternate_id, and we should skip it -- If the ID key and the metadata ID don't match this is a duplicate card created by an alternate_id, and we should skip it
if cardId == cardMetadata.id then if cardId == card.metadata.id then
-- Add card to the basic weakness list, if appropriate. Some weaknesses have multiple copies, and are added multiple times -- Add card to the basic weakness list, if appropriate. Some weaknesses have multiple copies, and are added multiple times
if cardMetadata.weakness then if card.metadata.weakness then
table.insert(uniqueWeaknessList, cardMetadata.id) table.insert(uniqueWeaknessList, card.metadata.id)
if cardMetadata.basicWeaknessCount ~= nil then if card.metadata.basicWeaknessCount ~= nil then
for i = 1, cardMetadata.basicWeaknessCount do for i = 1, card.metadata.basicWeaknessCount do
table.insert(basicWeaknessList, cardMetadata.id) table.insert(basicWeaknessList, card.metadata.id)
end end
end end
end end
-- Excludes signature cards (which have no class or level) -- Excludes signature cards (which have no class or level)
if cardMetadata.class ~= nil and cardMetadata.level ~= nil then if card.metadata.class ~= nil and card.metadata.level ~= nil then
local upgradeKey local upgradeKey = "-level0"
if cardMetadata.level > 0 then if card.metadata.level > 0 then
upgradeKey = "-upgrade" upgradeKey = "-upgrade"
else
upgradeKey = "-level0"
end end
-- parse classes (separated by "|") and add the card to the appropriate class and level indices -- parse classes (separated by "|") and add the card to the appropriate class and level indices
for str in cardMetadata.class:gmatch("([^|]+)") do for str in card.metadata.class:gmatch("([^|]+)") do
table.insert(classAndLevelIndex[str .. upgradeKey], cardMetadata.id) table.insert(classAndLevelIndex[str .. upgradeKey], card.metadata.id)
end end
-- add to cycle index -- add to cycle index
local cycleName = cardMetadata.cycle local cycleName = card.metadata.cycle
if cycleName ~= nil then if cycleName ~= nil then
cycleName = string.lower(cycleName) cycleName = string.lower(cycleName)
@ -211,12 +231,17 @@ function buildSupplementalIndexes()
-- override cycle name for night of the zealot -- override cycle name for night of the zealot
cycleName = cycleName:gsub("the night of the zealot", "core") cycleName = cycleName:gsub("the night of the zealot", "core")
else
-- track cards without defined cycle (should only be fan-made cards)
cycleName = "other"
otherCardsDetected = true
end
-- maybe initialize table
if cycleIndex[cycleName] == nil then if cycleIndex[cycleName] == nil then
cycleIndex[cycleName] = { } cycleIndex[cycleName] = {}
end
table.insert(cycleIndex[cycleName], cardMetadata.id)
end end
table.insert(cycleIndex[cycleName], card.metadata.id)
end end
end end
end end
@ -250,66 +275,133 @@ function cardComparator(id1, id2)
end end
end end
-- inform the player card panel about the presence of other cards (no cycle -> fan-made)
function updatePlayerCardPanel()
local panel = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayerCardPanel")
panel.call("createXML", otherCardsDetected)
end
---@return boolean: If true, the bag is currently not indexing and ready to be accessed
function isIndexReady() function isIndexReady()
if not indexingDone then
broadcastToAll("Still loading player cards, please try again in a few seconds", { 0.9, 0.2, 0.2 })
end
return indexingDone return indexingDone
end end
-- Returns a specific card from the bag, based on ArkhamDB ID -- Returns a specific card from the bag, based on ArkhamDB ID
-- Params table: ---@param params table ID of the card to retrieve
-- id: String ID of the card to retrieve ---@return table: If the indexes are still being constructed, returns an empty table.
-- Return: If the indexes are still being constructed, an empty table is -- Otherwise, a single table with the following fields
-- returned. Otherwise, a single table with the following fields -- data: TTS object data, suitable for spawning the card
-- cardData: TTS object data, suitable for spawning the card -- metadata: Table of parsed metadata
-- cardMetadata: Table of parsed metadata
function getCardById(params) function getCardById(params)
if (not indexingDone) then if not isIndexReady() then return {} end
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return { }
end
return cardIdIndex[params.id] return cardIdIndex[params.id]
end end
-- Returns a list of cards from the bag matching a class and level (0 or upgraded) -- Returns a list of cards from the bag matching a class and level (0 or upgraded)
-- Params table: ---@param params table
-- class: String class to retrieve ("Guardian", "Seeker", etc) -- class: String class to retrieve ("Guardian", "Seeker", etc)
-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0 -- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0
-- Return: If the indexes are still being constructed, returns an empty table. ---@return table: If the indexes are still being constructed, returns an empty table.
-- Otherwise, a list of tables, each with the following fields -- Otherwise, a list of tables, each with the following fields
-- cardData: TTS object data, suitable for spawning the card -- data: TTS object data, suitable for spawning the card
-- cardMetadata: Table of parsed metadata -- metadata: Table of parsed metadata
function getCardsByClassAndLevel(params) function getCardsByClassAndLevel(params)
if (not indexingDone) then if not isIndexReady() then return {} end
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return { } local upgradeKey = "-level0"
end if params.upgraded then
local upgradeKey
if (params.upgraded) then
upgradeKey = "-upgrade" upgradeKey = "-upgrade"
else
upgradeKey = "-level0"
end end
return classAndLevelIndex[params.class..upgradeKey]; return classAndLevelIndex[params.class .. upgradeKey]
end end
function getCardsByCycle(cycleName) -- Returns a list of cards from the bag matching a cycle
if (not indexingDone) then ---@param params table
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2}) -- cycle: String cycle to retrieve ("The Scarlet Keys" etc.)
return { } -- sortByMetadata: true to sort the table by metadata instead of ID
---@return table: If the indexes are still being constructed, returns an empty table.
-- Otherwise, a list of tables, each with the following fields
-- data: TTS object data, suitable for spawning the card
-- metadata: Table of parsed metadata
function getCardsByCycle(params)
if not isIndexReady() then return {} end
if not params.sortByMetadata then
return cycleIndex[string.lower(params.cycle)]
end end
return cycleIndex[string.lower(cycleName)]
-- sort list by metadata (useful for custom cards without proper IDs)
local cardList = {}
for _, id in ipairs(cycleIndex[string.lower(params.cycle)]) do
table.insert(cardList, id)
end
table.sort(cardList, metadataSortFunction)
return cardList
end
-- sorts cards by metadata: class, type, level, name and then description
function metadataSortFunction(id1, id2)
local card1 = cardIdIndex[id1]
local card2 = cardIdIndex[id2]
-- extract class per card
local classValue1 = getClassValueFromString(card1.metadata.class)
local classValue2 = getClassValueFromString(card2.metadata.class)
-- conversion tables to simplify type sorting
local typeConversion = {
Asset = 1,
Event = 2,
Skill = 3
}
if classValue1 ~= classValue2 then
return classValue1 < classValue2
elseif typeConversion[card1.metadata.type] ~= typeConversion[card2.metadata.type] then
return typeConversion[card1.metadata.type] < typeConversion[card2.metadata.type]
elseif card1.metadata.level ~= card2.metadata.level then
return card1.metadata.level < card2.metadata.level
elseif card1.data.Nickname ~= card2.data.Nickname then
return card1.data.Nickname < card2.data.Nickname
else
return card1.data.Description < card2.data.Description
end
end
-- helper function to calculate the class value for sorting from the "|" separated string
function getClassValueFromString(s)
local classValueList = {
Guardian = 1,
Seeker = 2,
Rogue = 3,
Mystic = 4,
Survivor = 5,
Neutral = 6
}
local classValue = 0
for str in s:gmatch("([^|]+)") do
-- this sorts multiclass cards
classValue = classValue * 10 + classValueList[str]
end
return classValue
end end
-- Searches the bag for cards which match the given name and returns a list. Note that this is -- Searches the bag for cards which match the given name and returns a list. Note that this is
-- an O(n) search without index support. It may be slow. -- an O(n) search without index support. It may be slow.
-- Parameter array must contain these fields to define the search: -- Parameter array must contain these fields to define the search:
-- name String or string fragment to search for names -- name: String or string fragment to search for names
-- exact Whether the name match should be exact -- exact: Whether the name match should be exact
function getCardsByName(params) function getCardsByName(params)
local name = params.name local name = params.name
local exact = params.exact local exact = params.exact
local results = { } local results = {}
-- Track cards (by ID) that we've added to avoid duplicates that may come from alternate IDs -- Track cards (by ID) that we've added to avoid duplicates that may come from alternate IDs
local addedCards = { } local addedCards = {}
for _, cardData in pairs(cardIdIndex) do for _, cardData in pairs(cardIdIndex) do
if (not addedCards[cardData.metadata.id]) then if (not addedCards[cardData.metadata.id]) then
if (exact and (string.lower(cardData.data.Nickname) == string.lower(name))) if (exact and (string.lower(cardData.data.Nickname) == string.lower(name)))
@ -322,42 +414,65 @@ function getCardsByName(params)
return results return results
end end
-- Gets a random basic weakness from the bag. Once a given ID has been returned -- Gets a random basic weakness from the bag. Once a given ID has been returned it will be
-- it will be removed from the list and cannot be selected again until a reload -- removed from the list and cannot be selected again until a reload occurs or the indexes
-- occurs or the indexes are rebuilt, which will refresh the list to include all -- are rebuilt, which will refresh the list to include all weaknesses.
-- weaknesses. ---@return string: ID of the selected weakness
-- Return: String ID of the selected weakness.
function getRandomWeaknessId() function getRandomWeaknessId()
local availableWeaknesses = buildAvailableWeaknesses() local availableWeaknesses = buildAvailableWeaknesses()
if (#availableWeaknesses > 0) then if #availableWeaknesses > 0 then
return availableWeaknesses[math.random(#availableWeaknesses)] return availableWeaknesses[math.random(#availableWeaknesses)]
end end
end end
-- Constructs a list of available basic weaknesses by starting with the full pool of basic -- Constructs a list of available basic weaknesses by starting with the full pool of basic
-- weaknesses then removing any which are currently in the play or deck construction areas -- weaknesses then removing any which are currently in the play or deck construction areas
-- Return: Table array of weakness IDs which are valid to choose from ---@param traits? string Trait(s) to use as filter
function buildAvailableWeaknesses() ---@return table: Array of weakness IDs which are valid to choose from
local weaknessesInPlay = { } function buildAvailableWeaknesses(traits)
local weaknessesInPlay = {}
local allObjects = getAllObjects() local allObjects = getAllObjects()
for _, object in ipairs(allObjects) do for _, object in ipairs(allObjects) do
if (object.name == "Deck") then if object.type == "Deck" then
for _, cardData in ipairs(object.getData().ContainedObjects) do for _, cardData in ipairs(object.getData().ContainedObjects) do
local cardMetadata = JSON.decode(cardData.GMNotes) incrementWeaknessCount(weaknessesInPlay, JSON.decode(cardData.GMNotes))
incrementWeaknessCount(weaknessesInPlay, cardMetadata)
end end
elseif (object.name == "Card") then elseif object.type == "Card" then
local cardMetadata = JSON.decode(object.getGMNotes()) incrementWeaknessCount(weaknessesInPlay, JSON.decode(object.getGMNotes()))
incrementWeaknessCount(weaknessesInPlay, cardMetadata)
end end
end end
local availableWeaknesses = { } local availableWeaknesses = {}
for _, weaknessId in ipairs(basicWeaknessList) do for _, weaknessId in ipairs(basicWeaknessList) do
if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then
weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1 weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1
else else
if traits then
-- split the string into separate traits (separated by "|")
local allowedTraits = {}
for str in traits:gmatch("([^|]+)") do
-- remove dots
str = str:gsub("[%.]", "")
-- remove leading and trailing whitespace
str = str:match("^%s*(.-)%s*$")
-- make sure string ends with a dot
str = string.lower(str .. ".")
table.insert(allowedTraits, str)
end
-- make sure the trait is present on the weakness
local card = cardIdIndex[weaknessId]
for _, allowedTrait in ipairs(allowedTraits) do
if string.contains(string.lower(card.metadata.traits), allowedTrait) then
table.insert(availableWeaknesses, weaknessId) table.insert(availableWeaknesses, weaknessId)
break
end
end
else
table.insert(availableWeaknesses, weaknessId)
end
end end
end end
return availableWeaknesses return availableWeaknesses
@ -373,8 +488,8 @@ end
-- Helper function that adds one to the table entry for the number of weaknesses in play -- Helper function that adds one to the table entry for the number of weaknesses in play
function incrementWeaknessCount(table, cardMetadata) function incrementWeaknessCount(table, cardMetadata)
if (isBasicWeakness(cardMetadata)) then if isBasicWeakness(cardMetadata) then
if (table[cardMetadata.id] == nil) then if table[cardMetadata.id] == nil then
table[cardMetadata.id] = 1 table[cardMetadata.id] = 1
else else
table[cardMetadata.id] = table[cardMetadata.id] + 1 table[cardMetadata.id] = table[cardMetadata.id] + 1
@ -389,4 +504,56 @@ function isBasicWeakness(cardMetadata)
and cardMetadata.basicWeaknessCount > 0 and cardMetadata.basicWeaknessCount > 0
end end
end) end)
__bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local GUIDReferenceApi = {}
local function getGuidHandler()
return getObjectFromGUID("123456")
end
-- Returns the matching object
---@param owner string Parent object for this search
---@param type string Type of object to search for
---@return any: Object reference to the matching object
GUIDReferenceApi.getObjectByOwnerAndType = function(owner, type)
return getGuidHandler().call("getObjectByOwnerAndType", { owner = owner, type = type })
end
-- Returns all matching objects as a table with references
---@param type string Type of object to search for
---@return table: List of object references to matching objects
GUIDReferenceApi.getObjectsByType = function(type)
return getGuidHandler().call("getObjectsByType", type)
end
-- Returns all matching objects as a table with references
---@param owner string Parent object for this search
---@return table: List of object references to matching objects
GUIDReferenceApi.getObjectsByOwner = function(owner)
return getGuidHandler().call("getObjectsByOwner", owner)
end
-- Sends new information to the reference handler to edit the main index
---@param owner string Parent of the object
---@param type string Type of the object
---@param guid string GUID of the object
GUIDReferenceApi.editIndex = function(owner, type, guid)
return getGuidHandler().call("editIndex", {
owner = owner,
type = type,
guid = guid
})
end
-- Returns the owner of an object or the object it's located on
---@param object tts__GameObject Object for this search
---@return string: Parent of the object or object it's located on
GUIDReferenceApi.getOwnerOfObject = function(object)
return getGuidHandler().call("getOwnerOfObject", object)
end
return GUIDReferenceApi
end
end)
return __bundle_require("__root") return __bundle_require("__root")

View File

@ -20,7 +20,7 @@ CustomDeck:
Description: Advanced Description: Advanced
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90040\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90040\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n
\ \"traits\": \"Madness.\",\n \"weakness\": true,\n \"cycle\": \"Standalone\"\n}" \ \"traits\": \"Madness.\",\n \"weakness\": true,\n \"cycle\": \"Red Tide Rising\"\n}"
GUID: 89fe92 GUID: 89fe92
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,8 +20,9 @@ CustomDeck:
Description: '' Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02266-t\",\n \"type\": \"Event\",\n \"class\": \"Rogue\",\n GMNotes: "{\n \"id\": \"02266-t\",\n \"type\": \"Event\",\n \"class\": \"Rogue\",\n
\ \"cost\": 0,\n \"level\": 3,\n \"traits\": \"Trick.\",\n \"cycle\": \"The Dunwich \ \"cost\": 0,\n \"level\": 3,\n \"traits\": \"Trick.\",\n \"uses\": [\n {\n
Legacy\"\n}" \ \"count\": 3,\n \"type\": \"Universal\",\n \"token\": \"universalActionAbility\"\n
\ }\n ],\n \"cycle\": \"The Dunwich Legacy\"\n}"
GUID: e92f21 GUID: e92f21
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,8 +20,9 @@ CustomDeck:
Description: '' Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02266\",\n \"type\": \"Event\",\n \"class\": \"Rogue\",\n GMNotes: "{\n \"id\": \"02266\",\n \"type\": \"Event\",\n \"class\": \"Rogue\",\n
\ \"cost\": 0,\n \"level\": 3,\n \"traits\": \"Trick.\",\n \"cycle\": \"The Dunwich \ \"cost\": 0,\n \"level\": 3,\n \"traits\": \"Trick.\",\n \"uses\": [\n {\n
Legacy\"\n}" \ \"count\": 3,\n \"type\": \"Universal\",\n \"token\": \"universalActionAbility\"\n
\ }\n ],\n \"cycle\": \"The Dunwich Legacy\"\n}"
GUID: 074858 GUID: 074858
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,14 +21,15 @@ Description: The Waitress
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01004-pb\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"01004-pb\",\n \"type\": \"Investigator\",\n \"class\":
\"Mystic\",\n \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": \"Mystic\",\n \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\":
2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"Core\",\n \"deck_requirements\": 2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"Bad Blood\",\n
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
[\n {\n \"01012\": 1,\n \"90018\": 1\n },\n {\n \"01013\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"01012\":
1,\n \"90019\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": 1,\n \"90018\": 1\n },\n {\n \"01013\": 1,\n \"90019\":
[\n \"mystic\",\n \"neutral\"\n ],\n \"level\": {\n \"min\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"spell\",\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"occult\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"spell\",\n \"occult\"\n
3\n }\n }\n ]\n}" \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
\ }\n ]\n}"
GUID: 909f30 GUID: 909f30
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,14 +21,14 @@ Description: The Waitress
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01004-pf\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"01004-pf\",\n \"type\": \"Investigator\",\n \"class\":
\"Mystic\",\n \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": \"Mystic\",\n \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\":
2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"Core\",\n \"deck_requirements\": 2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"Bad Blood\",\n
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
[\n {\n \"01012\": 1,\n \"90018\": 1\n },\n {\n \"01013\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"01012\":
1,\n \"90019\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": 1,\n \"90018\": 1\n },\n {\n \"01013\": 1,\n \"90019\":
[\n \"mystic\",\n \"neutral\"\n ],\n \"level\": {\n \"min\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"survivor\"\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n 5\n }\n },\n {\n \"faction\": [\n \"survivor\"\n ],\n
\ }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}"
GUID: 02db0a GUID: 02db0a
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,14 +21,15 @@ Description: The Waitress
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01004-p\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"01004-p\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": 2,\n \ \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": 2,\n
\ \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"Core\",\n \"deck_requirements\": \ \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"Bad Blood\",\n \"extraToken\":
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
[\n {\n \"01012\": 1,\n \"90018\": 1\n },\n {\n \"01013\": 1,\n \"signatures\": [\n {\n \"01012\": 1,\n \"90018\": 1\n
1,\n \"90019\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": \ },\n {\n \"01013\": 1,\n \"90019\": 1\n }\n ]\n
[\n \"mystic\",\n \"neutral\"\n ],\n \"level\": {\n \"min\": \ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"spell\",\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"occult\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"spell\",\n \"occult\"\n
3\n }\n }\n ]\n}" \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
\ }\n ]\n}"
GUID: 01b6ef GUID: 01b6ef
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,10 +22,10 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"01004\",\n \"alternate_ids\": [\n \"01504\"\n ],\n \"type\": GMNotes: "{\n \"id\": \"01004\",\n \"alternate_ids\": [\n \"01504\"\n ],\n \"type\":
\"Investigator\",\n \"class\": \"Mystic\",\n \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": \"Investigator\",\n \"class\": \"Mystic\",\n \"traits\": \"Sorcerer.\",\n \"willpowerIcons\":
5,\n \"intellectIcons\": 2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": 5,\n \"intellectIcons\": 2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\":
\"Core\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \"Core\",\n \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"01012\": 1,\n \"90018\": 1\n 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"01012\":
\ },\n {\n \"01013\": 1,\n \"90019\": 1\n }\n ]\n 1,\n \"90018\": 1\n },\n {\n \"01013\": 1,\n \"90019\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
5\n }\n },\n {\n \"faction\": [\n \"survivor\"\n ],\n 5\n }\n },\n {\n \"faction\": [\n \"survivor\"\n ],\n
\ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}"
@ -70,15 +70,15 @@ States:
GMNotes: "{\r\n \"id\": \"01004\",\r\n \"alternate_ids\": [\r\n \"01504\"\r\n GMNotes: "{\r\n \"id\": \"01004\",\r\n \"alternate_ids\": [\r\n \"01504\"\r\n
\ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Mystic\",\r\n \"traits\": \ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Mystic\",\r\n \"traits\":
\"Sorcerer.\",\r\n \"willpowerIcons\": 5,\r\n \"intellectIcons\": 2,\r\n \"combatIcons\": \"Sorcerer.\",\r\n \"willpowerIcons\": 5,\r\n \"intellectIcons\": 2,\r\n \"combatIcons\":
2,\r\n \"agilityIcons\": 3,\r\n \"cycle\": \"Core\",\r\n \"deck_requirements\": 2,\r\n \"agilityIcons\": 3,\r\n \"cycle\": \"Core\",\r\n \"extraToken\":
{\r\n \"size\": 30,\r\n \"randomBasicWeaknessCount\": 1,\r\n \"signatures\": \"Reaction\", \r\n \"deck_requirements\": {\r\n \"size\": 30,\r\n \"randomBasicWeaknessCount\":
[\r\n {\r\n \"01012\": 1,\r\n \"90018\": 1\r\n },\r\n 1,\r\n \"signatures\": [\r\n {\r\n \"01012\": 1,\r\n \"90018\":
\ {\r\n \"01013\": 1,\r\n \"90019\": 1\r\n }\r\n ]\r\n 1\r\n },\r\n {\r\n \"01013\": 1,\r\n \"90019\": 1\r\n
\ },\r\n \"deck_options\": [\r\n {\r\n \"faction\": [\r\n \"mystic\",\r\n \ }\r\n ]\r\n },\r\n \"deck_options\": [\r\n {\r\n \"faction\":
\ \"neutral\"\r\n ],\r\n \"level\": {\r\n \"min\": 0,\r\n [\r\n \"mystic\",\r\n \"neutral\"\r\n ],\r\n \"level\":
\ \"max\": 5\r\n }\r\n },\r\n {\r\n \"faction\": [\r\n {\r\n \"min\": 0,\r\n \"max\": 5\r\n }\r\n },\r\n {\r\n
\ \"survivor\"\r\n ],\r\n \"level\": {\r\n \"min\": 0,\r\n \ \"faction\": [\r\n \"survivor\"\r\n ],\r\n \"level\":
\ \"max\": 2\r\n }\r\n }\r\n ]\r\n}\r\n" {\r\n \"min\": 0,\r\n \"max\": 2\r\n }\r\n }\r\n ]\r\n}\r\n"
GUID: 6797bb GUID: 6797bb
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -81,12 +81,12 @@ States:
- Minicard - Minicard
Tooltip: true Tooltip: true
Transform: Transform:
posX: 40.7253036 posX: 40
posY: 1.29860592 posY: 1.3
posZ: 66.7765656 posZ: 66
rotX: 1.697304e-07 rotX: 0
rotY: 270.0102 rotY: 270
rotZ: 2.00479718e-07 rotZ: 0
scaleX: 0.6 scaleX: 0.6
scaleY: 1 scaleY: 1
scaleZ: 0.6 scaleZ: 0.6

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"03004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"03004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": 2,\n \ \"traits\": \"Sorcerer.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": 2,\n
\ \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Path to Carcosa\",\n \ \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Path to Carcosa\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"03014\": 1\n },\n {\n \"03015\": 1,\n \"signatures\": [\n {\n \"03014\": 1\n },\n {\n \"03015\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":

View File

@ -22,12 +22,13 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"10009\",\n \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n GMNotes: "{\n \"id\": \"10009\",\n \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n
\ \"traits\": \"Drifter. Socialite.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\": \ \"traits\": \"Drifter. Socialite.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\":
4,\n \"combatIcons\": 2,\n \"agilityIcons\": 4,\n \"cycle\": \"The Feast of Hemlock 4,\n \"combatIcons\": 2,\n \"agilityIcons\": 4,\n \"cycle\": \"The Feast of Hemlock
Vale\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Vale\",\n \"extraToken\": \"Parley\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"10010\": 3\n },\n {\n \"10011\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"10010\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"rogue\",\n 3\n },\n {\n \"10011\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"rogue\",\n \"neutral\"\n ],\n
5\n }\n },\n {\n \"special\": [\n \"parley\"\n ],\n \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n }\n ]\n}" \ \"special\": [\n \"parley\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 5\n }\n }\n ]\n}"
GUID: 54eaa5 GUID: 54eaa5
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,13 +22,14 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"07002\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"07002\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n
\ \"traits\": \"Miskatonic. Scholar.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": \ \"traits\": \"Miskatonic. Scholar.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\":
2,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"The Innsmouth 2,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"The Innsmouth
Conspiracy\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Conspiracy\",\n \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"07008\": 1\n },\n {\n \"07009\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"07008\":
1\n }\n ]\n \n },\n \"deck_options\": [\n {\n \"faction\": 1\n },\n {\n \"07009\": 1\n }\n ]\n \n },\n \"deck_options\":
[\n \"seeker\",\n \"neutral\"\n ],\n \"level\": {\n \"min\": [\n {\n \"faction\": [\n \"seeker\",\n \"neutral\"\n ],\n
0,\n \"max\": 5\n }\n },\n {\n \"trait\": [\n \"practiced\"\n \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
\ ],\n \"type\": [\n \"skill\"\n ],\n \"level\": {\n \"min\": \ \"trait\": [\n \"practiced\"\n ],\n \"type\": [\n \"skill\"\n
0,\n \"max\": 3\n }\n }\n ]\n}" \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
\ }\n ]\n}"
GUID: 05b950 GUID: 05b950
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,13 +22,13 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"09011\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"09011\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Chosen. Cursed.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\": \ \"traits\": \"Chosen. Cursed.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\":
3,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Scarlet Keys\",\n 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Scarlet Keys\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"09012\": 1,\n \"09013\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"09012\":
\ },\n {\n \"09014\": 1\n }\n ]\n },\n \"deck_options\": 1,\n \"09013\": 1\n },\n {\n \"09014\": 1\n }\n ]\n
[\n {\n \"faction\": [\n \"mystic\",\n \"neutral\"\n ],\n \ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"trait\": [\n \"charm\"\n ],\n \"level\": {\n \"min\": 5\n }\n },\n {\n \"trait\": [\n \"charm\"\n ],\n \"level\":
0,\n \"max\": 4\n }\n }\n ]\n}" {\n \"min\": 0,\n \"max\": 4\n }\n }\n ]\n}"
GUID: 4c2a3d GUID: 4c2a3d
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -0,0 +1,238 @@
-- Bundled by luabundle {"version":"1.6.0"}
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
local loadingPlaceholder = {[{}] = true}
local register
local modules = {}
local require
local loaded = {}
register = function(name, body)
if not modules[name] then
modules[name] = body
end
end
require = function(name)
local loadedModule = loaded[name]
if loadedModule then
if loadedModule == loadingPlaceholder then
return nil
end
else
if not modules[name] then
if not superRequire then
local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
error('Tried to require ' .. identifier .. ', but no such module has been registered')
else
return superRequire(name)
end
end
loaded[name] = loadingPlaceholder
loadedModule = modules[name](require, loaded, register, modules)
loaded[name] = loadedModule
end
return loadedModule
end
return require, loaded, register, modules
end)(nil)
__bundle_register("playercards/CardsWithHelper", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that have helpers
This file is used to share code between cards with helpers.
It syncs the visibility of the helper with the option panel and
makes sure the card has the respective tag.
Additionally, it will call 'initiliaze()' and 'shutOff()'
in the parent file if they are present.
Instructions:
1) Define the global variables before requiring this file:
hasXML = true (whether the card has an XML display)
isHelperEnabled = false (default state of the helper, should be 'false')
2) In 'onLoad()'', call 'syncDisplayWithOptionPanel()'
----------------------------------------------------------]]
local optionPanelApi = require("core/OptionPanelApi")
-- if the respective option is enabled in onLoad(), enable the helper
function syncDisplayWithOptionPanel()
self.addTag("CardWithHelper")
local options = optionPanelApi.getOptions()
if options.enableCardHelpers then
setHelperState(true)
else
updateDisplay()
end
end
-- forces a new state
function setHelperState(newState)
isHelperEnabled = newState
updateSave()
updateDisplay()
end
-- toggles the current state
function toggleHelper()
isHelperEnabled = not isHelperEnabled
updateSave()
updateDisplay()
end
-- updates the visibility and calls events (after a small delay to allow XML being set)
function updateDisplay()
Wait.frames(actualDisplayUpdate, 5)
end
function actualDisplayUpdate()
if isHelperEnabled then
self.clearContextMenu()
self.addContextMenuItem("Disable Helper", toggleHelper)
if hasXML then self.UI.show("Helper") end
if initialize then initialize() end
else
self.clearContextMenu()
self.addContextMenuItem("Enable Helper", toggleHelper)
if hasXML then self.UI.hide("Helper") end
if shutOff then shutOff() end
end
end
end)
__bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local OptionPanelApi = {}
-- loads saved options
---@param options table Set a new state for the option table
OptionPanelApi.loadSettings = function(options)
return Global.call("loadSettings", options)
end
---@return any: Table of option panel state
OptionPanelApi.getOptions = function()
return Global.getTable("optionPanel")
end
return OptionPanelApi
end
end)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/cards/Analysis")
end)
__bundle_register("playercards/cards/Analysis", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/CardsWithHelper")
require("playercards/CardsThatRedrawTokens")
end)
__bundle_register("playercards/CardsThatRedrawTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that return and redraw tokens
This file is used to add an XML button to a card, turned on via context menu.
Valid options modify the appearance of the XML button, as well as the
behavior of the return and redraw function. Set options before requiring this file.
Parameters for the return and redraw functions. Typically set VALID_TOKENS or INVALID_TOKENS, not both.
If there are no restrictions on which tokens can be redrawn (e.g. Wendy Adams), keep both empty.
* VALID_TOKENS --@type table
- keyed table which lists all tokens that can be redrawn by the card
- example usage: "False Covenant"
> VALID_TOKENS = {
> ["Curse"] = true
> }
* INVALID_TOKENS --@type table
- keyed table which lists all tokens that cannot be redrawn by the card
- example usage: "Custom Ammunition"
> INVALID_TOKENS = {
> ["Auto-fail"] = true
> }
* DRAW_SPECIFIC_TOKEN --@type string (name of token or nil)
- if set, will attempt to draw that specific token
* RETURN_TO_POOL --@type string
- allows for the name of the card to be passed onto Global for any special handling
The following parameters modify the appearence of the XML button and are not listed as part of a table.
- buttonHeight (default is 450)
- buttonWidth (default is 1400)
- buttonPosition (default is "0 -55 -22")
- buttonFontSize (default is 250)
- buttonRotation (change if button is placed on an investigator cards)
- buttonLabel (default is "Redraw Token")
- buttonIcon (to add an icon to the right)
- buttonColor (default is "#77674DE6")
----------------------------------------------------------
EXAMPLE: Claypool's Furs
This card can only redraw the Frost token, and is replaced with a random token from the bag.
As a nice reminder the XML button takes on the Frost color and icon with the text "Cancel".
> buttonValue = "Cancel"
> buttonIcon = "token-frost"
> buttonColor = "#404450E6"
> buttonFontSize = 300
> VALID_TOKENS = {
> ["Frost"] = true
> }
>
> require...
----------------------------------------------------------]]
-- intentionally global
hasXML = true
isHelperEnabled = false
function updateSave()
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
end
function onLoad(savedData)
if savedData and savedData ~= "" then
local loadedData = JSON.decode(savedData)
isHelperEnabled = loadedData.isHelperEnabled
end
createHelperXML()
syncDisplayWithOptionPanel()
end
function createHelperXML()
local xmlTable = { {
tag = "Button",
attributes = {
active = "false",
id = "Helper",
height = buttonHeight or 450,
width = buttonWidth or 1400,
rotation = buttonRotation or "0 0 180",
scale = "0.1 0.1 1",
position = buttonPosition or "0 -55 -22",
padding = "50 50 50 50",
font = "font_teutonic-arkham",
fontSize = buttonFontSize or 250,
onClick = "triggerXMLTokenLabelCreation",
color = buttonColor or "#77674DE6",
textColor = "White"
},
value = buttonLabel or "Redraw Token"
} }
if buttonIcon then
xmlTable[1].attributes.iconWidth = "400"
xmlTable[1].attributes.iconAlignment = "Right"
xmlTable[1].attributes.icon = buttonIcon
end
self.UI.setXmlTable(xmlTable)
end
function triggerXMLTokenLabelCreation()
Global.call("activeRedrawEffect", {
VALID_TOKENS = VALID_TOKENS,
INVALID_TOKENS = INVALID_TOKENS,
RETURN_TO_POOL = RETURN_TO_POOL
})
end
end)
return __bundle_require("__root")

View File

@ -30,7 +30,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: '' LuaScript: !include 'Card Analysis 80285f.ttslua'
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card

View File

@ -21,11 +21,11 @@ Description: The Drifter
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02005-pb\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"02005-pb\",\n \"type\": \"Investigator\",\n \"class\":
\"Survivor\",\n \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \"Survivor\",\n \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\n 2,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"On the Road Again\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"FreeTrigger\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"90047\": 1,\n \"02014\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90047\":
\ },\n {\n \"90048\": 1,\n \"02015\": 1\n }\n ]\n 1,\n \"02014\": 1\n },\n {\n \"90048\": 1,\n \"02015\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\"\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\"\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
\ },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\": \ },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"trait\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"trait\":

View File

@ -21,11 +21,11 @@ Description: The Drifter
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02005-pf\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"02005-pf\",\n \"type\": \"Investigator\",\n \"class\":
\"Survivor\",\n \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \"Survivor\",\n \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
2,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\n 2,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"On the Road Again\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"90047\": 1,\n \"02014\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90047\":
\ },\n {\n \"90048\": 1,\n \"02015\": 1\n }\n ]\n 1,\n \"02014\": 1\n },\n {\n \"90048\": 1,\n \"02015\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\":
0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5 0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5

View File

@ -21,11 +21,11 @@ Description: The Drifter
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02005-p\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n GMNotes: "{\n \"id\": \"02005-p\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n
\ \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": 2,\n \ \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": 2,\n
\ \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\n \ \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"On the Road Again\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"90047\": 1,\n \"02014\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90047\":
\ },\n {\n \"90048\": 1,\n \"02015\": 1\n }\n ]\n 1,\n \"02014\": 1\n },\n {\n \"90048\": 1,\n \"02015\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\"\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\"\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
\ },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\": \ },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"trait\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"trait\":

View File

@ -22,10 +22,10 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"02005\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n GMNotes: "{\n \"id\": \"02005\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n
\ \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": 2,\n \ \"traits\": \"Drifter.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": 2,\n
\ \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\n \ \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"FreeTrigger\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"90047\": 1,\n \"02014\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90047\":
\ },\n {\n \"90048\": 1,\n \"02015\": 1\n }\n ]\n 1,\n \"02014\": 1\n },\n {\n \"90048\": 1,\n \"02015\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\":
0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5 0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5

View File

@ -21,7 +21,9 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"09091\",\n \"type\": \"Asset\",\n \"slot\": \"Body\",\n GMNotes: "{\n \"id\": \"09091\",\n \"type\": \"Asset\",\n \"slot\": \"Body\",\n
\ \"class\": \"Mystic\",\n \"cost\": 3,\n \"level\": 2,\n \"traits\": \"Ritual.\",\n \ \"class\": \"Mystic\",\n \"cost\": 3,\n \"level\": 2,\n \"traits\": \"Ritual.\",\n
\ \"combatIcons\": 1,\n \"cycle\": \"The Scarlet Keys\"\n}" \ \"combatIcons\": 1,\n \"uses\": [\n {\n \"count\": 1,\n \"type\":
\"PlayItem\",\n \"token\": \"universalActionAbility\"\n }\n ],\n \"cycle\":
\"The Scarlet Keys\"\n}"
GUID: b5d894 GUID: b5d894
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -39,7 +39,7 @@ SidewaysCard: false
Snap: true Snap: true
Sticky: true Sticky: true
Tags: Tags:
- ScenarioCard - PlayerCard
Tooltip: true Tooltip: true
Transform: Transform:
posX: 48.98 posX: 48.98

View File

@ -21,7 +21,8 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"10129\",\n \"type\": \"Event\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"10129\",\n \"type\": \"Event\",\n \"class\": \"Neutral\",\n
\ \"cost\": 0,\n \"level\": 0,\n \"traits\": \"Double.\",\n \"wildIcons\": 1,\n \ \"cost\": 0,\n \"level\": 0,\n \"traits\": \"Double.\",\n \"wildIcons\": 1,\n
\ \"cycle\": \"The Feast of Hemlock Vale\"\n}" \ \"uses\": [\n {\n \"count\": 2,\n \"type\": \"Universal\",\n \"token\":
\"universalActionAbility\"\n }\n ],\n \"cycle\": \"The Feast of Hemlock Vale\"\n}"
GUID: 24d3b3 GUID: 24d3b3
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,16 +22,17 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"08016\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n GMNotes: "{\n \"id\": \"08016\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n
\ \"traits\": \"Entrepreneur.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": \ \"traits\": \"Entrepreneur.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\":
4,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"Edge of the Earth\",\n 4,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"Edge of the Earth\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"PlayItem\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"08017\": 1\n },\n {\n \"08018\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"08017\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\"\n 1\n },\n {\n \"08018\": 1\n }\n ]\n },\n \"deck_options\":
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n }\n [\n {\n \"faction\": [\n \"survivor\"\n ],\n \"level\":
\ },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n }\n },\n {\n \"faction\":
{\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
[\n \"rogue\"\n ],\n \"level\": {\n \"min\": 1,\n \"max\":
5\n }\n },\n {\n \"faction\": [\n \"rogue\"\n ],\n \"level\": 5\n }\n },\n {\n \"faction\": [\n \"rogue\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 0\n },\n \"limit\": 5,\n \"error\": {\n \"min\": 1,\n \"max\": 5\n }\n },\n {\n \"faction\":
\"You cannot have more than 5 level 0 Rogue cards\"\n }\n ]\n}" [\n \"rogue\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5
level 0 Rogue cards\"\n }\n ]\n}"
GUID: 419b0c GUID: 419b0c
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -14,18 +14,18 @@
onClick="resolveToken" onClick="resolveToken"
textColor="white" textColor="white"
active="false"/> active="false"/>
<Panel position="0 -55 -22" <TableLayout position="0 -55 -22"
rotation="0 0 180" rotation="0 0 180"
height="900" height="900"
width="1400" width="1400"
scale="0.1 0.1 1"/> scale="0.1 0.1 1"
<TableLayout active="false"
cellSpacing="80" cellSpacing="80"
cellBackgroundColor="rgba(1,1,1,0)"/> cellBackgroundColor="rgba(1,1,1,0)"/>
</Defaults> </Defaults>
<Panel> <Panel id="Helper"
<TableLayout id="actives"> active="false">
<TableLayout>
<Row> <Row>
<Cell> <Cell>
<Button id="Bless" <Button id="Bless"
@ -43,10 +43,7 @@
</Cell> </Cell>
</Row> </Row>
</TableLayout> </TableLayout>
</Panel> <TableLayout>
<Panel>
<TableLayout id="inactives">
<Row> <Row>
<Cell> <Cell>
<Button id="inactiveBless" <Button id="inactiveBless"

View File

@ -22,12 +22,13 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"04005\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n GMNotes: "{\n \"id\": \"04005\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n
\ \"traits\": \"Cursed. Drifter.\",\n \"willpowerIcons\": 0,\n \"intellectIcons\": \ \"traits\": \"Cursed. Drifter.\",\n \"willpowerIcons\": 0,\n \"intellectIcons\":
0,\n \"combatIcons\": 0,\n \"agilityIcons\": 0,\n \"cycle\": \"The Forgotten 0,\n \"combatIcons\": 0,\n \"agilityIcons\": 0,\n \"cycle\": \"The Forgotten
Age\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Age\",\n \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"04015\": 1\n },\n {\n \"04016\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"04015\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n 1\n },\n {\n \"04016\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"survivor\",\n \"neutral\"\n ],\n
5\n }\n },\n {\n \"trait\": [\n \"spirit\"\n ],\n \"level\": \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
{\n \"min\": 0,\n \"max\": 3\n }\n }\n ]\n}" \ \"trait\": [\n \"spirit\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 3\n }\n }\n ]\n}"
GUID: b02a1e GUID: b02a1e
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,20 +22,20 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"05001\",\n \"alternate_ids\": [\n \"98010\"\n ],\n \"type\": GMNotes: "{\n \"id\": \"05001\",\n \"alternate_ids\": [\n \"98010\"\n ],\n \"type\":
\"Investigator\",\n \"class\": \"Guardian\",\n \"traits\": \"Medic.\",\n \"willpowerIcons\": \"Investigator\",\n \"class\": \"Guardian\",\n \"traits\": \"Medic.\",\n \"willpowerIcons\":
3,\n \"intellectIcons\": 4,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": 3,\n \"intellectIcons\": 4,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\":
\"The Circle Undone\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \"The Circle Undone\",\n \"extraToken\": \"None\",\n \"deck_requirements\": {\n
1,\n \"signatures\": [\n {\n \"05007\": 1,\n \"98011\": 1\n },\n \ \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n
\ {\n \"05008\": 1,\n \"98012\": 1\n }\n ]\n },\n \"deck_options\": \ {\n \"05007\": 1,\n \"98011\": 1\n },\n {\n \"05008\":
[\n {\n \"not\": true,\n \"trait\": [\n \"weapon\"\n ],\n 1,\n \"98012\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"not\":
\ \"level\": {\n \"min\": 1,\n \"max\": 5\n }\n },\n {\n true,\n \"trait\": [\n \"weapon\"\n ],\n \"level\": {\n \"min\":
\ \"faction\": [\n \"guardian\"\n ],\n \"level\": {\n \"min\": 1,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"guardian\"\n
0,\n \"max\": 3\n }\n },\n {\n \"faction\": [\n \"neutral\"\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n \ },\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\":
\ },\n {\n \"special\": [\n \"heals_horror\"\n ],\n \"tag\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"special\":
[\n \"hh\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n \"heals_horror\"\n ],\n \"tag\": [\n \"hh\"\n ],\n
5\n }\n },\n {\n \"faction\": [\n \"seeker\",\n \"mystic\"\n \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 1\n },\n \ \"faction\": [\n \"seeker\",\n \"mystic\"\n ],\n \"level\":
\ \"limit\": 15,\n \"error\": \"You cannot have more than 15 level 0-1 {\n \"min\": 0,\n \"max\": 1\n },\n \"limit\": 15,\n \"error\":
Seeker and/or Mystic cards\"\n }\n ]\n}" \"You cannot have more than 15 level 0-1 Seeker and/or Mystic cards\"\n }\n ]\n}"
GUID: b03b12 GUID: b03b12
Grid: true Grid: true
GridProjection: false GridProjection: false
@ -77,23 +77,24 @@ States:
GMNotes: "{\r\n \"id\": \"05001\",\r\n \"alternate_ids\": [\r\n \"98010\"\r\n GMNotes: "{\r\n \"id\": \"05001\",\r\n \"alternate_ids\": [\r\n \"98010\"\r\n
\ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Guardian\",\r\n \"traits\": \ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Guardian\",\r\n \"traits\":
\"Medic.\",\r\n \"willpowerIcons\": 3,\r\n \"intellectIcons\": 4,\r\n \"combatIcons\": \"Medic.\",\r\n \"willpowerIcons\": 3,\r\n \"intellectIcons\": 4,\r\n \"combatIcons\":
2,\r\n \"agilityIcons\": 2,\r\n \"cycle\": \"The Circle Undone\",\r\n \"deck_requirements\": 2,\r\n \"agilityIcons\": 2,\r\n \"cycle\": \"The Circle Undone\",\r\n \"extraToken\":
{\r\n \"size\": 30,\r\n \"randomBasicWeaknessCount\": 1,\r\n \"signatures\": \"None\",\r\n \"deck_requirements\": {\r\n \"size\": 30,\r\n \"randomBasicWeaknessCount\":
[\r\n {\r\n \"05007\": 1,\r\n \"98011\": 1\r\n },\r\n {\r\n 1,\r\n \"signatures\": [\r\n {\r\n \"05007\": 1,\r\n \"98011\":
\ \"05008\": 1,\r\n \"98012\": 1\r\n }\r\n ]\r\n },\r\n \"deck_options\": 1\r\n },\r\n {\r\n \"05008\": 1,\r\n \"98012\": 1\r\n }\r\n
[\r\n {\r\n \"not\": true,\r\n \"trait\": [\r\n \"weapon\"\r\n \ ]\r\n },\r\n \"deck_options\": [\r\n {\r\n \"not\": true,\r\n
\ ],\r\n \"level\": {\r\n \"min\": 1,\r\n \"max\": 5\r\n \ \"trait\": [\r\n \"weapon\"\r\n ],\r\n \"level\": {\r\n
\ }\r\n },\r\n {\r\n \"faction\": [\r\n \"guardian\"\r\n \ \"min\": 1,\r\n \"max\": 5\r\n }\r\n },\r\n {\r\n
\ ],\r\n \"level\": {\r\n \"min\": 0,\r\n \"max\": 3\r\n \ \"faction\": [\r\n \"guardian\"\r\n ],\r\n \"level\":
\ }\r\n },\r\n {\r\n \"faction\": [\r\n \"neutral\"\r\n {\r\n \"min\": 0,\r\n \"max\": 3\r\n }\r\n },\r\n {\r\n
\ ],\r\n \"level\": {\r\n \"min\": 0,\r\n \"max\": 5\r\n \ \"faction\": [\r\n \"neutral\"\r\n ],\r\n \"level\":
\ }\r\n },\r\n {\r\n \"special\": [\r\n \"heals_horror\"\r\n
\ ],\r\n \"tag\": [\r\n \"hh\"\r\n ],\r\n \"level\":
{\r\n \"min\": 0,\r\n \"max\": 5\r\n }\r\n },\r\n {\r\n {\r\n \"min\": 0,\r\n \"max\": 5\r\n }\r\n },\r\n {\r\n
\ \"faction\": [\r\n \"seeker\",\r\n \"mystic\"\r\n ],\r\n \ \"special\": [\r\n \"heals_horror\"\r\n ],\r\n \"tag\":
\ \"level\": {\r\n \"min\": 0,\r\n \"max\": 1\r\n },\r\n [\r\n \"hh\"\r\n ],\r\n \"level\": {\r\n \"min\": 0,\r\n
\ \"limit\": 15,\r\n \"error\": \"You cannot have more than 15 level \ \"max\": 5\r\n }\r\n },\r\n {\r\n \"faction\": [\r\n
0-1 Seeker and/or Mystic cards\"\r\n }\r\n ]\r\n}\r\n" \ \"seeker\",\r\n \"mystic\"\r\n ],\r\n \"level\": {\r\n
\ \"min\": 0,\r\n \"max\": 1\r\n },\r\n \"limit\": 15,\r\n
\ \"error\": \"You cannot have more than 15 level 0-1 Seeker and/or Mystic
cards\"\r\n }\r\n ]\r\n}\r\n"
GUID: 9900a3 GUID: 9900a3
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,20 +22,21 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"09001\",\n \"type\": \"Investigator\",\n \"class\": \"Guardian\",\n GMNotes: "{\n \"id\": \"09001\",\n \"type\": \"Investigator\",\n \"class\": \"Guardian\",\n
\ \"traits\": \"Assistant.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": 2,\n \ \"traits\": \"Assistant.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": 2,\n
\ \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"The Scarlet Keys\",\n \ \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"The Scarlet Keys\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Activate\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"09002\": 2\n },\n {\n \"09003\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"09002\":
1\n }\n ],\n \"choices\": 1\n },\n \"deck_options\": [\n {\n \"faction\": 2\n },\n {\n \"09003\": 1\n }\n ],\n \"choices\": 1\n
[\n \"guardian\",\n \"neutral\"\n ],\n \"level\": {\n \"min\": \ },\n \"deck_options\": [\n {\n \"faction\": [\n \"guardian\",\n
0,\n \"max\": 5\n }\n },\n {\n \"choiceName\": \"Seeker\",\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"faction\": [\n \"seeker\"\n ],\n \"level\": {\n \"min\": 5\n }\n },\n {\n \"choiceName\": \"Seeker\",\n \"faction\":
0,\n \"max\": 1\n },\n \"type\": [\n \"event\",\n \"skill\"\n [\n \"seeker\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ ],\n \"limit\": 10\n },\n {\n \"choiceName\": \"Mystic\",\n 1\n },\n \"type\": [\n \"event\",\n \"skill\"\n ],\n
\ \"faction\": [\n \"mystic\"\n ],\n \"level\": {\n \"min\": \ \"limit\": 10\n },\n {\n \"choiceName\": \"Mystic\",\n \"faction\":
0,\n \"max\": 1\n },\n \"type\": [\n \"event\",\n \"skill\"\n [\n \"mystic\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ ],\n \"limit\": 10\n },\n {\n \"choiceName\": \"Survivor\",\n 1\n },\n \"type\": [\n \"event\",\n \"skill\"\n ],\n
\ \"faction\": [\n \"survivor\"\n ],\n \"level\": {\n \"min\": \ \"limit\": 10\n },\n {\n \"choiceName\": \"Survivor\",\n \"faction\":
0,\n \"max\": 1\n },\n \"type\": [\n \"event\",\n \"skill\"\n [\n \"survivor\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ ],\n \"limit\": 10\n }\n ]\n}" 1\n },\n \"type\": [\n \"event\",\n \"skill\"\n ],\n
\ \"limit\": 10\n }\n ]\n}"
GUID: dc96d1 GUID: dc96d1
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"09018\",\n \"type\": \"Investigator\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"09018\",\n \"type\": \"Investigator\",\n \"class\": \"Neutral\",\n
\ \"traits\": \"Civic. Socialite.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\": \ \"traits\": \"Civic. Socialite.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\":
1,\n \"combatIcons\": 1,\n \"agilityIcons\": 1,\n \"cycle\": \"The Scarlet Keys\",\n 1,\n \"combatIcons\": 1,\n \"agilityIcons\": 1,\n \"cycle\": \"The Scarlet Keys\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"09019\": 1\n },\n {\n \"09020\": 1,\n \"signatures\": [\n {\n \"09019\": 1\n },\n {\n \"09020\":
1\n }\n ],\n \"choices\": 2\n },\n \"deck_options\": [\n {\n \"faction\": 1\n }\n ],\n \"choices\": 2\n },\n \"deck_options\": [\n {\n \"faction\":
[\n \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":

View File

@ -0,0 +1,247 @@
-- Bundled by luabundle {"version":"1.6.0"}
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
local loadingPlaceholder = {[{}] = true}
local register
local modules = {}
local require
local loaded = {}
register = function(name, body)
if not modules[name] then
modules[name] = body
end
end
require = function(name)
local loadedModule = loaded[name]
if loadedModule then
if loadedModule == loadingPlaceholder then
return nil
end
else
if not modules[name] then
if not superRequire then
local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
error('Tried to require ' .. identifier .. ', but no such module has been registered')
else
return superRequire(name)
end
end
loaded[name] = loadingPlaceholder
loadedModule = modules[name](require, loaded, register, modules)
loaded[name] = loadedModule
end
return loadedModule
end
return require, loaded, register, modules
end)(nil)
__bundle_register("playercards/CardsThatRedrawTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that return and redraw tokens
This file is used to add an XML button to a card, turned on via context menu.
Valid options modify the appearance of the XML button, as well as the
behavior of the return and redraw function. Set options before requiring this file.
Parameters for the return and redraw functions. Typically set VALID_TOKENS or INVALID_TOKENS, not both.
If there are no restrictions on which tokens can be redrawn (e.g. Wendy Adams), keep both empty.
* VALID_TOKENS --@type table
- keyed table which lists all tokens that can be redrawn by the card
- example usage: "False Covenant"
> VALID_TOKENS = {
> ["Curse"] = true
> }
* INVALID_TOKENS --@type table
- keyed table which lists all tokens that cannot be redrawn by the card
- example usage: "Custom Ammunition"
> INVALID_TOKENS = {
> ["Auto-fail"] = true
> }
* DRAW_SPECIFIC_TOKEN --@type string (name of token or nil)
- if set, will attempt to draw that specific token
* RETURN_TO_POOL --@type string
- allows for the name of the card to be passed onto Global for any special handling
The following parameters modify the appearence of the XML button and are not listed as part of a table.
- buttonHeight (default is 450)
- buttonWidth (default is 1400)
- buttonPosition (default is "0 -55 -22")
- buttonFontSize (default is 250)
- buttonRotation (change if button is placed on an investigator cards)
- buttonLabel (default is "Redraw Token")
- buttonIcon (to add an icon to the right)
- buttonColor (default is "#77674DE6")
----------------------------------------------------------
EXAMPLE: Claypool's Furs
This card can only redraw the Frost token, and is replaced with a random token from the bag.
As a nice reminder the XML button takes on the Frost color and icon with the text "Cancel".
> buttonValue = "Cancel"
> buttonIcon = "token-frost"
> buttonColor = "#404450E6"
> buttonFontSize = 300
> VALID_TOKENS = {
> ["Frost"] = true
> }
>
> require...
----------------------------------------------------------]]
-- intentionally global
hasXML = true
isHelperEnabled = false
function updateSave()
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
end
function onLoad(savedData)
if savedData and savedData ~= "" then
local loadedData = JSON.decode(savedData)
isHelperEnabled = loadedData.isHelperEnabled
end
createHelperXML()
syncDisplayWithOptionPanel()
end
function createHelperXML()
local xmlTable = { {
tag = "Button",
attributes = {
active = "false",
id = "Helper",
height = buttonHeight or 450,
width = buttonWidth or 1400,
rotation = buttonRotation or "0 0 180",
scale = "0.1 0.1 1",
position = buttonPosition or "0 -55 -22",
padding = "50 50 50 50",
font = "font_teutonic-arkham",
fontSize = buttonFontSize or 250,
onClick = "triggerXMLTokenLabelCreation",
color = buttonColor or "#77674DE6",
textColor = "White"
},
value = buttonLabel or "Redraw Token"
} }
if buttonIcon then
xmlTable[1].attributes.iconWidth = "400"
xmlTable[1].attributes.iconAlignment = "Right"
xmlTable[1].attributes.icon = buttonIcon
end
self.UI.setXmlTable(xmlTable)
end
function triggerXMLTokenLabelCreation()
Global.call("activeRedrawEffect", {
VALID_TOKENS = VALID_TOKENS,
INVALID_TOKENS = INVALID_TOKENS,
RETURN_TO_POOL = RETURN_TO_POOL
})
end
end)
__bundle_register("playercards/CardsWithHelper", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that have helpers
This file is used to share code between cards with helpers.
It syncs the visibility of the helper with the option panel and
makes sure the card has the respective tag.
Additionally, it will call 'initiliaze()' and 'shutOff()'
in the parent file if they are present.
Instructions:
1) Define the global variables before requiring this file:
hasXML = true (whether the card has an XML display)
isHelperEnabled = false (default state of the helper, should be 'false')
2) In 'onLoad()'', call 'syncDisplayWithOptionPanel()'
----------------------------------------------------------]]
local optionPanelApi = require("core/OptionPanelApi")
-- if the respective option is enabled in onLoad(), enable the helper
function syncDisplayWithOptionPanel()
self.addTag("CardWithHelper")
local options = optionPanelApi.getOptions()
if options.enableCardHelpers then
setHelperState(true)
else
updateDisplay()
end
end
-- forces a new state
function setHelperState(newState)
isHelperEnabled = newState
updateSave()
updateDisplay()
end
-- toggles the current state
function toggleHelper()
isHelperEnabled = not isHelperEnabled
updateSave()
updateDisplay()
end
-- updates the visibility and calls events (after a small delay to allow XML being set)
function updateDisplay()
Wait.frames(actualDisplayUpdate, 5)
end
function actualDisplayUpdate()
if isHelperEnabled then
self.clearContextMenu()
self.addContextMenuItem("Disable Helper", toggleHelper)
if hasXML then self.UI.show("Helper") end
if initialize then initialize() end
else
self.clearContextMenu()
self.addContextMenuItem("Enable Helper", toggleHelper)
if hasXML then self.UI.hide("Helper") end
if shutOff then shutOff() end
end
end
end)
__bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local OptionPanelApi = {}
-- loads saved options
---@param options table Set a new state for the option table
OptionPanelApi.loadSettings = function(options)
return Global.call("loadSettings", options)
end
---@return any: Table of option panel state
OptionPanelApi.getOptions = function()
return Global.getTable("optionPanel")
end
return OptionPanelApi
end
end)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/cards/ClaypoolsFurs")
end)
__bundle_register("playercards/cards/ClaypoolsFurs", function(require, _LOADED, __bundle_register, __bundle_modules)
buttonLabel = "Cancel"
buttonIcon = "token-frost"
buttonColor = "#404450E6"
buttonFontSize = 300
VALID_TOKENS = {
["Frost"] = true
}
require("playercards/CardsWithHelper")
require("playercards/CardsThatRedrawTokens")
end)
return __bundle_require("__root")

View File

@ -30,7 +30,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: '' LuaScript: !include 'Card Claypool''s Furs c1f999.ttslua'
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"90031\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90031\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n
\ \"traits\": \"Task.\",\n \"weakness\": true,\n \"uses\": [\n {\n \"count\": \ \"traits\": \"Task.\",\n \"weakness\": true,\n \"uses\": [\n {\n \"count\":
4,\n \"type\": \"Clue\",\n \"token\": \"clue\"\n }\n ],\n \"cycle\": 4,\n \"type\": \"Clue\",\n \"token\": \"clue\"\n }\n ],\n \"cycle\":
\"Standalone\"\n}" \"By the Book\"\n}"
GUID: f802e3 GUID: f802e3
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -0,0 +1,242 @@
-- Bundled by luabundle {"version":"1.6.0"}
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
local loadingPlaceholder = {[{}] = true}
local register
local modules = {}
local require
local loaded = {}
register = function(name, body)
if not modules[name] then
modules[name] = body
end
end
require = function(name)
local loadedModule = loaded[name]
if loadedModule then
if loadedModule == loadingPlaceholder then
return nil
end
else
if not modules[name] then
if not superRequire then
local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
error('Tried to require ' .. identifier .. ', but no such module has been registered')
else
return superRequire(name)
end
end
loaded[name] = loadingPlaceholder
loadedModule = modules[name](require, loaded, register, modules)
loaded[name] = loadedModule
end
return loadedModule
end
return require, loaded, register, modules
end)(nil)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/cards/CustomModifications")
end)
__bundle_register("playercards/cards/CustomModifications", function(require, _LOADED, __bundle_register, __bundle_modules)
INVALID_TOKENS = {
["Auto-fail"] = true
}
require("playercards/CardsWithHelper")
require("playercards/CardsThatRedrawTokens")
end)
__bundle_register("playercards/CardsThatRedrawTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that return and redraw tokens
This file is used to add an XML button to a card, turned on via context menu.
Valid options modify the appearance of the XML button, as well as the
behavior of the return and redraw function. Set options before requiring this file.
Parameters for the return and redraw functions. Typically set VALID_TOKENS or INVALID_TOKENS, not both.
If there are no restrictions on which tokens can be redrawn (e.g. Wendy Adams), keep both empty.
* VALID_TOKENS --@type table
- keyed table which lists all tokens that can be redrawn by the card
- example usage: "False Covenant"
> VALID_TOKENS = {
> ["Curse"] = true
> }
* INVALID_TOKENS --@type table
- keyed table which lists all tokens that cannot be redrawn by the card
- example usage: "Custom Ammunition"
> INVALID_TOKENS = {
> ["Auto-fail"] = true
> }
* DRAW_SPECIFIC_TOKEN --@type string (name of token or nil)
- if set, will attempt to draw that specific token
* RETURN_TO_POOL --@type string
- allows for the name of the card to be passed onto Global for any special handling
The following parameters modify the appearence of the XML button and are not listed as part of a table.
- buttonHeight (default is 450)
- buttonWidth (default is 1400)
- buttonPosition (default is "0 -55 -22")
- buttonFontSize (default is 250)
- buttonRotation (change if button is placed on an investigator cards)
- buttonLabel (default is "Redraw Token")
- buttonIcon (to add an icon to the right)
- buttonColor (default is "#77674DE6")
----------------------------------------------------------
EXAMPLE: Claypool's Furs
This card can only redraw the Frost token, and is replaced with a random token from the bag.
As a nice reminder the XML button takes on the Frost color and icon with the text "Cancel".
> buttonValue = "Cancel"
> buttonIcon = "token-frost"
> buttonColor = "#404450E6"
> buttonFontSize = 300
> VALID_TOKENS = {
> ["Frost"] = true
> }
>
> require...
----------------------------------------------------------]]
-- intentionally global
hasXML = true
isHelperEnabled = false
function updateSave()
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
end
function onLoad(savedData)
if savedData and savedData ~= "" then
local loadedData = JSON.decode(savedData)
isHelperEnabled = loadedData.isHelperEnabled
end
createHelperXML()
syncDisplayWithOptionPanel()
end
function createHelperXML()
local xmlTable = { {
tag = "Button",
attributes = {
active = "false",
id = "Helper",
height = buttonHeight or 450,
width = buttonWidth or 1400,
rotation = buttonRotation or "0 0 180",
scale = "0.1 0.1 1",
position = buttonPosition or "0 -55 -22",
padding = "50 50 50 50",
font = "font_teutonic-arkham",
fontSize = buttonFontSize or 250,
onClick = "triggerXMLTokenLabelCreation",
color = buttonColor or "#77674DE6",
textColor = "White"
},
value = buttonLabel or "Redraw Token"
} }
if buttonIcon then
xmlTable[1].attributes.iconWidth = "400"
xmlTable[1].attributes.iconAlignment = "Right"
xmlTable[1].attributes.icon = buttonIcon
end
self.UI.setXmlTable(xmlTable)
end
function triggerXMLTokenLabelCreation()
Global.call("activeRedrawEffect", {
VALID_TOKENS = VALID_TOKENS,
INVALID_TOKENS = INVALID_TOKENS,
RETURN_TO_POOL = RETURN_TO_POOL
})
end
end)
__bundle_register("playercards/CardsWithHelper", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that have helpers
This file is used to share code between cards with helpers.
It syncs the visibility of the helper with the option panel and
makes sure the card has the respective tag.
Additionally, it will call 'initiliaze()' and 'shutOff()'
in the parent file if they are present.
Instructions:
1) Define the global variables before requiring this file:
hasXML = true (whether the card has an XML display)
isHelperEnabled = false (default state of the helper, should be 'false')
2) In 'onLoad()'', call 'syncDisplayWithOptionPanel()'
----------------------------------------------------------]]
local optionPanelApi = require("core/OptionPanelApi")
-- if the respective option is enabled in onLoad(), enable the helper
function syncDisplayWithOptionPanel()
self.addTag("CardWithHelper")
local options = optionPanelApi.getOptions()
if options.enableCardHelpers then
setHelperState(true)
else
updateDisplay()
end
end
-- forces a new state
function setHelperState(newState)
isHelperEnabled = newState
updateSave()
updateDisplay()
end
-- toggles the current state
function toggleHelper()
isHelperEnabled = not isHelperEnabled
updateSave()
updateDisplay()
end
-- updates the visibility and calls events (after a small delay to allow XML being set)
function updateDisplay()
Wait.frames(actualDisplayUpdate, 5)
end
function actualDisplayUpdate()
if isHelperEnabled then
self.clearContextMenu()
self.addContextMenuItem("Disable Helper", toggleHelper)
if hasXML then self.UI.show("Helper") end
if initialize then initialize() end
else
self.clearContextMenu()
self.addContextMenuItem("Enable Helper", toggleHelper)
if hasXML then self.UI.hide("Helper") end
if shutOff then shutOff() end
end
end
end)
__bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local OptionPanelApi = {}
-- loads saved options
---@param options table Set a new state for the option table
OptionPanelApi.loadSettings = function(options)
return Global.call("loadSettings", options)
end
---@return any: Table of option panel state
OptionPanelApi.getOptions = function()
return Global.getTable("optionPanel")
end
return OptionPanelApi
end
end)
return __bundle_require("__root")

View File

@ -44,7 +44,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: '' LuaScript: !include 'Card Custom Modifications d2252d.ttslua'
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card

View File

@ -21,18 +21,19 @@ Description: The Librarian
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01002-pb\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"01002-pb\",\n \"type\": \"Investigator\",\n \"class\":
\"Seeker\",\n \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\": \"Seeker\",\n \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\":
5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Core\",\n \"deck_requirements\": 5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Read or Die\",\n
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \ \"extraToken\": \"Tome\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
[\n {\n \"90002\": 1,\n \"01008\": 1\n },\n {\n \"90003\": 1,\n \"signatures\": [\n {\n \"90002\": 1,\n \"01008\": 1\n
1,\n \"01009\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"trait\": \ },\n {\n \"90003\": 1,\n \"01009\": 1\n }\n ]\n
[\n \"tome\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ },\n \"deck_options\": [\n {\n \"trait\": [\n \"tome\"\n ],\n
5\n }\n },\n {\n \"faction\": [\n \"neutral\"\n ],\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
\ \"faction\": [\n \"seeker\"\n ],\n \"level\": {\n \"min\": \ \"faction\": [\n \"neutral\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 3\n }\n },\n {\n \"faction\": [\n \"guardian\",\n 0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"seeker\"\n
\ \"mystic\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5 \ },\n {\n \"faction\": [\n \"guardian\",\n \"mystic\"\n
Guardian and/or Mystic cards\"\n }\n ]\n}" \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n },\n
\ \"limit\": 5,\n \"error\": \"You cannot have more than 5 Guardian and/or
Mystic cards\"\n }\n ]\n}"
GUID: 2f2e0d GUID: 2f2e0d
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,14 +21,14 @@ Description: The Librarian
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01002-pf\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"01002-pf\",\n \"type\": \"Investigator\",\n \"class\":
\"Seeker\",\n \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\": \"Seeker\",\n \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\":
5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Core\",\n \"deck_requirements\": 5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Read or Die\",\n
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
[\n {\n \"90002\": 1,\n \"01008\": 1\n },\n {\n \"90003\": 1,\n \"signatures\": [\n {\n \"90002\": 1,\n \"01008\": 1\n
1,\n \"01009\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": \ },\n {\n \"90003\": 1,\n \"01009\": 1\n }\n ]\n
[\n \"seeker\",\n \"neutral\"\n ],\n \"level\": {\n \"min\": \ },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n
0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"mystic\"\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n 5\n }\n },\n {\n \"faction\": [\n \"mystic\"\n ],\n
\ }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}"
GUID: e8cafc GUID: e8cafc
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,18 +21,19 @@ Description: The Librarian
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01002-p\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"01002-p\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n
\ \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\": \ \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\":
5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Core\",\n \"deck_requirements\": 5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Read or Die\",\n
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
[\n {\n \"90002\": 1,\n \"01008\": 1\n },\n {\n \"90003\": 1,\n \"signatures\": [\n {\n \"90002\": 1,\n \"01008\": 1\n
1,\n \"01009\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"trait\": \ },\n {\n \"90003\": 1,\n \"01009\": 1\n }\n ]\n
[\n \"tome\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ },\n \"deck_options\": [\n {\n \"trait\": [\n \"tome\"\n ],\n
5\n }\n },\n {\n \"faction\": [\n \"neutral\"\n ],\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
\ \"faction\": [\n \"seeker\"\n ],\n \"level\": {\n \"min\": \ \"faction\": [\n \"neutral\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 3\n }\n },\n {\n \"faction\": [\n \"guardian\",\n 0,\n \"max\": 5\n }\n },\n {\n \"faction\": [\n \"seeker\"\n
\ \"mystic\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 3\n }\n
0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5 \ },\n {\n \"faction\": [\n \"guardian\",\n \"mystic\"\n
Guardian and/or Mystic cards\"\n }\n ]\n}" \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n },\n
\ \"limit\": 5,\n \"error\": \"You cannot have more than 5 Guardian and/or
Mystic cards\"\n }\n ]\n}"
GUID: '282857' GUID: '282857'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,10 +22,10 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"01002\",\n \"alternate_ids\": [\n \"01502\"\n ],\n \"type\": GMNotes: "{\n \"id\": \"01002\",\n \"alternate_ids\": [\n \"01502\"\n ],\n \"type\":
\"Investigator\",\n \"class\": \"Seeker\",\n \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": \"Investigator\",\n \"class\": \"Seeker\",\n \"traits\": \"Miskatonic.\",\n \"willpowerIcons\":
3,\n \"intellectIcons\": 5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": 3,\n \"intellectIcons\": 5,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\":
\"Core\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \"Core\",\n \"extraToken\": \"Tome\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"90002\": 1,\n \"01008\": 1\n 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90002\":
\ },\n {\n \"90003\": 1,\n \"01009\": 1\n }\n ]\n 1,\n \"01008\": 1\n },\n {\n \"90003\": 1,\n \"01009\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
5\n }\n },\n {\n \"faction\": [\n \"mystic\"\n ],\n 5\n }\n },\n {\n \"faction\": [\n \"mystic\"\n ],\n
\ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}"
@ -71,14 +71,15 @@ States:
\ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Seeker\",\r\n \"traits\": \ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Seeker\",\r\n \"traits\":
\"Miskatonic.\",\r\n \"willpowerIcons\": 3,\r\n \"intellectIcons\": 5,\r\n \"Miskatonic.\",\r\n \"willpowerIcons\": 3,\r\n \"intellectIcons\": 5,\r\n
\ \"combatIcons\": 2,\r\n \"agilityIcons\": 2,\r\n \"cycle\": \"Core\",\r\n \ \"combatIcons\": 2,\r\n \"agilityIcons\": 2,\r\n \"cycle\": \"Core\",\r\n
\ \"deck_requirements\": {\r\n \"size\": 30,\r\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Tome\",\r\n \"deck_requirements\": {\r\n \"size\": 30,\r\n
1,\r\n \"signatures\": [\r\n {\r\n \"90002\": 1,\r\n \"01008\": \ \"randomBasicWeaknessCount\": 1,\r\n \"signatures\": [\r\n {\r\n
1\r\n },\r\n {\r\n \"90003\": 1,\r\n \"01009\": 1\r\n \ \"90002\": 1,\r\n \"01008\": 1\r\n },\r\n {\r\n \"90003\":
\ }\r\n ]\r\n },\r\n \"deck_options\": [\r\n {\r\n \"faction\": 1,\r\n \"01009\": 1\r\n }\r\n ]\r\n },\r\n \"deck_options\":
[\r\n \"seeker\",\r\n \"neutral\"\r\n ],\r\n \"level\": [\r\n {\r\n \"faction\": [\r\n \"seeker\",\r\n \"neutral\"\r\n
{\r\n \"min\": 0,\r\n \"max\": 5\r\n }\r\n },\r\n {\r\n \ ],\r\n \"level\": {\r\n \"min\": 0,\r\n \"max\": 5\r\n
\ \"faction\": [\r\n \"mystic\"\r\n ],\r\n \"level\": {\r\n \ }\r\n },\r\n {\r\n \"faction\": [\r\n \"mystic\"\r\n
\ \"min\": 0,\r\n \"max\": 2\r\n }\r\n }\r\n ]\r\n}\r\n" \ ],\r\n \"level\": {\r\n \"min\": 0,\r\n \"max\": 2\r\n
\ }\r\n }\r\n ]\r\n}\r\n"
GUID: ac7047 GUID: ac7047
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"08001\",\n \"type\": \"Investigator\",\n \"class\": \"Guardian\",\n GMNotes: "{\n \"id\": \"08001\",\n \"type\": \"Investigator\",\n \"class\": \"Guardian\",\n
\ \"traits\": \"Entrepreneur.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \ \"traits\": \"Entrepreneur.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
1,\n \"combatIcons\": 5,\n \"agilityIcons\": 2,\n \"cycle\": \"Edge of the Earth\",\n 1,\n \"combatIcons\": 5,\n \"agilityIcons\": 2,\n \"cycle\": \"Edge of the Earth\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"08002\": 1\n },\n {\n \"08003\": 1,\n \"signatures\": [\n {\n \"08002\": 1\n },\n {\n \"08003\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"guardian\"\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"guardian\"\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n }\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n }\n

View File

@ -20,7 +20,8 @@ CustomDeck:
Description: Advanced Description: Advanced
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90019\",\n \"type\": \"Event\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90019\",\n \"type\": \"Event\",\n \"class\": \"Neutral\",\n
\ \"cost\": 4,\n \"traits\": \"Spell.\",\n \"weakness\": true,\n \"cycle\": \"Standalone\"\n}" \ \"cost\": 4,\n \"traits\": \"Spell.\",\n \"weakness\": true,\n \"cycle\": \"Bad
Blood\"\n}"
GUID: 580a4d GUID: 580a4d
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"09015\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n GMNotes: "{\n \"id\": \"09015\",\n \"type\": \"Investigator\",\n \"class\": \"Survivor\",\n
\ \"traits\": \"Reporter.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": 5,\n \ \"traits\": \"Reporter.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": 5,\n
\ \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Scarlet Keys\",\n \ \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Scarlet Keys\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"09016\": 1\n },\n {\n \"09017\": 1,\n \"signatures\": [\n {\n \"09016\": 1\n },\n {\n \"09017\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"survivor\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":

View File

@ -22,10 +22,11 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"07004\",\n \"alternate_ids\": [\n \"98016\"\n ],\n \"type\": GMNotes: "{\n \"id\": \"07004\",\n \"alternate_ids\": [\n \"98016\"\n ],\n \"type\":
\"Investigator\",\n \"class\": \"Mystic\",\n \"traits\": \"Sorcerer. Veteran.\",\n \"Investigator\",\n \"class\": \"Mystic\",\n \"traits\": \"Sorcerer. Veteran.\",\n
\ \"willpowerIcons\": 5,\n \"intellectIcons\": 2,\n \"combatIcons\": 3,\n \"agilityIcons\": \ \"willpowerIcons\": 5,\n \"intellectIcons\": 2,\n \"combatIcons\": 3,\n \"agilityIcons\":
2,\n \"cycle\": \"The Innsmouth Conspiracy\",\n \"deck_requirements\": {\n \"size\": 2,\n \"cycle\": \"The Innsmouth Conspiracy\",\n \"extraToken\": \"FreeTrigger\",\n
30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"98017\": \ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"07012\": 1\n },\n {\n \"98018\": 1,\n \"07013\": 1,\n \"signatures\": [\n {\n \"98017\": 1,\n \"07012\": 1\n
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n \ },\n {\n \"98018\": 1,\n \"07013\": 1\n }\n ]\n
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
5\n }\n },\n {\n \"faction\": [\n \"rogue\"\n ],\n \"level\": 5\n }\n },\n {\n \"faction\": [\n \"rogue\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}" {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}"
@ -71,15 +72,15 @@ States:
\ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Mystic\",\r\n \"traits\": \ ],\r\n \"type\": \"Investigator\",\r\n \"class\": \"Mystic\",\r\n \"traits\":
\"Sorcerer. Veteran.\",\r\n \"willpowerIcons\": 5,\r\n \"intellectIcons\": \"Sorcerer. Veteran.\",\r\n \"willpowerIcons\": 5,\r\n \"intellectIcons\":
2,\r\n \"combatIcons\": 3,\r\n \"agilityIcons\": 2,\r\n \"cycle\": \"The 2,\r\n \"combatIcons\": 3,\r\n \"agilityIcons\": 2,\r\n \"cycle\": \"The
Innsmouth Conspiracy\",\r\n \"deck_requirements\": {\r\n \"size\": 30,\r\n Innsmouth Conspiracy\",\r\n \"extraToken\": \"Lightning\",\r\n \"deck_requirements\":
\ \"randomBasicWeaknessCount\": 1,\r\n \"signatures\": [\r\n {\r\n {\r\n \"size\": 30,\r\n \"randomBasicWeaknessCount\": 1,\r\n \"signatures\":
\ \"98017\": 1,\r\n \"07012\": 1\r\n },\r\n {\r\n \"98018\": [\r\n {\r\n \"98017\": 1,\r\n \"07012\": 1\r\n },\r\n
1,\r\n \"07013\": 1\r\n }\r\n ]\r\n },\r\n \"deck_options\": \ {\r\n \"98018\": 1,\r\n \"07013\": 1\r\n }\r\n ]\r\n
[\r\n {\r\n \"faction\": [\r\n \"mystic\",\r\n \"neutral\"\r\n \ },\r\n \"deck_options\": [\r\n {\r\n \"faction\": [\r\n \"mystic\",\r\n
\ ],\r\n \"level\": {\r\n \"min\": 0,\r\n \"max\": 5\r\n \ \"neutral\"\r\n ],\r\n \"level\": {\r\n \"min\": 0,\r\n
\ }\r\n },\r\n {\r\n \"faction\": [\r\n \"rogue\"\r\n \ \"max\": 5\r\n }\r\n },\r\n {\r\n \"faction\": [\r\n
\ ],\r\n \"level\": {\r\n \"min\": 0,\r\n \"max\": 2\r\n \ \"rogue\"\r\n ],\r\n \"level\": {\r\n \"min\": 0,\r\n
\ }\r\n }\r\n ]\r\n}\r\n" \ \"max\": 2\r\n }\r\n }\r\n ]\r\n}\r\n"
GUID: 3925ce GUID: 3925ce
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,13 +22,13 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"05004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"05004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Cultist. Silver Twilight.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\": \ \"traits\": \"Cultist. Silver Twilight.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\":
3,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Circle Undone\",\n 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": \"The Circle Undone\",\n
\ \"deck_requirements\": {\n \"size\": 35,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 35,\n
1,\n \"signatures\": [\n {\n \"05013\": 1\n },\n {\n \"05014\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"05013\":
1\n },\n {\n \"05015\": 1\n }\n ]\n },\n \"deck_options\": 1\n },\n {\n \"05014\": 1\n },\n {\n \"05015\":
[\n {\n \"faction\": [\n \"mystic\",\n \"neutral\"\n ],\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"faction\": [\n \"guardian\"\n ],\n \"level\": {\n \"min\": 5\n }\n },\n {\n \"faction\": [\n \"guardian\"\n ],\n
0,\n \"max\": 2\n }\n }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}"
GUID: 32b091 GUID: 32b091
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,7 +20,7 @@ CustomDeck:
Description: Leave No Doubt Description: Leave No Doubt
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90029\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90029\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n
\ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"Standalone\"\n}" \ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"By the Book\"\n}"
GUID: 07e7bd GUID: 07e7bd
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,7 +20,7 @@ CustomDeck:
Description: Seek the Truth Description: Seek the Truth
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90028\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90028\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n
\ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"Standalone\"\n}" \ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"By the Book\"\n}"
GUID: 0994c9 GUID: 0994c9
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,7 +20,7 @@ CustomDeck:
Description: Due Diligence Description: Due Diligence
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90025\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90025\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n
\ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"Standalone\"\n}" \ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"By the Book\"\n}"
GUID: '133521' GUID: '133521'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,7 +20,7 @@ CustomDeck:
Description: Consult Experts Description: Consult Experts
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90027\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90027\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n
\ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"Standalone\"\n}" \ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"By the Book\"\n}"
GUID: 2d9256 GUID: 2d9256
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,7 +20,7 @@ CustomDeck:
Description: Red Tape Description: Red Tape
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90026\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90026\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n
\ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"Standalone\"\n}" \ \"startsInPlay\": true,\n \"permanent\": true,\n \"cycle\": \"By the Book\"\n}"
GUID: '706176' GUID: '706176'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,8 +20,9 @@ CustomDeck:
Description: '' Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"09104\",\n \"type\": \"Event\",\n \"class\": \"Survivor\",\n GMNotes: "{\n \"id\": \"09104\",\n \"type\": \"Event\",\n \"class\": \"Survivor\",\n
\ \"cost\": 0,\n \"level\": 0,\n \"traits\": \"Insight. Spirit.\",\n \"cycle\": \ \"cost\": 0,\n \"level\": 0,\n \"traits\": \"Insight. Spirit.\",\n \"uses\":
\"The Scarlet Keys\"\n}" [\n {\n \"count\": 1,\n \"type\": \"Universal\",\n \"token\":
\"universalActionAbility\"\n }\n ],\n \"cycle\": \"The Scarlet Keys\"\n}"
GUID: a3d041 GUID: a3d041
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,8 +20,9 @@ CustomDeck:
Description: '' Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"04148\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"04148\",\n \"type\": \"Asset\",\n \"class\": \"Neutral\",\n
\ \"cost\": 2,\n \"traits\": \"Item. Tome.\",\n \"intellectIcons\": 2,\n \"cycle\": \ \"cost\": 2,\n \"traits\": \"Item. Tome.\",\n \"intellectIcons\": 2,\n \"uses\":
\"The Forgotten Age\"\n}" [\n {\n \"count\": 1,\n \"type\": \"Explore\",\n \"token\": \"universalActionAbility\"\n
\ }\n ],\n \"cycle\": \"The Forgotten Age\"\n}"
GUID: 9dc3d4 GUID: 9dc3d4
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,8 +21,9 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"10082\",\n \"type\": \"Asset\",\n \"slot\": \"Hand\",\n GMNotes: "{\n \"id\": \"10082\",\n \"type\": \"Asset\",\n \"slot\": \"Hand\",\n
\ \"class\": \"Rogue\",\n \"cost\": 2,\n \"level\": 4,\n \"traits\": \"Item. \ \"class\": \"Rogue\",\n \"cost\": 2,\n \"level\": 4,\n \"traits\": \"Item.
Illicit.\",\n \"intellectIcons\": 1,\n \"agilityIcons\": 1,\n \"cycle\": \"The Illicit.\",\n \"intellectIcons\": 1,\n \"agilityIcons\": 1,\n \"uses\": [\n {\n
Feast of Hemlock Vale\"\n}" \ \"count\": 0,\n \"type\": \"Suspicion\",\n \"token\": \"resource\"\n
\ }\n ],\n \"cycle\": \"The Feast of Hemlock Vale\"\n}"
GUID: 7ebb67 GUID: 7ebb67
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,7 +21,9 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"10066\",\n \"type\": \"Asset\",\n \"slot\": \"Hand\",\n GMNotes: "{\n \"id\": \"10066\",\n \"type\": \"Asset\",\n \"slot\": \"Hand\",\n
\ \"class\": \"Rogue\",\n \"cost\": 3,\n \"level\": 0,\n \"traits\": \"Item. \ \"class\": \"Rogue\",\n \"cost\": 3,\n \"level\": 0,\n \"traits\": \"Item.
Illicit.\",\n \"intellectIcons\": 1,\n \"cycle\": \"The Feast of Hemlock Vale\"\n}" Illicit.\",\n \"intellectIcons\": 1,\n \"uses\": [\n {\n \"count\": 0,\n
\ \"type\": \"Suspicion\",\n \"token\": \"resource\"\n }\n ],\n \"cycle\":
\"The Feast of Hemlock Vale\"\n}"
GUID: acd38d GUID: acd38d
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -0,0 +1,248 @@
-- Bundled by luabundle {"version":"1.6.0"}
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
local loadingPlaceholder = {[{}] = true}
local register
local modules = {}
local require
local loaded = {}
register = function(name, body)
if not modules[name] then
modules[name] = body
end
end
require = function(name)
local loadedModule = loaded[name]
if loadedModule then
if loadedModule == loadingPlaceholder then
return nil
end
else
if not modules[name] then
if not superRequire then
local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
error('Tried to require ' .. identifier .. ', but no such module has been registered')
else
return superRequire(name)
end
end
loaded[name] = loadingPlaceholder
loadedModule = modules[name](require, loaded, register, modules)
loaded[name] = loadedModule
end
return loadedModule
end
return require, loaded, register, modules
end)(nil)
__bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local OptionPanelApi = {}
-- loads saved options
---@param options table Set a new state for the option table
OptionPanelApi.loadSettings = function(options)
return Global.call("loadSettings", options)
end
---@return any: Table of option panel state
OptionPanelApi.getOptions = function()
return Global.getTable("optionPanel")
end
return OptionPanelApi
end
end)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/cards/FalseCovenant")
end)
__bundle_register("playercards/cards/FalseCovenant", function(require, _LOADED, __bundle_register, __bundle_modules)
buttonLabel = "Cancel"
buttonIcon = "token-curse"
buttonColor = "#633A84E6"
buttonFontSize = 300
RETURN_TO_POOL = true
VALID_TOKENS = {
["Curse"] = true
}
require("playercards/CardsWithHelper")
require("playercards/CardsThatRedrawTokens")
end)
__bundle_register("playercards/CardsThatRedrawTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that return and redraw tokens
This file is used to add an XML button to a card, turned on via context menu.
Valid options modify the appearance of the XML button, as well as the
behavior of the return and redraw function. Set options before requiring this file.
Parameters for the return and redraw functions. Typically set VALID_TOKENS or INVALID_TOKENS, not both.
If there are no restrictions on which tokens can be redrawn (e.g. Wendy Adams), keep both empty.
* VALID_TOKENS --@type table
- keyed table which lists all tokens that can be redrawn by the card
- example usage: "False Covenant"
> VALID_TOKENS = {
> ["Curse"] = true
> }
* INVALID_TOKENS --@type table
- keyed table which lists all tokens that cannot be redrawn by the card
- example usage: "Custom Ammunition"
> INVALID_TOKENS = {
> ["Auto-fail"] = true
> }
* DRAW_SPECIFIC_TOKEN --@type string (name of token or nil)
- if set, will attempt to draw that specific token
* RETURN_TO_POOL --@type string
- allows for the name of the card to be passed onto Global for any special handling
The following parameters modify the appearence of the XML button and are not listed as part of a table.
- buttonHeight (default is 450)
- buttonWidth (default is 1400)
- buttonPosition (default is "0 -55 -22")
- buttonFontSize (default is 250)
- buttonRotation (change if button is placed on an investigator cards)
- buttonLabel (default is "Redraw Token")
- buttonIcon (to add an icon to the right)
- buttonColor (default is "#77674DE6")
----------------------------------------------------------
EXAMPLE: Claypool's Furs
This card can only redraw the Frost token, and is replaced with a random token from the bag.
As a nice reminder the XML button takes on the Frost color and icon with the text "Cancel".
> buttonValue = "Cancel"
> buttonIcon = "token-frost"
> buttonColor = "#404450E6"
> buttonFontSize = 300
> VALID_TOKENS = {
> ["Frost"] = true
> }
>
> require...
----------------------------------------------------------]]
-- intentionally global
hasXML = true
isHelperEnabled = false
function updateSave()
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
end
function onLoad(savedData)
if savedData and savedData ~= "" then
local loadedData = JSON.decode(savedData)
isHelperEnabled = loadedData.isHelperEnabled
end
createHelperXML()
syncDisplayWithOptionPanel()
end
function createHelperXML()
local xmlTable = { {
tag = "Button",
attributes = {
active = "false",
id = "Helper",
height = buttonHeight or 450,
width = buttonWidth or 1400,
rotation = buttonRotation or "0 0 180",
scale = "0.1 0.1 1",
position = buttonPosition or "0 -55 -22",
padding = "50 50 50 50",
font = "font_teutonic-arkham",
fontSize = buttonFontSize or 250,
onClick = "triggerXMLTokenLabelCreation",
color = buttonColor or "#77674DE6",
textColor = "White"
},
value = buttonLabel or "Redraw Token"
} }
if buttonIcon then
xmlTable[1].attributes.iconWidth = "400"
xmlTable[1].attributes.iconAlignment = "Right"
xmlTable[1].attributes.icon = buttonIcon
end
self.UI.setXmlTable(xmlTable)
end
function triggerXMLTokenLabelCreation()
Global.call("activeRedrawEffect", {
VALID_TOKENS = VALID_TOKENS,
INVALID_TOKENS = INVALID_TOKENS,
RETURN_TO_POOL = RETURN_TO_POOL
})
end
end)
__bundle_register("playercards/CardsWithHelper", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that have helpers
This file is used to share code between cards with helpers.
It syncs the visibility of the helper with the option panel and
makes sure the card has the respective tag.
Additionally, it will call 'initiliaze()' and 'shutOff()'
in the parent file if they are present.
Instructions:
1) Define the global variables before requiring this file:
hasXML = true (whether the card has an XML display)
isHelperEnabled = false (default state of the helper, should be 'false')
2) In 'onLoad()'', call 'syncDisplayWithOptionPanel()'
----------------------------------------------------------]]
local optionPanelApi = require("core/OptionPanelApi")
-- if the respective option is enabled in onLoad(), enable the helper
function syncDisplayWithOptionPanel()
self.addTag("CardWithHelper")
local options = optionPanelApi.getOptions()
if options.enableCardHelpers then
setHelperState(true)
else
updateDisplay()
end
end
-- forces a new state
function setHelperState(newState)
isHelperEnabled = newState
updateSave()
updateDisplay()
end
-- toggles the current state
function toggleHelper()
isHelperEnabled = not isHelperEnabled
updateSave()
updateDisplay()
end
-- updates the visibility and calls events (after a small delay to allow XML being set)
function updateDisplay()
Wait.frames(actualDisplayUpdate, 5)
end
function actualDisplayUpdate()
if isHelperEnabled then
self.clearContextMenu()
self.addContextMenuItem("Disable Helper", toggleHelper)
if hasXML then self.UI.show("Helper") end
if initialize then initialize() end
else
self.clearContextMenu()
self.addContextMenuItem("Enable Helper", toggleHelper)
if hasXML then self.UI.hide("Helper") end
if shutOff then shutOff() end
end
end
end)
return __bundle_require("__root")

View File

@ -30,7 +30,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: '' LuaScript: !include 'Card False Covenant (2) 3442f5.ttslua'
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card

View File

@ -0,0 +1,39 @@
<!-- include playercards/FamilyInheritance.xml -->
<Defaults>
<Button padding="30 30 30 30"
font="font_teutonic-arkham"
textColor="white"
fontSize="235"
shadow="#405041B3"
shadowDistance="-15 15"/>
<TableLayout position="130 0 -22"
rotation="0 0 270"
height="460"
width="2600"
scale="0.1 0.1 1"
cellSpacing="80"
cellBackgroundColor="rgba(1,1,1,0)"/>
</Defaults>
<TableLayout id="Helper"
active="false">
<Row>
<Cell>
<Button onClick="loseAll"
color="#6D202C"
fontSize="195"
text="Discard all"/>
</Cell>
<Cell>
<Button onClick="takeAll"
color="#173B0B"
text="Move all"/>
</Cell>
<Cell>
<Button onClick="add4"
color="#77674D"
text="Place 4"/>
</Cell>
</Row>
</TableLayout>
<!-- include playercards/FamilyInheritance.xml -->

View File

@ -54,4 +54,4 @@ Transform:
scaleY: 1 scaleY: 1
scaleZ: 1 scaleZ: 1
Value: 0 Value: 0
XmlUI: '' XmlUI: !include 'Card Family Inheritance 394603.xml'

View File

@ -22,12 +22,13 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"04004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"04004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Believer. Warden.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \ \"traits\": \"Believer. Warden.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
3,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Forgotten 3,\n \"combatIcons\": 2,\n \"agilityIcons\": 3,\n \"cycle\": \"The Forgotten
Age\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Age\",\n \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"04013\": 1\n },\n {\n \"04014\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"04013\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n 1\n },\n {\n \"04014\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"mystic\",\n \"neutral\"\n ],\n
5\n }\n },\n {\n \"trait\": [\n \"blessed\"\n ],\n \"level\": \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
{\n \"min\": 0,\n \"max\": 3\n }\n }\n ]\n}" \ \"trait\": [\n \"blessed\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 3\n }\n }\n ]\n}"
GUID: eb96e6 GUID: eb96e6
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -44,86 +44,112 @@ end)(nil)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/cards/FavoroftheMoon1") require("playercards/cards/FavoroftheMoon1")
end) end)
__bundle_register("playercards/cards/FavoroftheMoon1", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("chaosbag/ChaosBagApi", function(require, _LOADED, __bundle_register, __bundle_modules)
VALID_TOKENS = {
["Curse"] = true
}
SHOW_SINGLE_RELEASE = true
KEEP_OPEN = true
RESOLVE_TOKEN = true
require("playercards/CardsThatSealTokens")
end)
__bundle_register("chaosbag/BlessCurseManagerApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local BlessCurseManagerApi = {} local ChaosBagApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
local function getManager() -- respawns the chaos bag with a new state of tokens
return guidReferenceApi.getObjectByOwnerAndType("Mythos", "BlessCurseManager") ---@param tokenList table List of chaos token ids
ChaosBagApi.setChaosBagState = function(tokenList)
Global.call("setChaosBagState", tokenList)
end end
-- removes all taken tokens and resets the counts -- returns a Table List of chaos token ids in the current chaos bag
BlessCurseManagerApi.removeTakenTokensAndReset = function() -- requires copying the data into a new table because TTS is weird about handling table return values in Global
local BlessCurseManager = getManager() ChaosBagApi.getChaosBagState = function()
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Bless") end, 0.05) local chaosBagContentsCatcher = Global.call("getChaosBagState")
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Curse") end, 0.10) local chaosBagContents = {}
Wait.time(function() BlessCurseManager.call("doReset", "White") end, 0.15) for _, v in ipairs(chaosBagContentsCatcher) do
table.insert(chaosBagContents, v)
end
return chaosBagContents
end end
-- updates the internal count (called by cards that seal bless/curse tokens) -- checks scripting zone for chaos bag (also called by a lot of objects!)
---@param type string Type of chaos token ("Bless" or "Curse") ChaosBagApi.findChaosBag = function()
---@param guid string GUID of the token return Global.call("findChaosBag")
BlessCurseManagerApi.sealedToken = function(type, guid)
getManager().call("sealedToken", { type = type, guid = guid })
end end
-- updates the internal count (called by cards that seal bless/curse tokens) -- returns a table of object references to the tokens in play (does not include sealed tokens!)
---@param type string Type of chaos token ("Bless" or "Curse") ChaosBagApi.getTokensInPlay = function()
---@param guid string GUID of the token return Global.call("getChaosTokensinPlay")
BlessCurseManagerApi.releasedToken = function(type, guid)
getManager().call("releasedToken", { type = type, guid = guid })
end end
-- updates the internal count (called by cards that seal bless/curse tokens) -- returns all sealed tokens on cards to the chaos bag
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.returnedToken = function(type, guid)
getManager().call("returnedToken", { type = type, guid = guid })
end
-- broadcasts the current status for bless/curse tokens
---@param playerColor string Color of the player to show the broadcast to ---@param playerColor string Color of the player to show the broadcast to
BlessCurseManagerApi.broadcastStatus = function(playerColor) ChaosBagApi.releaseAllSealedTokens = function(playerColor)
getManager().call("broadcastStatus", playerColor) Global.call("releaseAllSealedTokens", playerColor)
end end
-- removes all bless / curse tokens from the chaos bag and play -- returns all drawn tokens to the chaos bag
---@param playerColor string Color of the player to show the broadcast to ChaosBagApi.returnChaosTokens = function()
BlessCurseManagerApi.removeAll = function(playerColor) Global.call("returnChaosTokens")
getManager().call("doRemove", playerColor)
end end
-- adds bless / curse sealing to the hovered card -- removes the specified chaos token from the chaos bag
---@param playerColor string Color of the player to show the broadcast to ---@param id string ID of the chaos token
---@param hoveredObject tts__Object Hovered object ChaosBagApi.removeChaosToken = function(id)
BlessCurseManagerApi.addBlurseSealingMenu = function(playerColor, hoveredObject) Global.call("removeChaosToken", id)
getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject })
end end
return BlessCurseManagerApi -- returns a chaos token to the bag and calls all relevant functions
---@param token tts__Object Chaos token to return
---@param fromBag boolean whether or not the token to return was in the middle of being drawn (true) or elsewhere (false)
ChaosBagApi.returnChaosTokenToBag = function(token, fromBag)
Global.call("returnChaosTokenToBag", { token = token, fromBag = fromBag })
end
-- spawns the specified chaos token and puts it into the chaos bag
---@param id string ID of the chaos token
ChaosBagApi.spawnChaosToken = function(id)
Global.call("spawnChaosToken", id)
end
-- Checks to see if the chaos bag can be manipulated. If a player is searching the bag when tokens
-- are drawn or replaced a TTS bug can cause those tokens to vanish. Any functions which change the
-- contents of the bag should check this method before doing so.
-- This method will broadcast a message to all players if the bag is being searched.
---@return any: True if the bag is manipulated, false if it should be blocked.
ChaosBagApi.canTouchChaosTokens = function()
return Global.call("canTouchChaosTokens")
end
-- draws a chaos token to a playermat
---@param mat tts__Object Playermat that triggered this
---@param drawAdditional boolean Controls whether additional tokens should be drawn
---@param tokenType? string Name of token (e.g. "Bless") to be drawn from the bag
---@param guidToBeResolved? string GUID of the sealed token to be resolved instead of drawing a token from the bag
---@param takeParameters? table Position and rotation of the location where the new token should be drawn to, usually to replace a returned token
---@return tts__Object: Object reference to the token that was drawn
ChaosBagApi.drawChaosToken = function(mat, drawAdditional, tokenType, guidToBeResolved, takeParameters)
return Global.call("drawChaosToken", {
mat = mat,
drawAdditional = drawAdditional,
tokenType = tokenType,
guidToBeResolved = guidToBeResolved,
takeParameters = takeParameters
})
end
-- returns a Table List of chaos token ids in the current chaos bag
-- requires copying the data into a new table because TTS is weird about handling table return values in Global
ChaosBagApi.getIdUrlMap = function()
return Global.getTable("ID_URL_MAP")
end
return ChaosBagApi
end end
end) end)
__bundle_register("playermat/PlaymatApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("playermat/PlayermatApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local PlaymatApi = {} local PlayermatApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi") local guidReferenceApi = require("core/GUIDReferenceApi")
local searchLib = require("util/SearchLib") local searchLib = require("util/SearchLib")
local localInvestigatorPosition = { x = -1.17, y = 1, z = -0.01 }
-- Convenience function to look up a mat's object by color, or get all mats. -- Convenience function to look up a mat's object by color, or get all mats.
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@return table: Single-element if only single playmat is requested ---@return table: Single-element if only single playermat is requested
local function getMatForColor(matColor) local function getMatForColor(matColor)
if matColor == "All" then if matColor == "All" then
return guidReferenceApi.getObjectsByType("Playermat") return guidReferenceApi.getObjectsByType("Playermat")
@ -132,9 +158,9 @@ do
end end
end end
-- Returns the color of the closest playmat -- Returns the color of the closest playermat
---@param startPos table Starting position to get the closest mat from ---@param startPos table Starting position to get the closest mat from
PlaymatApi.getMatColorByPosition = function(startPos) PlayermatApi.getMatColorByPosition = function(startPos)
local result, smallestDistance local result, smallestDistance
for matColor, mat in pairs(getMatForColor("All")) do for matColor, mat in pairs(getMatForColor("All")) do
local distance = Vector.between(startPos, mat.getPosition()):magnitude() local distance = Vector.between(startPos, mat.getPosition()):magnitude()
@ -146,17 +172,17 @@ do
return result return result
end end
-- Returns the color of the player's hand that is seated next to the playmat -- Returns the color of the player's hand that is seated next to the playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getPlayerColor = function(matColor) PlayermatApi.getPlayerColor = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("playerColor") return mat.getVar("playerColor")
end end
end end
-- Returns the color of the playmat that owns the playercolor's hand -- Returns the color of the playermat that owns the playercolor's hand
---@param handColor string Color of the playmat ---@param handColor string Color of the playermat
PlaymatApi.getMatColor = function(handColor) PlayermatApi.getMatColor = function(handColor)
for matColor, mat in pairs(getMatForColor("All")) do for matColor, mat in pairs(getMatForColor("All")) do
local playerColor = mat.getVar("playerColor") local playerColor = mat.getVar("playerColor")
if playerColor == handColor then if playerColor == handColor then
@ -165,41 +191,69 @@ do
end end
end end
-- Returns if there is the card "Dream-Enhancing Serum" on the requested playmat -- Instructs a playermat to check for DES
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.isDES = function(matColor) PlayermatApi.checkForDES = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("isDES") mat.call("checkForDES")
end end
end end
-- Performs a search of the deck area of the requested playmat and returns the result as table -- Returns if there is the card "Dream-Enhancing Serum" on the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getDeckAreaObjects = function(matColor) ---@return boolean: whether DES is present on the playermat
PlayermatApi.hasDES = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("hasDES")
end
end
-- gets the slot data for the playermat
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlayermatApi.getSlotData = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
return mat.getTable("slotData")
end
end
-- sets the slot data for the playermat
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param newSlotData table New slot data for the playermat
PlayermatApi.loadSlotData = function(matColor, newSlotData)
for _, mat in pairs(getMatForColor(matColor)) do
mat.setTable("slotData", newSlotData)
mat.call("redrawSlotSymbols")
return
end
end
-- Performs a search of the deck area of the requested playermat and returns the result as table
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlayermatApi.getDeckAreaObjects = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("getDeckAreaObjects") return mat.call("getDeckAreaObjects")
end end
end end
-- Flips the top card of the deck (useful after deck manipulation for Norman Withers) -- Flips the top card of the deck (useful after deck manipulation for Norman Withers)
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.flipTopCardFromDeck = function(matColor) PlayermatApi.flipTopCardFromDeck = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("flipTopCardFromDeck") return mat.call("flipTopCardFromDeck")
end end
end end
-- Returns the position of the discard pile of the requested playmat -- Returns the position of the discard pile of the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getDiscardPosition = function(matColor) PlayermatApi.getDiscardPosition = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("returnGlobalDiscardPosition") return mat.call("returnGlobalDiscardPosition")
end end
end end
-- Returns the position of the draw pile of the requested playmat -- Returns the position of the draw pile of the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getDrawPosition = function(matColor) PlayermatApi.getDrawPosition = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("returnGlobalDrawPosition") return mat.call("returnGlobalDrawPosition")
end end
@ -207,25 +261,25 @@ do
-- Transforms a local position into a global position -- Transforms a local position into a global position
---@param localPos table Local position to be transformed ---@param localPos table Local position to be transformed
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.transformLocalPosition = function(localPos, matColor) PlayermatApi.transformLocalPosition = function(localPos, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.positionToWorld(localPos) return mat.positionToWorld(localPos)
end end
end end
-- Returns the rotation of the requested playmat -- Returns the rotation of the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.returnRotation = function(matColor) PlayermatApi.returnRotation = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getRotation() return mat.getRotation()
end end
end end
-- Returns a table with spawn data (position and rotation) for a helper object -- Returns a table with spawn data (position and rotation) for a helper object
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param helperName string Name of the helper object ---@param helperName string Name of the helper object
PlaymatApi.getHelperSpawnData = function(matColor, helperName) PlayermatApi.getHelperSpawnData = function(matColor, helperName)
local resultTable = {} local resultTable = {}
local localPositionTable = { local localPositionTable = {
["Hand Helper"] = {0.05, 0, -1.182}, ["Hand Helper"] = {0.05, 0, -1.182},
@ -242,82 +296,99 @@ do
end end
-- Triggers the Upkeep for the requested playmat -- Triggers the Upkeep for the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param playerColor string Color of the calling player (for messages) ---@param playerColor string Color of the calling player (for messages)
PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor) PlayermatApi.doUpkeepFromHotkey = function(matColor, playerColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("doUpkeepFromHotkey", playerColor) mat.call("doUpkeepFromHotkey", playerColor)
end end
end end
-- Handles discarding for the requested playmat for the provided list of objects -- Handles discarding for the requested playermat for the provided list of objects
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param objList table List of objects to discard ---@param objList table List of objects to discard
PlaymatApi.discardListOfObjects = function(matColor, objList) PlayermatApi.discardListOfObjects = function(matColor, objList)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("discardListOfObjects", objList) mat.call("discardListOfObjects", objList)
end end
end end
-- Returns the active investigator id -- Returns the active investigator id
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.returnInvestigatorId = function(matColor) PlayermatApi.returnInvestigatorId = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("activeInvestigatorId") return mat.getVar("activeInvestigatorId")
end end
end end
-- Returns the class of the active investigator
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlayermatApi.returnInvestigatorClass = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("activeInvestigatorClass")
end
end
-- Returns the position for encounter card drawing -- Returns the position for encounter card drawing
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param stack boolean If true, returns the leftmost position instead of the first empty from the right ---@param stack boolean If true, returns the leftmost position instead of the first empty from the right
PlaymatApi.getEncounterCardDrawPosition = function(matColor, stack) PlayermatApi.getEncounterCardDrawPosition = function(matColor, stack)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return Vector(mat.call("getEncounterCardDrawPosition", stack)) return Vector(mat.call("getEncounterCardDrawPosition", stack))
end end
end end
-- Sets the requested playmat's snap points to limit snapping to matching card types or not. If -- Sets the requested playermat's snap points to limit snapping to matching card types or not. If
-- matchTypes is true, the main card slot snap points will only snap assets, while the -- matchTypes is true, the main card slot snap points will only snap assets, while the
-- investigator area point will only snap Investigators. If matchTypes is false, snap points will -- investigator area point will only snap Investigators. If matchTypes is false, snap points will
-- be reset to snap all cards. -- be reset to snap all cards.
---@param matchCardTypes boolean Whether snap points should only snap for the matching card types ---@param matchCardTypes boolean Whether snap points should only snap for the matching card types
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor) PlayermatApi.setLimitSnapsByType = function(matchCardTypes, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("setLimitSnapsByType", matchCardTypes) mat.call("setLimitSnapsByType", matchCardTypes)
end end
end end
-- Sets the requested playmat's draw 1 button to visible -- Sets the requested playermat's draw 1 button to visible
---@param isDrawButtonVisible boolean Whether the draw 1 button should be visible or not ---@param isDrawButtonVisible boolean Whether the draw 1 button should be visible or not
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.showDrawButton = function(isDrawButtonVisible, matColor) PlayermatApi.showDrawButton = function(isDrawButtonVisible, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("showDrawButton", isDrawButtonVisible) mat.call("showDrawButton", isDrawButtonVisible)
end end
end end
-- Shows or hides the clickable clue counter for the requested playmat -- Shows or hides the clickable clue counter for the requested playermat
---@param showCounter boolean Whether the clickable counter should be present or not ---@param showCounter boolean Whether the clickable counter should be present or not
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.clickableClues = function(showCounter, matColor) PlayermatApi.clickableClues = function(showCounter, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("clickableClues", showCounter) mat.call("clickableClues", showCounter)
end end
end end
-- Removes all clues (to the trash for tokens and counters set to 0) for the requested playmat -- Toggles the use of class textures for the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param state boolean Whether the class texture should be used or not
PlaymatApi.removeClues = function(matColor) ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlayermatApi.useClassTexture = function(state, matColor)
for _, mat in pairs(getMatForColor(matColor)) do
mat.call("useClassTexture", state)
end
end
-- Removes all clues (to the trash for tokens and counters set to 0) for the requested playermat
---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlayermatApi.removeClues = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("removeClues") mat.call("removeClues")
end end
end end
-- Reports the clue count for the requested playmat -- Reports the clue count for the requested playermat
---@param useClickableCounters boolean Controls which type of counter is getting checked ---@param useClickableCounters boolean Controls which type of counter is getting checked
PlaymatApi.getClueCount = function(useClickableCounters, matColor) PlayermatApi.getClueCount = function(useClickableCounters, matColor)
local count = 0 local count = 0
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
count = count + mat.call("getClueCount", useClickableCounters) count = count + mat.call("getClueCount", useClickableCounters)
@ -326,37 +397,36 @@ do
end end
-- Updates the specified owned counter -- Updates the specified owned counter
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param type string Counter to target ---@param type string Counter to target
---@param newValue number Value to set the counter to ---@param newValue number Value to set the counter to
---@param modifier number If newValue is not provided, the existing value will be adjusted by this modifier ---@param modifier number If newValue is not provided, the existing value will be adjusted by this modifier
PlaymatApi.updateCounter = function(matColor, type, newValue, modifier) PlayermatApi.updateCounter = function(matColor, type, newValue, modifier)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier }) mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier })
end end
end end
-- Triggers the draw function for the specified playmat -- Triggers the draw function for the specified playermat
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param number number Amount of cards to draw ---@param number number Amount of cards to draw
PlaymatApi.drawCardsWithReshuffle = function(matColor, number) PlayermatApi.drawCardsWithReshuffle = function(matColor, number)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("drawCardsWithReshuffle", number) mat.call("drawCardsWithReshuffle", number)
end end
end end
-- Returns the resource counter amount -- Returns the resource counter amount
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param type string Counter to target ---@param type string Counter to target
PlaymatApi.getCounterValue = function(matColor, type) PlayermatApi.getCounterValue = function(matColor, type)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("getCounterValue", type) return mat.call("getCounterValue", type)
end end
end end
-- Returns a list of mat colors that have an investigator placed -- Returns a list of mat colors that have an investigator placed
PlaymatApi.getUsedMatColors = function() PlayermatApi.getUsedMatColors = function()
local localInvestigatorPosition = { x = -1.17, y = 1, z = -0.01 }
local usedColors = {} local usedColors = {}
for matColor, mat in pairs(getMatForColor("All")) do for matColor, mat in pairs(getMatForColor("All")) do
local searchPos = mat.positionToWorld(localInvestigatorPosition) local searchPos = mat.positionToWorld(localInvestigatorPosition)
@ -368,18 +438,39 @@ do
return usedColors return usedColors
end end
-- Returns investigator name
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All")
PlayermatApi.getInvestigatorName = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
local searchPos = mat.positionToWorld(localInvestigatorPosition)
local searchResult = searchLib.atPosition(searchPos, "isCardOrDeck")
if #searchResult == 1 then
return searchResult[1].getName()
end
end
return ""
end
-- Resets the specified skill tracker to "1, 1, 1, 1" -- Resets the specified skill tracker to "1, 1, 1, 1"
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.resetSkillTracker = function(matColor) PlayermatApi.resetSkillTracker = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("resetSkillTracker") mat.call("resetSkillTracker")
end end
end end
-- Finds all objects on the playmat and associated set aside zone and returns a table -- Redraws the XML for the slot symbols based on the slotData table
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlayermatApi.redrawSlotSymbols = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
mat.call("redrawSlotSymbols")
end
end
-- Finds all objects on the playermat and associated set aside zone and returns a table
---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param filter string Name of the filte function (see util/SearchLib) ---@param filter string Name of the filte function (see util/SearchLib)
PlaymatApi.searchAroundPlaymat = function(matColor, filter) PlayermatApi.searchAroundPlayermat = function(matColor, filter)
local objList = {} local objList = {}
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
for _, obj in ipairs(mat.call("searchAroundSelf", filter)) do for _, obj in ipairs(mat.call("searchAroundSelf", filter)) do
@ -390,33 +481,33 @@ do
end end
-- Discard a non-hidden card from the corresponding player's hand -- Discard a non-hidden card from the corresponding player's hand
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.doDiscardOne = function(matColor) PlayermatApi.doDiscardOne = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("doDiscardOne") mat.call("doDiscardOne")
end end
end end
-- Triggers the metadata sync for all playmats -- Triggers the metadata sync for all playermats
PlaymatApi.syncAllCustomizableCards = function() PlayermatApi.syncAllCustomizableCards = function()
for _, mat in pairs(getMatForColor("All")) do for _, mat in pairs(getMatForColor("All")) do
mat.call("syncAllCustomizableCards") mat.call("syncAllCustomizableCards")
end end
end end
return PlaymatApi return PlayermatApi
end end
end) end)
__bundle_register("util/SearchLib", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("util/SearchLib", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local SearchLib = {} local SearchLib = {}
local filterFunctions = { local filterFunctions = {
isActionToken = function(x) return x.getDescription() == "Action Token" end,
isCard = function(x) return x.type == "Card" end, isCard = function(x) return x.type == "Card" end,
isDeck = function(x) return x.type == "Deck" end, isDeck = function(x) return x.type == "Deck" end,
isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end, isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end,
isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end, isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end,
isTileOrToken = function(x) return x.type == "Tile" end isTileOrToken = function(x) return x.type == "Tile" end,
isUniversalToken = function(x) return x.getMemo() == "universalActionAbility" end,
} }
-- performs the actual search and returns a filtered list of object references -- performs the actual search and returns a filtered list of object references
@ -440,7 +531,7 @@ do
max_distance = maxDistance or 0 max_distance = maxDistance or 0
}) })
-- filtering the result -- filter the result for matching objects
local objList = {} local objList = {}
for _, v in ipairs(searchResult) do for _, v in ipairs(searchResult) do
if not filter or filterFunc(v.hit_object) then if not filter or filterFunc(v.hit_object) then
@ -457,32 +548,53 @@ do
-- searches the area on an object -- searches the area on an object
SearchLib.onObject = function(obj, filter) SearchLib.onObject = function(obj, filter)
pos = obj.getPosition() local pos = obj.getPosition()
size = obj.getBounds().size:setAt("y", 1) local size = obj.getBounds().size:setAt("y", 1)
return returnSearchResult(pos, _, size, filter) return returnSearchResult(pos, _, size, filter)
end end
-- searches the specified position (a single point) -- searches the specified position (a single point)
SearchLib.atPosition = function(pos, filter) SearchLib.atPosition = function(pos, filter)
size = { 0.1, 2, 0.1 } local size = { 0.1, 2, 0.1 }
return returnSearchResult(pos, _, size, filter) return returnSearchResult(pos, _, size, filter)
end end
-- searches below the specified position (downwards until y = 0) -- searches below the specified position (downwards until y = 0)
SearchLib.belowPosition = function(pos, filter) SearchLib.belowPosition = function(pos, filter)
direction = { 0, -1, 0 } local size = { 0.1, 2, 0.1 }
maxDistance = pos.y local direction = { 0, -1, 0 }
local maxDistance = pos.y
return returnSearchResult(pos, _, size, filter, direction, maxDistance) return returnSearchResult(pos, _, size, filter, direction, maxDistance)
end end
return SearchLib return SearchLib
end end
end) end)
__bundle_register("playercards/cards/FavoroftheMoon1", function(require, _LOADED, __bundle_register, __bundle_modules)
VALID_TOKENS = {
["Curse"] = true
}
KEEP_OPEN = true
MAX_SEALED = 3
RESOLVE_TOKEN = true
require("playercards/CardsThatSealTokens")
end)
__bundle_register("playercards/CardsThatSealTokens", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("playercards/CardsThatSealTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that seal tokens --[[ Library for cards that seal tokens
This file is used to add sealing option to cards' context menu. This file is used to add sealing option to cards' context menu.
NOTE: all cards are allowed to release a single token to enable Hallow and A Watchful Peace,
and to release all sealed tokens to allow for cards that might leave play with sealed tokens on them.
Valid options (set before requiring this file): Valid options (set before requiring this file):
MAX_SEALED --@type: number (maximum number of tokens allowable by the card to be sealed)
- required for all cards
- if MAX_SEALED is more than 1, then an XML label is created for the topmost token indicating the number of sealed tokens
- gives an error if user tries to seal additional tokens on the card
- example usage: "The Chthonian Stone"
> MAX_SEALED = 1
UPDATE_ON_HOVER --@type: boolean UPDATE_ON_HOVER --@type: boolean
- automatically updates the context menu options when the card is hovered - automatically updates the context menu options when the card is hovered
- the "Read Bag" function reads the content of the chaos bag to update the context menu - the "Read Bag" function reads the content of the chaos bag to update the context menu
@ -493,19 +605,16 @@ KEEP_OPEN --@type: boolean
- makes the context menu stay open after selecting an option - makes the context menu stay open after selecting an option
- example usage: "Unrelenting" - example usage: "Unrelenting"
SHOW_SINGLE_RELEASE --@type: boolean SHOW_MULTI_RELEASE --@type: number (maximum amount of tokens to release at once)
- enables an entry in the context menu - enables an entry in the context menu
- this entry allows releasing a single token - this entry allows releasing of multiple tokens at once, to the maximum number
- example usage: "Holy Spear" (to keep the other tokens and just release one) - does not fail if there are fewer than the maximum sealed
- example usage: "Nephthys" (to release up to 3 bless tokens at once)
SHOW_MULTI_RELEASE --@type: number (amount of tokens to release at once)
- enables an entry in the context menu
- this entry allows releasing of multiple tokens at once
- example usage: "Nephthys" (to release 3 bless tokens at once)
SHOW_MULTI_RETURN --@type: number (amount of tokens to return to pool at once) SHOW_MULTI_RETURN --@type: number (amount of tokens to return to pool at once)
- enables an entry in the context menu - enables an entry in the context menu
- this entry allows returning tokens to the token pool - this entry allows returning tokens to the token pool
- fails if not enough tokens are sealed
- example usage: "Nephthys" (to return 3 bless tokens at once) - example usage: "Nephthys" (to return 3 bless tokens at once)
SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once) SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once)
@ -539,6 +648,7 @@ Thus it should be implemented like this:
> ["+1"] = true, > ["+1"] = true,
> ["Elder Sign"] = true > ["Elder Sign"] = true
> } > }
> MAX_SEALED = 1
> require... > require...
---------------------------------------------------------- ----------------------------------------------------------
Example 2: Holy Spear Example 2: Holy Spear
@ -549,21 +659,35 @@ Thus it should be implemented like this:
> VALID_TOKENS = { > VALID_TOKENS = {
> ["Bless"] = true > ["Bless"] = true
> } > }
> SHOW_SINGLE_RELEASE = true
> SHOW_MULTI_SEAL = 2 > SHOW_MULTI_SEAL = 2
> MAX_SEALED = 10
> require... > require...
----------------------------------------------------------]] ----------------------------------------------------------]]
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local chaosBagApi = require("chaosbag/ChaosBagApi") local chaosBagApi = require("chaosbag/ChaosBagApi")
local guidReferenceApi = require("core/GUIDReferenceApi") local guidReferenceApi = require("core/GUIDReferenceApi")
local playmatApi = require("playermat/PlaymatApi") local playermatApi = require("playermat/PlayermatApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi") local tokenArrangerApi = require("accessories/TokenArrangerApi")
local sealedTokens = {} local sealedTokens = {}
local ID_URL_MAP = {} local ID_URL_MAP = {}
local tokensInBag = {} local tokensInBag = {}
-- XML background color for each token for label when stacked
local tokenColor = {
["Skull"] = "#4A0400E6",
["Cultist"] = "#173B0BE6",
["Tablet"] = "#1D2238E6",
["Elder Thing"] = "#4D2331E6",
["Auto-fail"] = "#9B0004E6",
["Bless"] = "#9D702CE6",
["Curse"] = "#633A84E6",
["Frost"] = "#404450E6",
["Elder Sign"] = "#50A8CEE6",
[""] = "#77674DE6"
}
function onSave() return JSON.encode(sealedTokens) end function onSave() return JSON.encode(sealedTokens) end
function onLoad(savedData) function onLoad(savedData)
@ -575,13 +699,15 @@ end
-- builds the context menu -- builds the context menu
function generateContextMenu() function generateContextMenu()
-- conditional single or multi release options self.addContextMenuItem("Release one token", releaseOneToken)
if SHOW_SINGLE_RELEASE then
self.addContextMenuItem("Release token", releaseOneToken) -- conditional release options
elseif SHOW_MULTI_RELEASE then if MAX_SEALED > 1 then
self.addContextMenuItem("Release all tokens", releaseAllTokens)
end
if SHOW_MULTI_RELEASE then
self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens) self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens)
else
self.addContextMenuItem("Release token(s)", releaseAllTokens)
end end
if RESOLVE_TOKEN then if RESOLVE_TOKEN then
@ -619,7 +745,7 @@ function generateContextMenu()
end end
if allowed then if allowed then
for i = 1, SHOW_MULTI_SEAL do for i = SHOW_MULTI_SEAL, 1, -1 do
sealToken(map.name, playerColor) sealToken(map.name, playerColor)
end end
else else
@ -656,6 +782,10 @@ end
-- seals the named token on this card -- seals the named token on this card
function sealToken(name, playerColor) function sealToken(name, playerColor)
if #sealedTokens >= MAX_SEALED then
printToColor("Cannot seal any more tokens on this card", playerColor, "Red")
return
end
if not chaosBagApi.canTouchChaosTokens() then return end if not chaosBagApi.canTouchChaosTokens() then return end
local chaosbag = chaosBagApi.findChaosBag() local chaosbag = chaosBagApi.findChaosBag()
for i, obj in ipairs(chaosbag.getObjects()) do for i, obj in ipairs(chaosbag.getObjects()) do
@ -672,6 +802,16 @@ function sealToken(name, playerColor)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.sealedToken(name, guid) blessCurseManagerApi.sealedToken(name, guid)
end end
-- destroy XML on just covered token
if #sealedTokens > 1 then
local coveredToken = getObjectFromGUID(sealedTokens[#sealedTokens - 1])
if coveredToken ~= nil then
coveredToken.UI.setXml("")
else
table.remove(sealedTokens, #sealedTokens - 1)
end
end
updateStackSize()
end end
}) })
return return
@ -691,16 +831,22 @@ function releaseOneToken(playerColor)
end end
end end
-- release multiple tokens at once -- release up to multiple tokens at once with no minimum
function releaseMultipleTokens(playerColor) function releaseMultipleTokens(playerColor)
if SHOW_MULTI_RELEASE <= #sealedTokens then if #sealedTokens == 0 then
for i = 1, SHOW_MULTI_RELEASE do printToColor("Not enough tokens sealed.", playerColor)
return
end
local numRemoved = SHOW_MULTI_RELEASE
if #sealedTokens < SHOW_MULTI_RELEASE then
numRemoved = #sealedTokens
end
for i = 1, numRemoved do
putTokenAway(table.remove(sealedTokens)) putTokenAway(table.remove(sealedTokens))
end end
printToColor("Releasing " .. SHOW_MULTI_RELEASE .. " tokens", playerColor) printToColor("Releasing " .. numRemoved .. " tokens", playerColor)
else
printToColor("Not enough tokens sealed.", playerColor)
end
end end
-- releases all sealed tokens -- releases all sealed tokens
@ -741,6 +887,7 @@ function putTokenAway(guid)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.releasedToken(name, guid) blessCurseManagerApi.releasedToken(name, guid)
end end
updateStackSize()
end end
-- returns the token to the pool (== removes it) -- returns the token to the pool (== removes it)
@ -753,6 +900,7 @@ function returnToken(guid)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.returnedToken(name, guid) blessCurseManagerApi.returnedToken(name, guid)
end end
updateStackSize()
end end
-- resolves sealed token as if it came from the chaos bag -- resolves sealed token as if it came from the chaos bag
@ -761,11 +909,47 @@ function resolveSealed()
broadcastToAll("No tokens sealed.", "Red") broadcastToAll("No tokens sealed.", "Red")
return return
end end
local closestMatColor = playmatApi.getMatColorByPosition(self.getPosition()) local closestMatColor = playermatApi.getMatColorByPosition(self.getPosition())
local mat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat") local mat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat")
local guidToBeResolved = table.remove(sealedTokens) local guidToBeResolved = table.remove(sealedTokens)
local resolvedToken = getObjectFromGUID(guidToBeResolved)
resolvedToken.UI.setXml("")
updateStackSize()
chaosBagApi.drawChaosToken(mat, true, _, guidToBeResolved) chaosBagApi.drawChaosToken(mat, true, _, guidToBeResolved)
end end
function updateStackSize()
if MAX_SEALED == 1 then return end
if #sealedTokens == 0 then return end
-- get topmost sealed token
local topToken = getObjectFromGUID(sealedTokens[#sealedTokens])
local name = topToken.getName()
topToken.UI.setXmlTable({
{
tag = "Panel",
attributes = {
height = 380,
width = 380,
rotation = "0 0 180",
scale = "0.2 0.2 1",
position = "0 0 -12",
color = tokenColor[name] or "#77674DE6"
},
children = {
tag = "Text",
attributes = {
fontSize = "380",
font = "font_teutonic-arkham",
color = "#ffffff",
outline = "#000000",
outlineSize = "8 -8",
text = "x" .. #sealedTokens
}
}
}
})
end
end) end)
__bundle_register("accessories/TokenArrangerApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("accessories/TokenArrangerApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
@ -801,92 +985,81 @@ do
return TokenArrangerApi return TokenArrangerApi
end end
end) end)
__bundle_register("chaosbag/ChaosBagApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("chaosbag/BlessCurseManagerApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local ChaosBagApi = {} local BlessCurseManagerApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
-- respawns the chaos bag with a new state of tokens local function getManager()
---@param tokenList table List of chaos token ids return guidReferenceApi.getObjectByOwnerAndType("Mythos", "BlessCurseManager")
ChaosBagApi.setChaosBagState = function(tokenList)
return Global.call("setChaosBagState", tokenList)
end end
-- returns a Table List of chaos token ids in the current chaos bag -- removes all taken tokens and resets the counts
-- requires copying the data into a new table because TTS is weird about handling table return values in Global BlessCurseManagerApi.removeTakenTokensAndReset = function()
ChaosBagApi.getChaosBagState = function() local BlessCurseManager = getManager()
local chaosBagContentsCatcher = Global.call("getChaosBagState") Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Bless") end, 0.05)
local chaosBagContents = {} Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Curse") end, 0.10)
for _, v in ipairs(chaosBagContentsCatcher) do Wait.time(function() BlessCurseManager.call("doReset", "White") end, 0.15)
table.insert(chaosBagContents, v)
end
return chaosBagContents
end end
-- checks scripting zone for chaos bag (also called by a lot of objects!) -- updates the internal count (called by cards that seal bless/curse tokens)
ChaosBagApi.findChaosBag = function() ---@param type string Type of chaos token ("Bless" or "Curse")
return Global.call("findChaosBag") ---@param guid string GUID of the token
BlessCurseManagerApi.sealedToken = function(type, guid)
getManager().call("sealedToken", { type = type, guid = guid })
end end
-- returns a table of object references to the tokens in play (does not include sealed tokens!) -- updates the internal count (called by cards that seal bless/curse tokens)
ChaosBagApi.getTokensInPlay = function() ---@param type string Type of chaos token ("Bless" or "Curse")
return Global.call("getChaosTokensinPlay") ---@param guid string GUID of the token
---@param fromBag? boolean Whether or not token was just drawn from the chaos bag
BlessCurseManagerApi.releasedToken = function(type, guid, fromBag)
getManager().call("releasedToken", { type = type, guid = guid, fromBag = fromBag })
end end
-- returns all sealed tokens on cards to the chaos bag -- updates the internal count (called by cards that seal bless/curse tokens)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.returnedToken = function(type, guid)
getManager().call("returnedToken", { type = type, guid = guid })
end
-- broadcasts the current status for bless/curse tokens
---@param playerColor string Color of the player to show the broadcast to ---@param playerColor string Color of the player to show the broadcast to
ChaosBagApi.releaseAllSealedTokens = function(playerColor) BlessCurseManagerApi.broadcastStatus = function(playerColor)
return Global.call("releaseAllSealedTokens", playerColor) getManager().call("broadcastStatus", playerColor)
end end
-- returns all drawn tokens to the chaos bag -- removes all bless / curse tokens from the chaos bag and play
ChaosBagApi.returnChaosTokens = function() ---@param playerColor string Color of the player to show the broadcast to
return Global.call("returnChaosTokens") BlessCurseManagerApi.removeAll = function(playerColor)
getManager().call("doRemove", playerColor)
end end
-- removes the specified chaos token from the chaos bag -- adds bless / curse sealing to the hovered card
---@param id string ID of the chaos token ---@param playerColor string Color of the player to show the broadcast to
ChaosBagApi.removeChaosToken = function(id) ---@param hoveredObject tts__Object Hovered object
return Global.call("removeChaosToken", id) BlessCurseManagerApi.addBlurseSealingMenu = function(playerColor, hoveredObject)
getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject })
end end
-- returns a chaos token to the bag and calls all relevant functions -- adds bless / curse to the chaos bag
---@param token tts__Object Chaos token to return ---@param type string Type of chaos token ("Bless" or "Curse")
ChaosBagApi.returnChaosTokenToBag = function(token) BlessCurseManagerApi.addToken = function(type)
return Global.call("returnChaosTokenToBag", token) getManager().call("addToken", type)
end end
-- spawns the specified chaos token and puts it into the chaos bag -- removes bless / curse from the chaos bag
---@param id string ID of the chaos token ---@param type string Type of chaos token ("Bless" or "Curse")
ChaosBagApi.spawnChaosToken = function(id) BlessCurseManagerApi.removeToken = function(type)
return Global.call("spawnChaosToken", id) getManager().call("removeToken", type)
end end
-- Checks to see if the chaos bag can be manipulated. If a player is searching the bag when tokens BlessCurseManagerApi.getBlessCurseInBag = function()
-- are drawn or replaced a TTS bug can cause those tokens to vanish. Any functions which change the return getManager().call("getBlessCurseInBag", {})
-- 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 any canTouch True if the bag is manipulated, false if it should be blocked.
ChaosBagApi.canTouchChaosTokens = function()
return Global.call("canTouchChaosTokens")
end end
-- called by playermats (by the "Draw chaos token" button) return BlessCurseManagerApi
---@param mat tts__Object Playermat that triggered this
---@param drawAdditional boolean Controls whether additional tokens should be drawn
---@param tokenType? string Name of token (e.g. "Bless") to be drawn from the bag
---@param guidToBeResolved? string GUID of the sealed token to be resolved instead of drawing a token from the bag
---@param returnedToken? tts__Object Token to be replaced with newly drawn token
ChaosBagApi.drawChaosToken = function(mat, drawAdditional, tokenType, guidToBeResolved, returnedToken)
return Global.call("drawChaosToken", {mat = mat, drawAdditional = drawAdditional, tokenType = tokenType, guidToBeResolved = guidToBeResolved, returnedToken = returnedToken})
end
-- returns a Table List of chaos token ids in the current chaos bag
-- requires copying the data into a new table because TTS is weird about handling table return values in Global
ChaosBagApi.getIdUrlMap = function()
return Global.getTable("ID_URL_MAP")
end
return ChaosBagApi
end end
end) end)
__bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules)

View File

@ -20,8 +20,7 @@ CustomDeck:
Description: Advanced Description: Advanced
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90051\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90051\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n
\ \"traits\": \"Endtimes.\",\n \"weakness\": true,\n \"cycle\": \"The Dunwich \ \"traits\": \"Endtimes.\",\n \"weakness\": true,\n \"cycle\": \"Laid to Rest\"\n}"
Legacy\"\n}"
GUID: '561775' GUID: '561775'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"04003\",\n \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n GMNotes: "{\n \"id\": \"04003\",\n \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n
\ \"traits\": \"Criminal.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\": 4,\n \ \"traits\": \"Criminal.\",\n \"willpowerIcons\": 1,\n \"intellectIcons\": 4,\n
\ \"combatIcons\": 3,\n \"agilityIcons\": 4,\n \"cycle\": \"The Forgotten Age\",\n \ \"combatIcons\": 3,\n \"agilityIcons\": 4,\n \"cycle\": \"The Forgotten Age\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Evade\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"04010\": 1\n },\n {\n \"04011\": 1,\n \"signatures\": [\n {\n \"04010\": 1\n },\n {\n \"04011\":
1\n },\n {\n \"04012\": 1\n }\n ]\n },\n \"deck_options\": 1\n },\n {\n \"04012\": 1\n }\n ]\n },\n \"deck_options\":
[\n {\n \"trait\": [\n \"illicit\"\n ],\n \"level\": {\n [\n {\n \"trait\": [\n \"illicit\"\n ],\n \"level\": {\n

View File

@ -49,71 +49,11 @@ VALID_TOKENS = {
["Curse"] = true ["Curse"] = true
} }
SHOW_SINGLE_RELEASE = true MAX_SEALED = 10
KEEP_OPEN = true KEEP_OPEN = true
require("playercards/CardsThatSealTokens") require("playercards/CardsThatSealTokens")
end) end)
__bundle_register("chaosbag/BlessCurseManagerApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local BlessCurseManagerApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
local function getManager()
return guidReferenceApi.getObjectByOwnerAndType("Mythos", "BlessCurseManager")
end
-- removes all taken tokens and resets the counts
BlessCurseManagerApi.removeTakenTokensAndReset = function()
local BlessCurseManager = getManager()
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Bless") end, 0.05)
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "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)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.sealedToken = function(type, guid)
getManager().call("sealedToken", { type = type, guid = guid })
end
-- updates the internal count (called by cards that seal bless/curse tokens)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.releasedToken = function(type, guid)
getManager().call("releasedToken", { type = type, guid = guid })
end
-- updates the internal count (called by cards that seal bless/curse tokens)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.returnedToken = function(type, guid)
getManager().call("returnedToken", { type = type, guid = guid })
end
-- broadcasts the current status for bless/curse tokens
---@param playerColor string Color of the player to show the broadcast to
BlessCurseManagerApi.broadcastStatus = function(playerColor)
getManager().call("broadcastStatus", playerColor)
end
-- removes all bless / curse tokens from the chaos bag and play
---@param playerColor string Color of the player to show the broadcast to
BlessCurseManagerApi.removeAll = function(playerColor)
getManager().call("doRemove", playerColor)
end
-- adds bless / curse sealing to the hovered card
---@param playerColor string Color of the player to show the broadcast to
---@param hoveredObject tts__Object Hovered object
BlessCurseManagerApi.addBlurseSealingMenu = function(playerColor, hoveredObject)
getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject })
end
return BlessCurseManagerApi
end
end)
__bundle_register("chaosbag/ChaosBagApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("chaosbag/ChaosBagApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local ChaosBagApi = {} local ChaosBagApi = {}
@ -121,7 +61,7 @@ do
-- respawns the chaos bag with a new state of tokens -- respawns the chaos bag with a new state of tokens
---@param tokenList table List of chaos token ids ---@param tokenList table List of chaos token ids
ChaosBagApi.setChaosBagState = function(tokenList) ChaosBagApi.setChaosBagState = function(tokenList)
return Global.call("setChaosBagState", tokenList) Global.call("setChaosBagState", tokenList)
end end
-- returns a Table List of chaos token ids in the current chaos bag -- returns a Table List of chaos token ids in the current chaos bag
@ -148,49 +88,57 @@ do
-- returns all sealed tokens on cards to the chaos bag -- returns all sealed tokens on cards to the chaos bag
---@param playerColor string Color of the player to show the broadcast to ---@param playerColor string Color of the player to show the broadcast to
ChaosBagApi.releaseAllSealedTokens = function(playerColor) ChaosBagApi.releaseAllSealedTokens = function(playerColor)
return Global.call("releaseAllSealedTokens", playerColor) Global.call("releaseAllSealedTokens", playerColor)
end end
-- returns all drawn tokens to the chaos bag -- returns all drawn tokens to the chaos bag
ChaosBagApi.returnChaosTokens = function() ChaosBagApi.returnChaosTokens = function()
return Global.call("returnChaosTokens") Global.call("returnChaosTokens")
end end
-- removes the specified chaos token from the chaos bag -- removes the specified chaos token from the chaos bag
---@param id string ID of the chaos token ---@param id string ID of the chaos token
ChaosBagApi.removeChaosToken = function(id) ChaosBagApi.removeChaosToken = function(id)
return Global.call("removeChaosToken", id) Global.call("removeChaosToken", id)
end end
-- returns a chaos token to the bag and calls all relevant functions -- returns a chaos token to the bag and calls all relevant functions
---@param token tts__Object Chaos token to return ---@param token tts__Object Chaos token to return
ChaosBagApi.returnChaosTokenToBag = function(token) ---@param fromBag boolean whether or not the token to return was in the middle of being drawn (true) or elsewhere (false)
return Global.call("returnChaosTokenToBag", token) ChaosBagApi.returnChaosTokenToBag = function(token, fromBag)
Global.call("returnChaosTokenToBag", { token = token, fromBag = fromBag })
end end
-- spawns the specified chaos token and puts it into the chaos bag -- spawns the specified chaos token and puts it into the chaos bag
---@param id string ID of the chaos token ---@param id string ID of the chaos token
ChaosBagApi.spawnChaosToken = function(id) ChaosBagApi.spawnChaosToken = function(id)
return Global.call("spawnChaosToken", id) Global.call("spawnChaosToken", id)
end end
-- Checks to see if the chaos bag can be manipulated. If a player is searching the bag when tokens -- Checks to see if the chaos bag can be manipulated. If a player is searching the bag when tokens
-- are drawn or replaced a TTS bug can cause those tokens to vanish. Any functions which change the -- 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. -- 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. -- This method will broadcast a message to all players if the bag is being searched.
---@return any canTouch True if the bag is manipulated, false if it should be blocked. ---@return any: True if the bag is manipulated, false if it should be blocked.
ChaosBagApi.canTouchChaosTokens = function() ChaosBagApi.canTouchChaosTokens = function()
return Global.call("canTouchChaosTokens") return Global.call("canTouchChaosTokens")
end end
-- called by playermats (by the "Draw chaos token" button) -- draws a chaos token to a playermat
---@param mat tts__Object Playermat that triggered this ---@param mat tts__Object Playermat that triggered this
---@param drawAdditional boolean Controls whether additional tokens should be drawn ---@param drawAdditional boolean Controls whether additional tokens should be drawn
---@param tokenType? string Name of token (e.g. "Bless") to be drawn from the bag ---@param tokenType? string Name of token (e.g. "Bless") to be drawn from the bag
---@param guidToBeResolved? string GUID of the sealed token to be resolved instead of drawing a token from the bag ---@param guidToBeResolved? string GUID of the sealed token to be resolved instead of drawing a token from the bag
---@param returnedToken? tts__Object Token to be replaced with newly drawn token ---@param takeParameters? table Position and rotation of the location where the new token should be drawn to, usually to replace a returned token
ChaosBagApi.drawChaosToken = function(mat, drawAdditional, tokenType, guidToBeResolved, returnedToken) ---@return tts__Object: Object reference to the token that was drawn
return Global.call("drawChaosToken", {mat = mat, drawAdditional = drawAdditional, tokenType = tokenType, guidToBeResolved = guidToBeResolved, returnedToken = returnedToken}) ChaosBagApi.drawChaosToken = function(mat, drawAdditional, tokenType, guidToBeResolved, takeParameters)
return Global.call("drawChaosToken", {
mat = mat,
drawAdditional = drawAdditional,
tokenType = tokenType,
guidToBeResolved = guidToBeResolved,
takeParameters = takeParameters
})
end end
-- returns a Table List of chaos token ids in the current chaos bag -- returns a Table List of chaos token ids in the current chaos bag
@ -202,15 +150,16 @@ do
return ChaosBagApi return ChaosBagApi
end end
end) end)
__bundle_register("playermat/PlaymatApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("playermat/PlayermatApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local PlaymatApi = {} local PlayermatApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi") local guidReferenceApi = require("core/GUIDReferenceApi")
local searchLib = require("util/SearchLib") local searchLib = require("util/SearchLib")
local localInvestigatorPosition = { x = -1.17, y = 1, z = -0.01 }
-- Convenience function to look up a mat's object by color, or get all mats. -- Convenience function to look up a mat's object by color, or get all mats.
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@return table: Single-element if only single playmat is requested ---@return table: Single-element if only single playermat is requested
local function getMatForColor(matColor) local function getMatForColor(matColor)
if matColor == "All" then if matColor == "All" then
return guidReferenceApi.getObjectsByType("Playermat") return guidReferenceApi.getObjectsByType("Playermat")
@ -219,9 +168,9 @@ do
end end
end end
-- Returns the color of the closest playmat -- Returns the color of the closest playermat
---@param startPos table Starting position to get the closest mat from ---@param startPos table Starting position to get the closest mat from
PlaymatApi.getMatColorByPosition = function(startPos) PlayermatApi.getMatColorByPosition = function(startPos)
local result, smallestDistance local result, smallestDistance
for matColor, mat in pairs(getMatForColor("All")) do for matColor, mat in pairs(getMatForColor("All")) do
local distance = Vector.between(startPos, mat.getPosition()):magnitude() local distance = Vector.between(startPos, mat.getPosition()):magnitude()
@ -233,17 +182,17 @@ do
return result return result
end end
-- Returns the color of the player's hand that is seated next to the playmat -- Returns the color of the player's hand that is seated next to the playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getPlayerColor = function(matColor) PlayermatApi.getPlayerColor = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("playerColor") return mat.getVar("playerColor")
end end
end end
-- Returns the color of the playmat that owns the playercolor's hand -- Returns the color of the playermat that owns the playercolor's hand
---@param handColor string Color of the playmat ---@param handColor string Color of the playermat
PlaymatApi.getMatColor = function(handColor) PlayermatApi.getMatColor = function(handColor)
for matColor, mat in pairs(getMatForColor("All")) do for matColor, mat in pairs(getMatForColor("All")) do
local playerColor = mat.getVar("playerColor") local playerColor = mat.getVar("playerColor")
if playerColor == handColor then if playerColor == handColor then
@ -252,41 +201,69 @@ do
end end
end end
-- Returns if there is the card "Dream-Enhancing Serum" on the requested playmat -- Instructs a playermat to check for DES
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.isDES = function(matColor) PlayermatApi.checkForDES = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("isDES") mat.call("checkForDES")
end end
end end
-- Performs a search of the deck area of the requested playmat and returns the result as table -- Returns if there is the card "Dream-Enhancing Serum" on the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getDeckAreaObjects = function(matColor) ---@return boolean: whether DES is present on the playermat
PlayermatApi.hasDES = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("hasDES")
end
end
-- gets the slot data for the playermat
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlayermatApi.getSlotData = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
return mat.getTable("slotData")
end
end
-- sets the slot data for the playermat
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param newSlotData table New slot data for the playermat
PlayermatApi.loadSlotData = function(matColor, newSlotData)
for _, mat in pairs(getMatForColor(matColor)) do
mat.setTable("slotData", newSlotData)
mat.call("redrawSlotSymbols")
return
end
end
-- Performs a search of the deck area of the requested playermat and returns the result as table
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlayermatApi.getDeckAreaObjects = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("getDeckAreaObjects") return mat.call("getDeckAreaObjects")
end end
end end
-- Flips the top card of the deck (useful after deck manipulation for Norman Withers) -- Flips the top card of the deck (useful after deck manipulation for Norman Withers)
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.flipTopCardFromDeck = function(matColor) PlayermatApi.flipTopCardFromDeck = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("flipTopCardFromDeck") return mat.call("flipTopCardFromDeck")
end end
end end
-- Returns the position of the discard pile of the requested playmat -- Returns the position of the discard pile of the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getDiscardPosition = function(matColor) PlayermatApi.getDiscardPosition = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("returnGlobalDiscardPosition") return mat.call("returnGlobalDiscardPosition")
end end
end end
-- Returns the position of the draw pile of the requested playmat -- Returns the position of the draw pile of the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.getDrawPosition = function(matColor) PlayermatApi.getDrawPosition = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("returnGlobalDrawPosition") return mat.call("returnGlobalDrawPosition")
end end
@ -294,25 +271,25 @@ do
-- Transforms a local position into a global position -- Transforms a local position into a global position
---@param localPos table Local position to be transformed ---@param localPos table Local position to be transformed
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.transformLocalPosition = function(localPos, matColor) PlayermatApi.transformLocalPosition = function(localPos, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.positionToWorld(localPos) return mat.positionToWorld(localPos)
end end
end end
-- Returns the rotation of the requested playmat -- Returns the rotation of the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.returnRotation = function(matColor) PlayermatApi.returnRotation = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getRotation() return mat.getRotation()
end end
end end
-- Returns a table with spawn data (position and rotation) for a helper object -- Returns a table with spawn data (position and rotation) for a helper object
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param helperName string Name of the helper object ---@param helperName string Name of the helper object
PlaymatApi.getHelperSpawnData = function(matColor, helperName) PlayermatApi.getHelperSpawnData = function(matColor, helperName)
local resultTable = {} local resultTable = {}
local localPositionTable = { local localPositionTable = {
["Hand Helper"] = {0.05, 0, -1.182}, ["Hand Helper"] = {0.05, 0, -1.182},
@ -329,82 +306,99 @@ do
end end
-- Triggers the Upkeep for the requested playmat -- Triggers the Upkeep for the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param playerColor string Color of the calling player (for messages) ---@param playerColor string Color of the calling player (for messages)
PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor) PlayermatApi.doUpkeepFromHotkey = function(matColor, playerColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("doUpkeepFromHotkey", playerColor) mat.call("doUpkeepFromHotkey", playerColor)
end end
end end
-- Handles discarding for the requested playmat for the provided list of objects -- Handles discarding for the requested playermat for the provided list of objects
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param objList table List of objects to discard ---@param objList table List of objects to discard
PlaymatApi.discardListOfObjects = function(matColor, objList) PlayermatApi.discardListOfObjects = function(matColor, objList)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("discardListOfObjects", objList) mat.call("discardListOfObjects", objList)
end end
end end
-- Returns the active investigator id -- Returns the active investigator id
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlaymatApi.returnInvestigatorId = function(matColor) PlayermatApi.returnInvestigatorId = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("activeInvestigatorId") return mat.getVar("activeInvestigatorId")
end end
end end
-- Returns the class of the active investigator
---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
PlayermatApi.returnInvestigatorClass = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("activeInvestigatorClass")
end
end
-- Returns the position for encounter card drawing -- Returns the position for encounter card drawing
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param stack boolean If true, returns the leftmost position instead of the first empty from the right ---@param stack boolean If true, returns the leftmost position instead of the first empty from the right
PlaymatApi.getEncounterCardDrawPosition = function(matColor, stack) PlayermatApi.getEncounterCardDrawPosition = function(matColor, stack)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return Vector(mat.call("getEncounterCardDrawPosition", stack)) return Vector(mat.call("getEncounterCardDrawPosition", stack))
end end
end end
-- Sets the requested playmat's snap points to limit snapping to matching card types or not. If -- Sets the requested playermat's snap points to limit snapping to matching card types or not. If
-- matchTypes is true, the main card slot snap points will only snap assets, while the -- matchTypes is true, the main card slot snap points will only snap assets, while the
-- investigator area point will only snap Investigators. If matchTypes is false, snap points will -- investigator area point will only snap Investigators. If matchTypes is false, snap points will
-- be reset to snap all cards. -- be reset to snap all cards.
---@param matchCardTypes boolean Whether snap points should only snap for the matching card types ---@param matchCardTypes boolean Whether snap points should only snap for the matching card types
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor) PlayermatApi.setLimitSnapsByType = function(matchCardTypes, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("setLimitSnapsByType", matchCardTypes) mat.call("setLimitSnapsByType", matchCardTypes)
end end
end end
-- Sets the requested playmat's draw 1 button to visible -- Sets the requested playermat's draw 1 button to visible
---@param isDrawButtonVisible boolean Whether the draw 1 button should be visible or not ---@param isDrawButtonVisible boolean Whether the draw 1 button should be visible or not
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.showDrawButton = function(isDrawButtonVisible, matColor) PlayermatApi.showDrawButton = function(isDrawButtonVisible, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("showDrawButton", isDrawButtonVisible) mat.call("showDrawButton", isDrawButtonVisible)
end end
end end
-- Shows or hides the clickable clue counter for the requested playmat -- Shows or hides the clickable clue counter for the requested playermat
---@param showCounter boolean Whether the clickable counter should be present or not ---@param showCounter boolean Whether the clickable counter should be present or not
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.clickableClues = function(showCounter, matColor) PlayermatApi.clickableClues = function(showCounter, matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("clickableClues", showCounter) mat.call("clickableClues", showCounter)
end end
end end
-- Removes all clues (to the trash for tokens and counters set to 0) for the requested playmat -- Toggles the use of class textures for the requested playermat
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param state boolean Whether the class texture should be used or not
PlaymatApi.removeClues = function(matColor) ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlayermatApi.useClassTexture = function(state, matColor)
for _, mat in pairs(getMatForColor(matColor)) do
mat.call("useClassTexture", state)
end
end
-- Removes all clues (to the trash for tokens and counters set to 0) for the requested playermat
---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlayermatApi.removeClues = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("removeClues") mat.call("removeClues")
end end
end end
-- Reports the clue count for the requested playmat -- Reports the clue count for the requested playermat
---@param useClickableCounters boolean Controls which type of counter is getting checked ---@param useClickableCounters boolean Controls which type of counter is getting checked
PlaymatApi.getClueCount = function(useClickableCounters, matColor) PlayermatApi.getClueCount = function(useClickableCounters, matColor)
local count = 0 local count = 0
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
count = count + mat.call("getClueCount", useClickableCounters) count = count + mat.call("getClueCount", useClickableCounters)
@ -413,37 +407,36 @@ do
end end
-- Updates the specified owned counter -- Updates the specified owned counter
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param type string Counter to target ---@param type string Counter to target
---@param newValue number Value to set the counter to ---@param newValue number Value to set the counter to
---@param modifier number If newValue is not provided, the existing value will be adjusted by this modifier ---@param modifier number If newValue is not provided, the existing value will be adjusted by this modifier
PlaymatApi.updateCounter = function(matColor, type, newValue, modifier) PlayermatApi.updateCounter = function(matColor, type, newValue, modifier)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier }) mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier })
end end
end end
-- Triggers the draw function for the specified playmat -- Triggers the draw function for the specified playermat
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param number number Amount of cards to draw ---@param number number Amount of cards to draw
PlaymatApi.drawCardsWithReshuffle = function(matColor, number) PlayermatApi.drawCardsWithReshuffle = function(matColor, number)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("drawCardsWithReshuffle", number) mat.call("drawCardsWithReshuffle", number)
end end
end end
-- Returns the resource counter amount -- Returns the resource counter amount
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor string Color of the playermat - White, Orange, Green or Red (does not support "All")
---@param type string Counter to target ---@param type string Counter to target
PlaymatApi.getCounterValue = function(matColor, type) PlayermatApi.getCounterValue = function(matColor, type)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("getCounterValue", type) return mat.call("getCounterValue", type)
end end
end end
-- Returns a list of mat colors that have an investigator placed -- Returns a list of mat colors that have an investigator placed
PlaymatApi.getUsedMatColors = function() PlayermatApi.getUsedMatColors = function()
local localInvestigatorPosition = { x = -1.17, y = 1, z = -0.01 }
local usedColors = {} local usedColors = {}
for matColor, mat in pairs(getMatForColor("All")) do for matColor, mat in pairs(getMatForColor("All")) do
local searchPos = mat.positionToWorld(localInvestigatorPosition) local searchPos = mat.positionToWorld(localInvestigatorPosition)
@ -455,18 +448,39 @@ do
return usedColors return usedColors
end end
-- Returns investigator name
---@param matColor string Color of the playmat - White, Orange, Green or Red (does not support "All")
PlayermatApi.getInvestigatorName = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
local searchPos = mat.positionToWorld(localInvestigatorPosition)
local searchResult = searchLib.atPosition(searchPos, "isCardOrDeck")
if #searchResult == 1 then
return searchResult[1].getName()
end
end
return ""
end
-- Resets the specified skill tracker to "1, 1, 1, 1" -- Resets the specified skill tracker to "1, 1, 1, 1"
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.resetSkillTracker = function(matColor) PlayermatApi.resetSkillTracker = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("resetSkillTracker") mat.call("resetSkillTracker")
end end
end end
-- Finds all objects on the playmat and associated set aside zone and returns a table -- Redraws the XML for the slot symbols based on the slotData table
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlayermatApi.redrawSlotSymbols = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do
mat.call("redrawSlotSymbols")
end
end
-- Finds all objects on the playermat and associated set aside zone and returns a table
---@param matColor string Color of the playermat - White, Orange, Green, Red or All
---@param filter string Name of the filte function (see util/SearchLib) ---@param filter string Name of the filte function (see util/SearchLib)
PlaymatApi.searchAroundPlaymat = function(matColor, filter) PlayermatApi.searchAroundPlayermat = function(matColor, filter)
local objList = {} local objList = {}
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
for _, obj in ipairs(mat.call("searchAroundSelf", filter)) do for _, obj in ipairs(mat.call("searchAroundSelf", filter)) do
@ -477,33 +491,33 @@ do
end end
-- Discard a non-hidden card from the corresponding player's hand -- Discard a non-hidden card from the corresponding player's hand
---@param matColor string Color of the playmat - White, Orange, Green, Red or All ---@param matColor string Color of the playermat - White, Orange, Green, Red or All
PlaymatApi.doDiscardOne = function(matColor) PlayermatApi.doDiscardOne = function(matColor)
for _, mat in pairs(getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("doDiscardOne") mat.call("doDiscardOne")
end end
end end
-- Triggers the metadata sync for all playmats -- Triggers the metadata sync for all playermats
PlaymatApi.syncAllCustomizableCards = function() PlayermatApi.syncAllCustomizableCards = function()
for _, mat in pairs(getMatForColor("All")) do for _, mat in pairs(getMatForColor("All")) do
mat.call("syncAllCustomizableCards") mat.call("syncAllCustomizableCards")
end end
end end
return PlaymatApi return PlayermatApi
end end
end) end)
__bundle_register("util/SearchLib", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("util/SearchLib", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local SearchLib = {} local SearchLib = {}
local filterFunctions = { local filterFunctions = {
isActionToken = function(x) return x.getDescription() == "Action Token" end,
isCard = function(x) return x.type == "Card" end, isCard = function(x) return x.type == "Card" end,
isDeck = function(x) return x.type == "Deck" end, isDeck = function(x) return x.type == "Deck" end,
isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end, isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end,
isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end, isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end,
isTileOrToken = function(x) return x.type == "Tile" end isTileOrToken = function(x) return x.type == "Tile" end,
isUniversalToken = function(x) return x.getMemo() == "universalActionAbility" end,
} }
-- performs the actual search and returns a filtered list of object references -- performs the actual search and returns a filtered list of object references
@ -527,7 +541,7 @@ do
max_distance = maxDistance or 0 max_distance = maxDistance or 0
}) })
-- filtering the result -- filter the result for matching objects
local objList = {} local objList = {}
for _, v in ipairs(searchResult) do for _, v in ipairs(searchResult) do
if not filter or filterFunc(v.hit_object) then if not filter or filterFunc(v.hit_object) then
@ -544,21 +558,22 @@ do
-- searches the area on an object -- searches the area on an object
SearchLib.onObject = function(obj, filter) SearchLib.onObject = function(obj, filter)
pos = obj.getPosition() local pos = obj.getPosition()
size = obj.getBounds().size:setAt("y", 1) local size = obj.getBounds().size:setAt("y", 1)
return returnSearchResult(pos, _, size, filter) return returnSearchResult(pos, _, size, filter)
end end
-- searches the specified position (a single point) -- searches the specified position (a single point)
SearchLib.atPosition = function(pos, filter) SearchLib.atPosition = function(pos, filter)
size = { 0.1, 2, 0.1 } local size = { 0.1, 2, 0.1 }
return returnSearchResult(pos, _, size, filter) return returnSearchResult(pos, _, size, filter)
end end
-- searches below the specified position (downwards until y = 0) -- searches below the specified position (downwards until y = 0)
SearchLib.belowPosition = function(pos, filter) SearchLib.belowPosition = function(pos, filter)
direction = { 0, -1, 0 } local size = { 0.1, 2, 0.1 }
maxDistance = pos.y local direction = { 0, -1, 0 }
local maxDistance = pos.y
return returnSearchResult(pos, _, size, filter, direction, maxDistance) return returnSearchResult(pos, _, size, filter, direction, maxDistance)
end end
@ -568,8 +583,17 @@ end)
__bundle_register("playercards/CardsThatSealTokens", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("playercards/CardsThatSealTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that seal tokens --[[ Library for cards that seal tokens
This file is used to add sealing option to cards' context menu. This file is used to add sealing option to cards' context menu.
NOTE: all cards are allowed to release a single token to enable Hallow and A Watchful Peace,
and to release all sealed tokens to allow for cards that might leave play with sealed tokens on them.
Valid options (set before requiring this file): Valid options (set before requiring this file):
MAX_SEALED --@type: number (maximum number of tokens allowable by the card to be sealed)
- required for all cards
- if MAX_SEALED is more than 1, then an XML label is created for the topmost token indicating the number of sealed tokens
- gives an error if user tries to seal additional tokens on the card
- example usage: "The Chthonian Stone"
> MAX_SEALED = 1
UPDATE_ON_HOVER --@type: boolean UPDATE_ON_HOVER --@type: boolean
- automatically updates the context menu options when the card is hovered - automatically updates the context menu options when the card is hovered
- the "Read Bag" function reads the content of the chaos bag to update the context menu - the "Read Bag" function reads the content of the chaos bag to update the context menu
@ -580,19 +604,16 @@ KEEP_OPEN --@type: boolean
- makes the context menu stay open after selecting an option - makes the context menu stay open after selecting an option
- example usage: "Unrelenting" - example usage: "Unrelenting"
SHOW_SINGLE_RELEASE --@type: boolean SHOW_MULTI_RELEASE --@type: number (maximum amount of tokens to release at once)
- enables an entry in the context menu - enables an entry in the context menu
- this entry allows releasing a single token - this entry allows releasing of multiple tokens at once, to the maximum number
- example usage: "Holy Spear" (to keep the other tokens and just release one) - does not fail if there are fewer than the maximum sealed
- example usage: "Nephthys" (to release up to 3 bless tokens at once)
SHOW_MULTI_RELEASE --@type: number (amount of tokens to release at once)
- enables an entry in the context menu
- this entry allows releasing of multiple tokens at once
- example usage: "Nephthys" (to release 3 bless tokens at once)
SHOW_MULTI_RETURN --@type: number (amount of tokens to return to pool at once) SHOW_MULTI_RETURN --@type: number (amount of tokens to return to pool at once)
- enables an entry in the context menu - enables an entry in the context menu
- this entry allows returning tokens to the token pool - this entry allows returning tokens to the token pool
- fails if not enough tokens are sealed
- example usage: "Nephthys" (to return 3 bless tokens at once) - example usage: "Nephthys" (to return 3 bless tokens at once)
SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once) SHOW_MULTI_SEAL --@type: number (amount of tokens to seal at once)
@ -626,6 +647,7 @@ Thus it should be implemented like this:
> ["+1"] = true, > ["+1"] = true,
> ["Elder Sign"] = true > ["Elder Sign"] = true
> } > }
> MAX_SEALED = 1
> require... > require...
---------------------------------------------------------- ----------------------------------------------------------
Example 2: Holy Spear Example 2: Holy Spear
@ -636,21 +658,35 @@ Thus it should be implemented like this:
> VALID_TOKENS = { > VALID_TOKENS = {
> ["Bless"] = true > ["Bless"] = true
> } > }
> SHOW_SINGLE_RELEASE = true
> SHOW_MULTI_SEAL = 2 > SHOW_MULTI_SEAL = 2
> MAX_SEALED = 10
> require... > require...
----------------------------------------------------------]] ----------------------------------------------------------]]
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local chaosBagApi = require("chaosbag/ChaosBagApi") local chaosBagApi = require("chaosbag/ChaosBagApi")
local guidReferenceApi = require("core/GUIDReferenceApi") local guidReferenceApi = require("core/GUIDReferenceApi")
local playmatApi = require("playermat/PlaymatApi") local playermatApi = require("playermat/PlayermatApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi") local tokenArrangerApi = require("accessories/TokenArrangerApi")
local sealedTokens = {} local sealedTokens = {}
local ID_URL_MAP = {} local ID_URL_MAP = {}
local tokensInBag = {} local tokensInBag = {}
-- XML background color for each token for label when stacked
local tokenColor = {
["Skull"] = "#4A0400E6",
["Cultist"] = "#173B0BE6",
["Tablet"] = "#1D2238E6",
["Elder Thing"] = "#4D2331E6",
["Auto-fail"] = "#9B0004E6",
["Bless"] = "#9D702CE6",
["Curse"] = "#633A84E6",
["Frost"] = "#404450E6",
["Elder Sign"] = "#50A8CEE6",
[""] = "#77674DE6"
}
function onSave() return JSON.encode(sealedTokens) end function onSave() return JSON.encode(sealedTokens) end
function onLoad(savedData) function onLoad(savedData)
@ -662,13 +698,15 @@ end
-- builds the context menu -- builds the context menu
function generateContextMenu() function generateContextMenu()
-- conditional single or multi release options self.addContextMenuItem("Release one token", releaseOneToken)
if SHOW_SINGLE_RELEASE then
self.addContextMenuItem("Release token", releaseOneToken) -- conditional release options
elseif SHOW_MULTI_RELEASE then if MAX_SEALED > 1 then
self.addContextMenuItem("Release all tokens", releaseAllTokens)
end
if SHOW_MULTI_RELEASE then
self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens) self.addContextMenuItem("Release " .. SHOW_MULTI_RELEASE .. " token(s)", releaseMultipleTokens)
else
self.addContextMenuItem("Release token(s)", releaseAllTokens)
end end
if RESOLVE_TOKEN then if RESOLVE_TOKEN then
@ -706,7 +744,7 @@ function generateContextMenu()
end end
if allowed then if allowed then
for i = 1, SHOW_MULTI_SEAL do for i = SHOW_MULTI_SEAL, 1, -1 do
sealToken(map.name, playerColor) sealToken(map.name, playerColor)
end end
else else
@ -743,6 +781,10 @@ end
-- seals the named token on this card -- seals the named token on this card
function sealToken(name, playerColor) function sealToken(name, playerColor)
if #sealedTokens >= MAX_SEALED then
printToColor("Cannot seal any more tokens on this card", playerColor, "Red")
return
end
if not chaosBagApi.canTouchChaosTokens() then return end if not chaosBagApi.canTouchChaosTokens() then return end
local chaosbag = chaosBagApi.findChaosBag() local chaosbag = chaosBagApi.findChaosBag()
for i, obj in ipairs(chaosbag.getObjects()) do for i, obj in ipairs(chaosbag.getObjects()) do
@ -759,6 +801,16 @@ function sealToken(name, playerColor)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.sealedToken(name, guid) blessCurseManagerApi.sealedToken(name, guid)
end end
-- destroy XML on just covered token
if #sealedTokens > 1 then
local coveredToken = getObjectFromGUID(sealedTokens[#sealedTokens - 1])
if coveredToken ~= nil then
coveredToken.UI.setXml("")
else
table.remove(sealedTokens, #sealedTokens - 1)
end
end
updateStackSize()
end end
}) })
return return
@ -778,16 +830,22 @@ function releaseOneToken(playerColor)
end end
end end
-- release multiple tokens at once -- release up to multiple tokens at once with no minimum
function releaseMultipleTokens(playerColor) function releaseMultipleTokens(playerColor)
if SHOW_MULTI_RELEASE <= #sealedTokens then if #sealedTokens == 0 then
for i = 1, SHOW_MULTI_RELEASE do printToColor("Not enough tokens sealed.", playerColor)
return
end
local numRemoved = SHOW_MULTI_RELEASE
if #sealedTokens < SHOW_MULTI_RELEASE then
numRemoved = #sealedTokens
end
for i = 1, numRemoved do
putTokenAway(table.remove(sealedTokens)) putTokenAway(table.remove(sealedTokens))
end end
printToColor("Releasing " .. SHOW_MULTI_RELEASE .. " tokens", playerColor) printToColor("Releasing " .. numRemoved .. " tokens", playerColor)
else
printToColor("Not enough tokens sealed.", playerColor)
end
end end
-- releases all sealed tokens -- releases all sealed tokens
@ -828,6 +886,7 @@ function putTokenAway(guid)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.releasedToken(name, guid) blessCurseManagerApi.releasedToken(name, guid)
end end
updateStackSize()
end end
-- returns the token to the pool (== removes it) -- returns the token to the pool (== removes it)
@ -840,6 +899,7 @@ function returnToken(guid)
if name == "Bless" or name == "Curse" then if name == "Bless" or name == "Curse" then
blessCurseManagerApi.returnedToken(name, guid) blessCurseManagerApi.returnedToken(name, guid)
end end
updateStackSize()
end end
-- resolves sealed token as if it came from the chaos bag -- resolves sealed token as if it came from the chaos bag
@ -848,11 +908,47 @@ function resolveSealed()
broadcastToAll("No tokens sealed.", "Red") broadcastToAll("No tokens sealed.", "Red")
return return
end end
local closestMatColor = playmatApi.getMatColorByPosition(self.getPosition()) local closestMatColor = playermatApi.getMatColorByPosition(self.getPosition())
local mat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat") local mat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat")
local guidToBeResolved = table.remove(sealedTokens) local guidToBeResolved = table.remove(sealedTokens)
local resolvedToken = getObjectFromGUID(guidToBeResolved)
resolvedToken.UI.setXml("")
updateStackSize()
chaosBagApi.drawChaosToken(mat, true, _, guidToBeResolved) chaosBagApi.drawChaosToken(mat, true, _, guidToBeResolved)
end end
function updateStackSize()
if MAX_SEALED == 1 then return end
if #sealedTokens == 0 then return end
-- get topmost sealed token
local topToken = getObjectFromGUID(sealedTokens[#sealedTokens])
local name = topToken.getName()
topToken.UI.setXmlTable({
{
tag = "Panel",
attributes = {
height = 380,
width = 380,
rotation = "0 0 180",
scale = "0.2 0.2 1",
position = "0 0 -12",
color = tokenColor[name] or "#77674DE6"
},
children = {
tag = "Text",
attributes = {
fontSize = "380",
font = "font_teutonic-arkham",
color = "#ffffff",
outline = "#000000",
outlineSize = "8 -8",
text = "x" .. #sealedTokens
}
}
}
})
end
end) end)
__bundle_register("accessories/TokenArrangerApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("accessories/TokenArrangerApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
@ -888,6 +984,83 @@ do
return TokenArrangerApi return TokenArrangerApi
end end
end) end)
__bundle_register("chaosbag/BlessCurseManagerApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local BlessCurseManagerApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
local function getManager()
return guidReferenceApi.getObjectByOwnerAndType("Mythos", "BlessCurseManager")
end
-- removes all taken tokens and resets the counts
BlessCurseManagerApi.removeTakenTokensAndReset = function()
local BlessCurseManager = getManager()
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Bless") end, 0.05)
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "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)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.sealedToken = function(type, guid)
getManager().call("sealedToken", { type = type, guid = guid })
end
-- updates the internal count (called by cards that seal bless/curse tokens)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
---@param fromBag? boolean Whether or not token was just drawn from the chaos bag
BlessCurseManagerApi.releasedToken = function(type, guid, fromBag)
getManager().call("releasedToken", { type = type, guid = guid, fromBag = fromBag })
end
-- updates the internal count (called by cards that seal bless/curse tokens)
---@param type string Type of chaos token ("Bless" or "Curse")
---@param guid string GUID of the token
BlessCurseManagerApi.returnedToken = function(type, guid)
getManager().call("returnedToken", { type = type, guid = guid })
end
-- broadcasts the current status for bless/curse tokens
---@param playerColor string Color of the player to show the broadcast to
BlessCurseManagerApi.broadcastStatus = function(playerColor)
getManager().call("broadcastStatus", playerColor)
end
-- removes all bless / curse tokens from the chaos bag and play
---@param playerColor string Color of the player to show the broadcast to
BlessCurseManagerApi.removeAll = function(playerColor)
getManager().call("doRemove", playerColor)
end
-- adds bless / curse sealing to the hovered card
---@param playerColor string Color of the player to show the broadcast to
---@param hoveredObject tts__Object Hovered object
BlessCurseManagerApi.addBlurseSealingMenu = function(playerColor, hoveredObject)
getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject })
end
-- adds bless / curse to the chaos bag
---@param type string Type of chaos token ("Bless" or "Curse")
BlessCurseManagerApi.addToken = function(type)
getManager().call("addToken", type)
end
-- removes bless / curse from the chaos bag
---@param type string Type of chaos token ("Bless" or "Curse")
BlessCurseManagerApi.removeToken = function(type)
getManager().call("removeToken", type)
end
BlessCurseManagerApi.getBlessCurseInBag = function()
return getManager().call("getBlessCurseInBag", {})
end
return BlessCurseManagerApi
end
end)
__bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules) __bundle_register("core/GUIDReferenceApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do do
local GUIDReferenceApi = {} local GUIDReferenceApi = {}

View File

@ -21,7 +21,8 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"60121\",\n \"type\": \"Event\",\n \"class\": \"Guardian\",\n GMNotes: "{\n \"id\": \"60121\",\n \"type\": \"Event\",\n \"class\": \"Guardian\",\n
\ \"cost\": 2,\n \"level\": 1,\n \"traits\": \"Spirit.\",\n \"willpowerIcons\": \ \"cost\": 2,\n \"level\": 1,\n \"traits\": \"Spirit.\",\n \"willpowerIcons\":
2,\n \"cycle\": \"Investigator Packs\"\n}" 2,\n \"uses\": [\n {\n \"count\": 1,\n \"type\": \"Fight\",\n \"token\":
\"universalActionAbility\"\n }\n ],\n \"cycle\": \"Investigator Packs\"\n}"
GUID: 9e7f6a GUID: 9e7f6a
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,7 +21,9 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"09053\",\n \"type\": \"Event\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"09053\",\n \"type\": \"Event\",\n \"class\": \"Seeker\",\n
\ \"cost\": 0,\n \"level\": 1,\n \"traits\": \"Insight.\",\n \"willpowerIcons\": \ \"cost\": 0,\n \"level\": 1,\n \"traits\": \"Insight.\",\n \"willpowerIcons\":
1,\n \"wildIcons\": 1,\n \"cycle\": \"The Scarlet Keys\"\n}" 1,\n \"wildIcons\": 1,\n \"uses\": [\n {\n \"count\": 1,\n \"type\":
\"Universal\",\n \"token\": \"universalActionAbility\"\n }\n ],\n \"cycle\":
\"The Scarlet Keys\"\n}"
GUID: '425841' GUID: '425841'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,7 +21,8 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"03265\",\n \"type\": \"Event\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"03265\",\n \"type\": \"Event\",\n \"class\": \"Seeker\",\n
\ \"cost\": 0,\n \"level\": 0,\n \"traits\": \"Insight.\",\n \"wildIcons\": 1,\n \ \"cost\": 0,\n \"level\": 0,\n \"traits\": \"Insight.\",\n \"wildIcons\": 1,\n
\ \"cycle\": \"The Path to Carcosa\"\n}" \ \"uses\": [\n {\n \"count\": 1,\n \"type\": \"Universal\",\n \"token\":
\"universalActionAbility\"\n }\n ],\n \"cycle\": \"The Path to Carcosa\"\n}"
GUID: bbfe9b GUID: bbfe9b
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -24,14 +24,14 @@ GMNotes: "{\n \"id\": \"10015\",\n \"type\": \"Investigator\",\n \"class\": \
1,\n \"maxCount\": 1,\n \"id\": \"10015-b1\"\n },\n {\n \"count\": 1,\n \"maxCount\": 1,\n \"id\": \"10015-b1\"\n },\n {\n \"count\":
1,\n \"maxCount\": 1,\n \"id\": \"10015-b2\"\n }\n ],\n \"willpowerIcons\": 1,\n \"maxCount\": 1,\n \"id\": \"10015-b2\"\n }\n ],\n \"willpowerIcons\":
3,\n \"intellectIcons\": 1,\n \"combatIcons\": 5,\n \"agilityIcons\": 3,\n \"cycle\": 3,\n \"intellectIcons\": 1,\n \"combatIcons\": 5,\n \"agilityIcons\": 3,\n \"cycle\":
\"The Feast of Hemlock Vale\",\n \"deck_requirements\": {\n \"size\": 35,\n \"The Feast of Hemlock Vale\",\n \"extraToken\": \"None\",\n \"deck_requirements\":
\ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"10017\": {\n \"size\": 35,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\":
1\n },\n {\n \"10018\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"10017\": 1\n },\n {\n \"10018\": 1\n }\n
[\n {\n \"faction\": [\n \"seeker\",\n \"neutral\"\n ],\n \ ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"trait\": [\n \"insight\",\n \"spirit\"\n ],\n \"level\": 5\n }\n },\n {\n \"trait\": [\n \"insight\",\n \"spirit\"\n
{\n \"min\": 0,\n \"max\": 2\n },\n \"limit\": 10\n }\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 2\n },\n
\ ]\n}" \ \"limit\": 10\n }\n ]\n}"
GUID: 3764cd GUID: 3764cd
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -20,8 +20,8 @@ CustomDeck:
Description: '' Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90048\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n GMNotes: "{\n \"id\": \"90048\",\n \"type\": \"Treachery\",\n \"class\": \"Neutral\",\n
\ \"traits\": \"Hardship.\",\n \"weakness\": true,\n \"cycle\": \"The Dunwich \ \"traits\": \"Hardship.\",\n \"weakness\": true,\n \"cycle\": \"On the Road
Legacy\"\n}" Again\"\n}"
GUID: '876557' GUID: '876557'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,11 +22,11 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"60201\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"60201\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n
\ \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \ \"traits\": \"Miskatonic.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
5,\n \"combatIcons\": 1,\n \"agilityIcons\": 2,\n \"cycle\": \"Investigator Packs\",\n 5,\n \"combatIcons\": 1,\n \"agilityIcons\": 2,\n \"cycle\": \"Investigator Packs\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"60202\": 1\n },\n {\n \"60203\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"60202\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n 1\n },\n {\n \"60203\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"seeker\",\n \"neutral\"\n ],\n
5\n }\n }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n }\n ]\n}"
GUID: 1fa944 GUID: 1fa944
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -0,0 +1,250 @@
-- Bundled by luabundle {"version":"1.6.0"}
local __bundle_require, __bundle_loaded, __bundle_register, __bundle_modules = (function(superRequire)
local loadingPlaceholder = {[{}] = true}
local register
local modules = {}
local require
local loaded = {}
register = function(name, body)
if not modules[name] then
modules[name] = body
end
end
require = function(name)
local loadedModule = loaded[name]
if loadedModule then
if loadedModule == loadingPlaceholder then
return nil
end
else
if not modules[name] then
if not superRequire then
local identifier = type(name) == 'string' and '\"' .. name .. '\"' or tostring(name)
error('Tried to require ' .. identifier .. ', but no such module has been registered')
else
return superRequire(name)
end
end
loaded[name] = loadingPlaceholder
loadedModule = modules[name](require, loaded, register, modules)
loaded[name] = loadedModule
end
return loadedModule
end
return require, loaded, register, modules
end)(nil)
__bundle_register("playercards/CardsWithHelper", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that have helpers
This file is used to share code between cards with helpers.
It syncs the visibility of the helper with the option panel and
makes sure the card has the respective tag.
Additionally, it will call 'initiliaze()' and 'shutOff()'
in the parent file if they are present.
Instructions:
1) Define the global variables before requiring this file:
hasXML = true (whether the card has an XML display)
isHelperEnabled = false (default state of the helper, should be 'false')
2) In 'onLoad()'', call 'syncDisplayWithOptionPanel()'
----------------------------------------------------------]]
local optionPanelApi = require("core/OptionPanelApi")
-- if the respective option is enabled in onLoad(), enable the helper
function syncDisplayWithOptionPanel()
self.addTag("CardWithHelper")
local options = optionPanelApi.getOptions()
if options.enableCardHelpers then
setHelperState(true)
else
updateDisplay()
end
end
-- forces a new state
function setHelperState(newState)
isHelperEnabled = newState
updateSave()
updateDisplay()
end
-- toggles the current state
function toggleHelper()
isHelperEnabled = not isHelperEnabled
updateSave()
updateDisplay()
end
-- updates the visibility and calls events (after a small delay to allow XML being set)
function updateDisplay()
Wait.frames(actualDisplayUpdate, 5)
end
function actualDisplayUpdate()
if isHelperEnabled then
self.clearContextMenu()
self.addContextMenuItem("Disable Helper", toggleHelper)
if hasXML then self.UI.show("Helper") end
if initialize then initialize() end
else
self.clearContextMenu()
self.addContextMenuItem("Enable Helper", toggleHelper)
if hasXML then self.UI.hide("Helper") end
if shutOff then shutOff() end
end
end
end)
__bundle_register("core/OptionPanelApi", function(require, _LOADED, __bundle_register, __bundle_modules)
do
local OptionPanelApi = {}
-- loads saved options
---@param options table Set a new state for the option table
OptionPanelApi.loadSettings = function(options)
return Global.call("loadSettings", options)
end
---@return any: Table of option panel state
OptionPanelApi.getOptions = function()
return Global.getTable("optionPanel")
end
return OptionPanelApi
end
end)
__bundle_register("__root", function(require, _LOADED, __bundle_register, __bundle_modules)
require("playercards/cards/HeavyFurs")
end)
__bundle_register("playercards/cards/HeavyFurs", function(require, _LOADED, __bundle_register, __bundle_modules)
VALID_TOKENS = {
["Skull"] = true,
["Tablet"] = true,
["Elder Thing"] = true,
["Cultist"] = true,
["Frost"] = true,
["Custom Token"] = true,
["Elder Sign"] = true,
["Bless"] = true,
["Curse"] = true
}
require("playercards/CardsWithHelper")
require("playercards/CardsThatRedrawTokens")
end)
__bundle_register("playercards/CardsThatRedrawTokens", function(require, _LOADED, __bundle_register, __bundle_modules)
--[[ Library for cards that return and redraw tokens
This file is used to add an XML button to a card, turned on via context menu.
Valid options modify the appearance of the XML button, as well as the
behavior of the return and redraw function. Set options before requiring this file.
Parameters for the return and redraw functions. Typically set VALID_TOKENS or INVALID_TOKENS, not both.
If there are no restrictions on which tokens can be redrawn (e.g. Wendy Adams), keep both empty.
* VALID_TOKENS --@type table
- keyed table which lists all tokens that can be redrawn by the card
- example usage: "False Covenant"
> VALID_TOKENS = {
> ["Curse"] = true
> }
* INVALID_TOKENS --@type table
- keyed table which lists all tokens that cannot be redrawn by the card
- example usage: "Custom Ammunition"
> INVALID_TOKENS = {
> ["Auto-fail"] = true
> }
* DRAW_SPECIFIC_TOKEN --@type string (name of token or nil)
- if set, will attempt to draw that specific token
* RETURN_TO_POOL --@type string
- allows for the name of the card to be passed onto Global for any special handling
The following parameters modify the appearence of the XML button and are not listed as part of a table.
- buttonHeight (default is 450)
- buttonWidth (default is 1400)
- buttonPosition (default is "0 -55 -22")
- buttonFontSize (default is 250)
- buttonRotation (change if button is placed on an investigator cards)
- buttonLabel (default is "Redraw Token")
- buttonIcon (to add an icon to the right)
- buttonColor (default is "#77674DE6")
----------------------------------------------------------
EXAMPLE: Claypool's Furs
This card can only redraw the Frost token, and is replaced with a random token from the bag.
As a nice reminder the XML button takes on the Frost color and icon with the text "Cancel".
> buttonValue = "Cancel"
> buttonIcon = "token-frost"
> buttonColor = "#404450E6"
> buttonFontSize = 300
> VALID_TOKENS = {
> ["Frost"] = true
> }
>
> require...
----------------------------------------------------------]]
-- intentionally global
hasXML = true
isHelperEnabled = false
function updateSave()
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
end
function onLoad(savedData)
if savedData and savedData ~= "" then
local loadedData = JSON.decode(savedData)
isHelperEnabled = loadedData.isHelperEnabled
end
createHelperXML()
syncDisplayWithOptionPanel()
end
function createHelperXML()
local xmlTable = { {
tag = "Button",
attributes = {
active = "false",
id = "Helper",
height = buttonHeight or 450,
width = buttonWidth or 1400,
rotation = buttonRotation or "0 0 180",
scale = "0.1 0.1 1",
position = buttonPosition or "0 -55 -22",
padding = "50 50 50 50",
font = "font_teutonic-arkham",
fontSize = buttonFontSize or 250,
onClick = "triggerXMLTokenLabelCreation",
color = buttonColor or "#77674DE6",
textColor = "White"
},
value = buttonLabel or "Redraw Token"
} }
if buttonIcon then
xmlTable[1].attributes.iconWidth = "400"
xmlTable[1].attributes.iconAlignment = "Right"
xmlTable[1].attributes.icon = buttonIcon
end
self.UI.setXmlTable(xmlTable)
end
function triggerXMLTokenLabelCreation()
Global.call("activeRedrawEffect", {
VALID_TOKENS = VALID_TOKENS,
INVALID_TOKENS = INVALID_TOKENS,
RETURN_TO_POOL = RETURN_TO_POOL
})
end
end)
return __bundle_require("__root")

View File

@ -30,7 +30,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: '' LuaScript: !include 'Card Heavy Furs 275450.ttslua'
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card

View File

@ -21,7 +21,7 @@ Description: Artifact from Another Life (Advanced)
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90018\",\n \"type\": \"Asset\",\n \"slot\": \"Accessory\",\n GMNotes: "{\n \"id\": \"90018\",\n \"type\": \"Asset\",\n \"slot\": \"Accessory\",\n
\ \"class\": \"Neutral\",\n \"cost\": 3,\n \"traits\": \"Item. Relic.\",\n \"willpowerIcons\": \ \"class\": \"Neutral\",\n \"cost\": 3,\n \"traits\": \"Item. Relic.\",\n \"willpowerIcons\":
1,\n \"combatIcons\": 1,\n \"wildIcons\": 2,\n \"cycle\": \"Standalone\"\n}" 1,\n \"combatIcons\": 1,\n \"wildIcons\": 2,\n \"cycle\": \"Bad Blood\"\n}"
GUID: bf151d GUID: bf151d
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,25 +21,29 @@ Description: ''
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"09061\",\n \"type\": \"Event\",\n \"class\": \"Rogue\",\n GMNotes: "{\n \"id\": \"09061\",\n \"type\": \"Event\",\n \"class\": \"Rogue\",\n
\ \"cost\": 1,\n \"level\": 0,\n \"traits\": \"Gambit.\",\n \"agilityIcons\": \ \"cost\": 1,\n \"level\": 0,\n \"traits\": \"Gambit.\",\n \"agilityIcons\":
1,\n \"customizations\": [\n {\n \"name\": \"Reflex Response\",\n \"xp\": 1,\n \"uses\": [\n {\n \"count\": 1,\n \"type\": \"Universal\",\n
1,\n \"text\": \"Add the following play condition: \u201C- You take damage \ \"token\": \"universalActionAbility\"\n }\n ],\n \"customizations\":
or horror.\u201D\"\n },\n {\n \"name\": \"Situational Awareness\",\n [\n {\n \"name\": \"Reflex Response\",\n \"xp\": 1,\n \"text\":
\ \"xp\": 1,\n \"text\": \"Add the following play condition: \u201C- A \"Add the following play condition: \u201C- You take damage or horror.\u201D\"\n
location enters play or is revealed.\u201D\"\n },\n {\n \"name\": \"Killer \ },\n {\n \"name\": \"Situational Awareness\",\n \"xp\": 1,\n \"text\":
Instinct\",\n \"xp\": 1,\n \"text\": \"Add the following play condition: \"Add the following play condition: \u201C- A location enters play or is revealed.\u201D\"\n
\u201C- An enemy engages you.\u201D\"\n },\n {\n \"name\": \"Gut Reaction\",\n \ },\n {\n \"name\": \"Killer Instinct\",\n \"xp\": 1,\n \"text\":
\ \"xp\": 1,\n \"text\": \"Add the following play condition: \u201C- A \"Add the following play condition: \u201C- An enemy engages you.\u201D\"\n },\n
treachery enters your threat area .\u201D\"\n },\n {\n \"name\": \"Muscle \ {\n \"name\": \"Gut Reaction\",\n \"xp\": 1,\n \"text\": \"Add
Memory\",\n \"xp\": 1,\n \"text\": \"Add the following play condition: the following play condition: \u201C- A treachery enters your threat area .\u201D\"\n
\u201C- You play an asset.\u201D\"\n },\n {\n \"name\": \"Sharpened Talent\",\n \ },\n {\n \"name\": \"Muscle Memory\",\n \"xp\": 1,\n \"text\":
\ \"xp\": 2,\n \"text\": \"During the action granted by Honed Instinct, \"Add the following play condition: \u201C- You play an asset.\u201D\"\n },\n
you get +2 to each of your skills.\"\n },\n {\n \"name\": \"Impulse Control\",\n \ {\n \"name\": \"Sharpened Talent\",\n \"xp\": 2,\n \"text\":
\ \"xp\": 3,\n \"text\": \"You may include up to three copies of Honed \"During the action granted by Honed Instinct, you get +2 to each of your skills.\"\n
Instinct in your deck. Honed Instinct gets \u20131 cost.\",\n \"replaces\": \ },\n {\n \"name\": \"Impulse Control\",\n \"xp\": 3,\n \"text\":
{\n \"cost\": 0\n }\n },\n {\n \"name\": \"Force of Habit\",\n \"You may include up to three copies of Honed Instinct in your deck. Honed Instinct
\ \"xp\": 5,\n \"text\": \"When you play Honed Instinct, you may take 2 gets \u20131 cost.\",\n \"replaces\": {\n \"cost\": 0\n }\n },\n
actions instead of 1 (one at a time). Then, remove it from the game.\"\n }\n \ {\n \"name\": \"Force of Habit\",\n \"xp\": 5,\n \"text\": \"When
\ ],\n \"cycle\": \"The Scarlet Keys\"\n}" you play Honed Instinct, you may take 2 actions instead of 1 (one at a time). Then,
remove it from the game.\",\n \"replaces\": {\n \"uses\": [\n {\n
\ \"count\": 2,\n \"type\": \"Universal\",\n \"token\":
\"universalActionAbility\"\n }\n ]\n }\n }\n ],\n \"cycle\":
\"The Scarlet Keys\"\n}"
GUID: 1cde62 GUID: 1cde62
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -30,7 +30,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: !include 'Card Jacob Morrison (3) aa38d0.ttslua' LuaScript: ''
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card
@ -40,6 +40,7 @@ Snap: true
Sticky: true Sticky: true
Tags: Tags:
- Asset - Asset
- DoNotReady
- PlayerCard - PlayerCard
Tooltip: true Tooltip: true
Transform: Transform:

View File

@ -22,11 +22,11 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"60401\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"60401\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Clairvoyant.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\": \ \"traits\": \"Clairvoyant.\",\n \"willpowerIcons\": 5,\n \"intellectIcons\":
3,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Investigator Packs\",\n 3,\n \"combatIcons\": 2,\n \"agilityIcons\": 2,\n \"cycle\": \"Investigator Packs\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"60402\": 1\n },\n {\n \"60403\": \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"60402\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n 1\n },\n {\n \"60403\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"mystic\",\n \"neutral\"\n ],\n
5\n }\n }\n ]\n}" \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n }\n ]\n}"
GUID: a2cd75 GUID: a2cd75
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,14 +22,14 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"02003\",\n \"alternate_ids\": [\n \"98001\"\n ],\n \"type\": GMNotes: "{\n \"id\": \"02003\",\n \"alternate_ids\": [\n \"98001\"\n ],\n \"type\":
\"Investigator\",\n \"class\": \"Rogue\",\n \"traits\": \"Drifter.\",\n \"willpowerIcons\": \"Investigator\",\n \"class\": \"Rogue\",\n \"traits\": \"Drifter.\",\n \"willpowerIcons\":
3,\n \"intellectIcons\": 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\": 3,\n \"intellectIcons\": 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 3,\n \"cycle\":
\"The Dunwich Legacy\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \"The Dunwich Legacy\",\n \"extraToken\": \"None\",\n \"deck_requirements\": {\n
1,\n \"signatures\": [\n {\n \"98002\": 1,\n \"02010\": 1\n \ \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n
\ },\n {\n \"98003\": 1,\n \"02011\": 1\n }\n ]\n \ {\n \"98002\": 1,\n \"02010\": 1\n },\n {\n \"98003\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"rogue\",\n \"neutral\"\n 1,\n \"02011\": 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\":
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n [\n \"rogue\",\n \"neutral\"\n ],\n \"level\": {\n \"min\":
\ },\n {\n \"level\": {\n \"min\": 0,\n \"max\": 0\n },\n 0,\n \"max\": 5\n }\n },\n {\n \"level\": {\n \"min\":
\ \"limit\": 5,\n \"error\": \"You cannot have more than 5 cards that are 0,\n \"max\": 0\n },\n \"limit\": 5,\n \"error\": \"You cannot
not Rogue or Neutral\"\n }\n ]\n}" have more than 5 cards that are not Rogue or Neutral\"\n }\n ]\n}"
GUID: 9058d3 GUID: 9058d3
Grid: true Grid: true
GridProjection: false GridProjection: false
@ -71,15 +71,15 @@ States:
GMNotes: "{\n \"id\": \"02003\",\n \"alternate_ids\": [\n \"98001\"\n ],\n GMNotes: "{\n \"id\": \"02003\",\n \"alternate_ids\": [\n \"98001\"\n ],\n
\ \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n \"traits\": \"Drifter.\",\n \ \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n \"traits\": \"Drifter.\",\n
\ \"willpowerIcons\": 3,\n \"intellectIcons\": 3,\n \"combatIcons\": 3,\n \ \"willpowerIcons\": 3,\n \"intellectIcons\": 3,\n \"combatIcons\": 3,\n
\ \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\n \"deck_requirements\": \ \"agilityIcons\": 3,\n \"cycle\": \"The Dunwich Legacy\",\r\n \"extraToken\":
{\n \"size\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": \"None\"\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
[\n {\n \"98002\": 1,\n \"02010\": 1\n },\n {\n 1,\n \"signatures\": [\n {\n \"98002\": 1,\n \"02010\":
\ \"98003\": 1,\n \"02011\": 1\n }\n ]\n },\n \"deck_options\": 1\n },\n {\n \"98003\": 1,\n \"02011\": 1\n }\n
[\n {\n \"faction\": [\n \"rogue\",\n \"neutral\"\n ],\n \ ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"rogue\",\n
\ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
\ {\n \"level\": {\n \"min\": 0,\n \"max\": 0\n },\n 5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\":
\ \"limit\": 5,\n \"error\": \"You cannot have more than 5 cards that 0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than
are not Rogue or Neutral\"\n }\n ]\n}\n" 5 cards that are not Rogue or Neutral\"\n }\n ]\n}\n"
GUID: b954f6 GUID: b954f6
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,8 +21,8 @@ Description: The Musician
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02004-pb\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"02004-pb\",\n \"type\": \"Investigator\",\n \"class\":
\"Mystic\",\n \"traits\": \"Performer.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \"Mystic\",\n \"traits\": \"Performer.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
3,\n \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"The Dunwich Legacy\",\n 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"Laid to Rest\",\n
\ \"deck_requirements\": {\n \"size\": 39,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 39,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"90050\": 1,\n \"02012\": 1\n 1,\n \"signatures\": [\n {\n \"90050\": 1,\n \"02012\": 1\n
\ },\n {\n \"90051\": 1,\n \"02013\": 1\n },\n {\n \ },\n {\n \"90051\": 1,\n \"02013\": 1\n },\n {\n
\ \"90052\": 1\n },\n {\n \"90053\": 1\n }\n ]\n \ \"90052\": 1\n },\n {\n \"90053\": 1\n }\n ]\n

View File

@ -21,11 +21,11 @@ Description: The Musician
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02004-pf\",\n \"type\": \"Investigator\",\n \"class\": GMNotes: "{\n \"id\": \"02004-pf\",\n \"type\": \"Investigator\",\n \"class\":
\"Mystic\",\n \"traits\": \"Performer. Cursed.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \"Mystic\",\n \"traits\": \"Performer. Cursed.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
3,\n \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"The Dunwich Legacy\",\n 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"Laid to Rest\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 30,\n
1,\n \"signatures\": [\n {\n \"90050\": 1,\n \"02012\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90050\":
\ },\n {\n \"90051\": 1,\n \"02013\": 1\n }\n ]\n 1,\n \"02012\": 1\n },\n {\n \"90051\": 1,\n \"02013\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"level\": {\n \"min\": 0,\n \"max\":
0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5 0\n },\n \"limit\": 5,\n \"error\": \"You cannot have more than 5

View File

@ -21,12 +21,12 @@ Description: The Musician
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"02004-p\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"02004-p\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Performer. Cursed.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \ \"traits\": \"Performer. Cursed.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
3,\n \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"The Dunwich Legacy\",\n 3,\n \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"Laid to Rest\",\n
\ \"deck_requirements\": {\n \"size\": 39,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\": 39,\n
1,\n \"signatures\": [\n {\n \"90050\": 1,\n \"02012\": 1\n \ \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"90050\":
\ },\n {\n \"90051\": 1,\n \"02013\": 1\n },\n {\n 1,\n \"02012\": 1\n },\n {\n \"90051\": 1,\n \"02013\":
\ \"90052\": 1\n },\n {\n \"90053\": 1\n }\n ]\n 1\n },\n {\n \"90052\": 1\n },\n {\n \"90053\":
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"neutral\"\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"neutral\"\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n
\ },\n {\n \"faction\": [\n \"mystic\"\n ],\n \"level\": \ },\n {\n \"faction\": [\n \"mystic\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 3\n }\n },\n {\n \"faction\": {\n \"min\": 0,\n \"max\": 3\n }\n },\n {\n \"faction\":

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"02004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"02004\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Performer.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": 3,\n \ \"traits\": \"Performer.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": 3,\n
\ \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"The Dunwich Legacy\",\n \ \"combatIcons\": 3,\n \"agilityIcons\": 2,\n \"cycle\": \"The Dunwich Legacy\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"90050\": 1,\n \"02012\": 1\n 1,\n \"signatures\": [\n {\n \"90050\": 1,\n \"02012\": 1\n
\ },\n {\n \"90051\": 1,\n \"02013\": 1\n }\n ]\n \ },\n {\n \"90051\": 1,\n \"02013\": 1\n }\n ]\n
\ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n \ },\n \"deck_options\": [\n {\n \"faction\": [\n \"mystic\",\n

View File

@ -21,7 +21,7 @@ Description: The Dead Speak (Advanced)
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"90050\",\n \"type\": \"Asset\",\n \"slot\": \"Hand\",\n GMNotes: "{\n \"id\": \"90050\",\n \"type\": \"Asset\",\n \"slot\": \"Hand\",\n
\ \"class\": \"Neutral\",\n \"cost\": 2,\n \"traits\": \"Item. Instrument. Relic.\",\n \ \"class\": \"Neutral\",\n \"cost\": 2,\n \"traits\": \"Item. Instrument. Relic.\",\n
\ \"willpowerIcons\": 2,\n \"wildIcons\": 2,\n \"cycle\": \"The Dunwich Legacy\"\n}" \ \"willpowerIcons\": 2,\n \"wildIcons\": 2,\n \"cycle\": \"Laid to Rest\"\n}"
GUID: 7dfd5f GUID: 7dfd5f
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"05002\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"05002\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n
\ \"traits\": \"Detective.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": 4,\n \ \"traits\": \"Detective.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": 4,\n
\ \"combatIcons\": 4,\n \"agilityIcons\": 2,\n \"cycle\": \"The Circle Undone\",\n \ \"combatIcons\": 4,\n \"agilityIcons\": 2,\n \"cycle\": \"The Circle Undone\",\n
\ \"deck_requirements\": {\n \"size\": 40,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 40,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"05009\": 1\n },\n {\n \"05010\": 1,\n \"signatures\": [\n {\n \"05009\": 1\n },\n {\n \"05010\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":

View File

@ -22,14 +22,15 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"10004\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n GMNotes: "{\n \"id\": \"10004\",\n \"type\": \"Investigator\",\n \"class\": \"Seeker\",\n
\ \"traits\": \"Miskatonic. Scholar.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\": \ \"traits\": \"Miskatonic. Scholar.\",\n \"willpowerIcons\": 2,\n \"intellectIcons\":
4,\n \"combatIcons\": 2,\n \"agilityIcons\": 4,\n \"cycle\": \"The Feast of Hemlock 4,\n \"combatIcons\": 2,\n \"agilityIcons\": 4,\n \"cycle\": \"The Feast of Hemlock
Vale\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Vale\",\n \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"10005\": 1\n },\n {\n \"10008\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"10005\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"seeker\",\n 1\n },\n {\n \"10008\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"seeker\",\n \"neutral\"\n ],\n
5\n }\n },\n {\n \"trait\": [\n \"science\"\n ],\n \"level\": \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
{\n \"min\": 0,\n \"max\": 4\n }\n },\n {\n \"trait\": \ \"trait\": [\n \"science\"\n ],\n \"level\": {\n \"min\":
[\n \"insight\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": 0,\n \"max\": 4\n }\n },\n {\n \"trait\": [\n \"insight\"\n
1\n }\n }\n ]\n}" \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 1\n }\n
\ }\n ]\n}"
GUID: ce2322 GUID: ce2322
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -22,7 +22,7 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"09008\",\n \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n GMNotes: "{\n \"id\": \"09008\",\n \"type\": \"Investigator\",\n \"class\": \"Rogue\",\n
\ \"traits\": \"Criminal.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\": 2,\n \ \"traits\": \"Criminal.\",\n \"willpowerIcons\": 3,\n \"intellectIcons\": 2,\n
\ \"combatIcons\": 2,\n \"agilityIcons\": 5,\n \"cycle\": \"The Scarlet Keys\",\n \ \"combatIcons\": 2,\n \"agilityIcons\": 5,\n \"cycle\": \"The Scarlet Keys\",\n
\ \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": \ \"extraToken\": \"None\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\":
1,\n \"signatures\": [\n {\n \"09009\": 1\n },\n {\n \"09010\": 1,\n \"signatures\": [\n {\n \"09009\": 1\n },\n {\n \"09010\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"neutral\",\n 1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"neutral\",\n
\ \"rogue\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": \ \"rogue\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
<!-- include playercards/KohakuNarukami.xml -->
<Defaults>
<Button padding="50 50 50 50"
font="font_teutonic-arkham"
fontSize="200"
iconWidth="300"
iconAlignment="Right"/>
<TableLayout position="0 188 -22"
rotation="0 0 90"
height="1800"
width="700"
scale="0.1 0.1 1"
cellSpacing="80"
cellBackgroundColor="rgba(1,1,1,0)"/>
</Defaults>
<TableLayout id="Helper"
active="false">
<Row>
<Cell>
<Button id="Bless"
icon="token-bless"/>
</Cell>
</Row>
<Row>
<Cell>
<Button id="Curse"
icon="token-curse"/>
</Cell>
</Row>
<Row>
<Cell>
<Button id="Action"
text="Remove tokens"/>
</Cell>
</Row>
<Row>
<Cell>
<Button id="ElderSign"
icon="token-eldersign"/>
</Cell>
</Row>
</TableLayout>
<!-- include playercards/KohakuNarukami.xml -->

View File

@ -22,16 +22,16 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"10012\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n GMNotes: "{\n \"id\": \"10012\",\n \"type\": \"Investigator\",\n \"class\": \"Mystic\",\n
\ \"traits\": \"Scholar. Blessed. Cursed.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \ \"traits\": \"Scholar. Blessed. Cursed.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
4,\n \"combatIcons\": 3,\n \"agilityIcons\": 1,\n \"cycle\": \"The Feast of Hemlock 4,\n \"combatIcons\": 3,\n \"agilityIcons\": 1,\n \"cycle\": \"The Feast of Hemlock
Vale\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Vale\",\n \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"10013\": 1\n },\n {\n \"10014\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"10013\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"neutral\"\n 1\n },\n {\n \"10014\": 1\n }\n ]\n },\n \"deck_options\":
[\n {\n \"faction\": [\n \"neutral\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n \"faction\":
[\n \"mystic\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\":
3\n }\n },\n {\n \"trait\": [\n \"blessed\",\n \"cursed\"\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n \ ],\n \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n
\ },\n {\n \"faction\": [\n \"mystic\"\n ],\n \"level\": \ },\n {\n \"trait\": [\n \"occult\"\n ],\n \"level\":
{\n \"min\": 0,\n \"max\": 3\n }\n },\n {\n \"trait\": {\n \"min\": 0,\n \"max\": 0\n }\n }\n ]\n}"
[\n \"blessed\",\n \"cursed\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 5\n }\n },\n {\n \"trait\": [\n \"occult\"\n
\ ],\n \"level\": {\n \"min\": 0,\n \"max\": 0\n }\n
\ }\n ]\n}"
GUID: 54eaa7 GUID: 54eaa7
Grid: true Grid: true
GridProjection: false GridProjection: false
@ -40,7 +40,7 @@ HideWhenFaceDown: true
IgnoreFoW: false IgnoreFoW: false
LayoutGroupSortIndex: 0 LayoutGroupSortIndex: 0
Locked: false Locked: false
LuaScript: '' LuaScript: !include "Card K\u014Dhaku Narukami 54eaa7.ttslua"
LuaScriptState: '' LuaScriptState: ''
MeasureMovement: false MeasureMovement: false
Name: Card Name: Card
@ -63,4 +63,4 @@ Transform:
scaleY: 1 scaleY: 1
scaleZ: 1.15 scaleZ: 1.15
Value: 0 Value: 0
XmlUI: '' XmlUI: !include "Card K\u014Dhaku Narukami 54eaa7.xml"

View File

@ -22,12 +22,13 @@ DragSelectable: true
GMNotes: "{\n \"id\": \"04001\",\n \"type\": \"Investigator\",\n \"class\": \"Guardian\",\n GMNotes: "{\n \"id\": \"04001\",\n \"type\": \"Investigator\",\n \"class\": \"Guardian\",\n
\ \"traits\": \"Veteran. Wayfarer.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\": \ \"traits\": \"Veteran. Wayfarer.\",\n \"willpowerIcons\": 4,\n \"intellectIcons\":
3,\n \"combatIcons\": 4,\n \"agilityIcons\": 1,\n \"cycle\": \"The Forgotten 3,\n \"combatIcons\": 4,\n \"agilityIcons\": 1,\n \"cycle\": \"The Forgotten
Age\",\n \"deck_requirements\": {\n \"size\": 30,\n \"randomBasicWeaknessCount\": Age\",\n \"extraToken\": \"Reaction\",\n \"deck_requirements\": {\n \"size\":
1,\n \"signatures\": [\n {\n \"04006\": 1\n },\n {\n \"04007\": 30,\n \"randomBasicWeaknessCount\": 1,\n \"signatures\": [\n {\n \"04006\":
1\n }\n ]\n },\n \"deck_options\": [\n {\n \"faction\": [\n \"guardian\",\n 1\n },\n {\n \"04007\": 1\n }\n ]\n },\n \"deck_options\":
\ \"neutral\"\n ],\n \"level\": {\n \"min\": 0,\n \"max\": [\n {\n \"faction\": [\n \"guardian\",\n \"neutral\"\n ],\n
5\n }\n },\n {\n \"faction\": [\n \"rogue\"\n ],\n \"level\": \ \"level\": {\n \"min\": 0,\n \"max\": 5\n }\n },\n {\n
{\n \"min\": 0,\n \"max\": 2\n }\n }\n ]\n}" \ \"faction\": [\n \"rogue\"\n ],\n \"level\": {\n \"min\":
0,\n \"max\": 2\n }\n }\n ]\n}"
GUID: '126932' GUID: '126932'
Grid: true Grid: true
GridProjection: false GridProjection: false

View File

@ -21,7 +21,9 @@ Description: The Louisiana Lion
DragSelectable: true DragSelectable: true
GMNotes: "{\n \"id\": \"01054\",\n \"alternate_ids\": [\n \"01554\"\n ],\n \"type\": GMNotes: "{\n \"id\": \"01054\",\n \"alternate_ids\": [\n \"01554\"\n ],\n \"type\":
\"Asset\",\n \"slot\": \"Ally\",\n \"class\": \"Rogue\",\n \"cost\": 5,\n \"level\": \"Asset\",\n \"slot\": \"Ally\",\n \"class\": \"Rogue\",\n \"cost\": 5,\n \"level\":
1,\n \"traits\": \"Ally. Criminal.\",\n \"intellectIcons\": 1,\n \"cycle\": \"Core\"\n}" 1,\n \"traits\": \"Ally. Criminal.\",\n \"intellectIcons\": 1,\n \"uses\": [\n
\ {\n \"count\": 1,\n \"type\": \"Universal\",\n \"token\": \"universalActionAbility\"\n
\ }\n ],\n \"cycle\": \"Core\"\n}"
GUID: 27446e GUID: 27446e
Grid: true Grid: true
GridProjection: false GridProjection: false

Some files were not shown because too many files have changed in this diff Show More