Merge branch 'master' into patches

This commit is contained in:
Adam Goldsmith 2024-07-27 22:53:03 -04:00
commit 85cb7bc7e2
781 changed files with 66859 additions and 89457 deletions

File diff suppressed because it is too large Load Diff

View File

@ -567,16 +567,13 @@
<!-- Default formatting -->
<Defaults>
<Text color="#FFFFFF"
alignment="MiddleLeft" />
alignment="MiddleLeft"/>
<Toggle isOn="False"
rectAlignment="MiddleRight" />
<Dropdown rectAlignment="MiddleCenter" />
<Dropdown rectAlignment="MiddleCenter"/>
<Cell dontUseTableCellBackground="true"
outlineSize="0 1"
outline="grey" />
outline="grey"/>
<!-- main window -->
<TableLayout class="window"
@ -588,20 +585,20 @@
outline="grey"
showAnimation="SlideIn_Right"
hideAnimation="SlideOut_Right"
animationDuration="0.2" />
animationDuration="0.2"/>
<!-- group headers -->
<Row class="group-header"
preferredHeight="44" />
preferredHeight="44"/>
<Cell class="group-header"
padding="10 10 0 0"
columnSpan="3"
color="#222222" />
color="#222222"/>
<Panel class="group-header"
padding="5 0 0 0" />
padding="5 0 0 0"/>
<Text class="group-header"
fontSize="28"
font="font_teutonic-arkham" />
font="font_teutonic-arkham"/>
<!-- options -->
<Row class="option-text"
@ -630,6 +627,14 @@
font="font_teutonic-arkham"/>
<Panel class="doubleColumn-wrapper"
padding="0 17 3 3"/>
<Button class="optionToggle"
image="option_off"
rectAlignment="MiddleRight"
offsetXY="-30 0"
colors="#FFFFFF|#dfdfdf"
height="36"
width="65"
ignoreLayout="True"/>
<!-- buttons at the bottom -->
<Button class="bottomButtons"
@ -716,8 +721,24 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showTitleSplash"
onValueChanged="onClick_toggleOption(showTitleSplash)"/>
<Button class="optionToggle"
id="showTitleSplash"
onClick="onClick_toggleOption"/>
</Cell>
</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>
@ -740,8 +761,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="playAreaSnapTags"
onValueChanged="onClick_toggleOption(playAreaSnapTags)"/>
<Button class="optionToggle"
id="playAreaSnapTags"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -754,8 +776,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="playAreaConnections"
onValueChanged="onClick_toggleOption(playAreaConnections)"/>
<Button class="optionToggle"
id="playAreaConnections"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -785,8 +808,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="changePlayAreaImage"
onValueChanged="onClick_toggleOption(changePlayAreaImage)"/>
<Button class="optionToggle"
id="changePlayAreaImage"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -809,8 +833,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="useSnapTags"
onValueChanged="onClick_toggleOption(useSnapTags)"/>
<Button class="optionToggle"
id="useSnapTags"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -823,8 +848,24 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showDrawButton"
onValueChanged="onClick_toggleOption(showDrawButton)"/>
<Button class="optionToggle"
id="showDrawButton"
onClick="onClick_toggleOption"/>
</Cell>
</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>
@ -837,8 +878,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="useClueClickers"
onValueChanged="onClick_toggleOption(useClueClickers)"/>
<Button class="optionToggle"
id="useClueClickers"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -862,7 +904,7 @@
</Cell>
</Row>
<!-- Option: remove a player mat -->
<!-- Option: remove a playermat -->
<Row class="option-text"
tooltip="Remove an unused playermat for more table space.&#xA;Displayed are the default colors.">
<Cell class="option-singleColumn">
@ -903,8 +945,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showAttachmentHelper"
onValueChanged="onClick_toggleOption(showAttachmentHelper)"/>
<Button class="optionToggle"
id="showAttachmentHelper"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -917,8 +960,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showCleanUpHelper"
onValueChanged="onClick_toggleOption(showCleanUpHelper)"/>
<Button class="optionToggle"
id="showCleanUpHelper"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -931,8 +975,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showCYOA"
onValueChanged="onClick_toggleOption(showCYOA)"/>
<Button class="optionToggle"
id="showCYOA"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -945,8 +990,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showDisplacementTool"
onValueChanged="onClick_toggleOption(showDisplacementTool)"/>
<Button class="optionToggle"
id="showDisplacementTool"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -959,8 +1005,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showHandHelper"
onValueChanged="onClick_toggleOption(showHandHelper)"/>
<Button class="optionToggle"
id="showHandHelper"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
@ -973,8 +1020,9 @@
</Panel>
</Cell>
<Cell class="option-button">
<Toggle id="showSearchAssistant"
onValueChanged="onClick_toggleOption(showSearchAssistant)"/>
<Button class="optionToggle"
id="showSearchAssistant"
onClick="onClick_toggleOption"/>
</Cell>
</Row>
</TableLayout>

View File

@ -71,33 +71,18 @@ ComponentTags:
normalized: investigator
- displayed: chaosBag
normalized: chaosbag
- displayed: ActionToken
normalized: actiontoken
- displayed: LargeBox
normalized: largebox
- displayed: CampaignBox
normalized: campaignbox
- displayed: CameraZoom_ignore
normalized: camerazoom_ignore
- displayed: UniversalToken
normalized: universaltoken
CustomUIAssets:
- Name: refresh
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1695031152736214852/EC3BBEF1A1788381A8F4C5ACB7FB27770CAF03C5/
- Name: close
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1695031152736214756/2EEB07E453A7ECF4BE5A1030A253185B37A7CDAB/
- Name: cthulhu
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1782854877010107768/BC6A97F193385D01C1A9149B68923F55A284CB2D/
- Name: dark-cult
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1782854877010108105/08594607341D6537C28A08A34CE82159025AB8DB/
- Name: yog-sothoth
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1782854877010107124/D8042D1A1B08CFB7E76488B09216B4611D85A2B9/
- Name: elder-sign
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1782854877010107442/43BC029410751208A90AE7FDEBCB587A0E9403D7/
- Name: devourer
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/1782854877010106784/6E00433E3425D0A7C6121E0DDB6A79167BA78569/
@ -113,24 +98,18 @@ CustomUIAssets:
- Name: option-gear
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2026086584372569912/5CB461AEAE2E59D3064D90A776EB86C46081EC78/
- Name: font_birmingham
- Name: option_on
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2462982115668997008/2178787B67B3C96F3419EDBAB8420E39893756BC/
- Name: option_off
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2462982115668996901/D6438ECBB11DECC6DB9987589FF526FBAD4D2368/
- Name: font_arkhamicons
Type: 1
URL: http://cloud-3.steamusercontent.com/ugc/2027213118466443497/3CF9BB9AF968D245961494CC9A151774EB9BA638/
- Name: font_columbus
Type: 1
URL: http://cloud-3.steamusercontent.com/ugc/2027213118466515872/F473E4ACC75ACB6CE07457C45290B4912E0B3286/
- Name: font_oldremington
Type: 1
URL: http://cloud-3.steamusercontent.com/ugc/2027213118466515932/AFCE53F1E1D9580D166F53AD9EB0D77A331D4A26/
URL: http://cloud-3.steamusercontent.com/ugc/2462982115649258367/C20CC4C299A6FE5F1ECAB968E15BE590337CC019/
- Name: font_teutonic-arkham
Type: 1
URL: http://cloud-3.steamusercontent.com/ugc/2027213118467703445/89328E273B4C5180BF491516CE998DE3C604E162/
- Name: font_uglyqua
Type: 1
URL: http://cloud-3.steamusercontent.com/ugc/2027213118466516005/113C19D37CFFA9E554394FD5B11B32967F846A62/
- Name: font_chinese_fzlibian
Type: 1
URL: http://cloud-3.steamusercontent.com/ugc/2028355502896482829/8DAB311590B97586309E66D795AC2C43D4913188/
- Name: header_cover
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2280574378889753624/53E7443E2A9957BC5CA4D73B67D5C1C30971C9F9/
@ -143,24 +122,18 @@ CustomUIAssets:
- Name: header_olive
Type: 0
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
Type: 0
URL: https://i.imgur.com/6MReiEO.png
- Name: Inv-Roland
Type: 0
URL: https://i.imgur.com/lx6unDY.png
- Name: NextArrow
Type: 0
URL: https://i.imgur.com/MztSQis.png
- Name: Exit
Type: 0
URL: https://i.imgur.com/8qmTXwt.png
- Name: Inv-Roland
Type: 0
URL: https://i.imgur.com/lx6unDY.png
- Name: Inv-Mandy
Type: 0
URL: https://i.imgur.com/hniMC5g.png
@ -224,7 +197,37 @@ CustomUIAssets:
- Name: box-cover-mask-wide
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2115061298538827369/A20C2ECB8ECDC1B0AD8B2B38F68CA1C1F5E07D37/
Date: Mon Mar 4 23:52:37 CET 2024
- Name: token-skull
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374775547231/E0FEEF462DE4E7704832CA2415D0D027A6BF5041/
- Name: token-bless
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374775547028/7855033DE0EB1FDDF706E1303054D35FE0902532/
- Name: token-curse
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374775547135/2360372CBE9452CB7B4D135BE13BBA6D46B7D427/
- Name: token-cultist
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650461/10D8037632900AA86045BDD42A564716D5855B1B/
- Name: token-tablet
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650857/29DAE514E5C838C24C90ABBFFF92B1359B9A2F76/
- Name: token-elder-thing
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650683/1C046B6335317CA1AEBFC80645EEC18852D83D80/
- Name: token-frost
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650766/73E07A50BE6FD9BED266F3421B472C4BF913DE81/
- Name: token-auto-fail
Type: 0
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
Type: 0
URL: http://cloud-3.steamusercontent.com/ugc/2380784374792650571/E4C2B2B69282A4EE15FE290FF6B08BEFC8FCA65C/
Date: Mon Jul 8 20:19:48 CEST 2024
DecalPallet:
- ImageURL: http://cloud-3.steamusercontent.com/ugc/1474319121424323663/BC5570ECF747F1B30224461B576E8B0FE7FA5F33/
Name: Achivement Checkmark
@ -233,7 +236,7 @@ DecalPallet:
Name: Victory Display
Size: 15
Decals: []
EpochTime: 1709592757
EpochTime: 1720462788
GameComplexity: ''
GameMode: Arkham Horror LCG - Super Complete Edition
GameType: ''
@ -284,7 +287,7 @@ Lighting:
LutIndex: 0
ReflectionIntensity: 1
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:
AudioLibrary:
- Item1: http://cloud-3.steamusercontent.com/ugc/784110538847453001/4481D1CC5684FCF04AB143954DEFE09E94BF5CEB/
@ -342,11 +345,14 @@ MusicPlayer:
Note: ''
ObjectStates:
- !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/HandTrigger 5fe087.yaml'
- !include 'unpacked/HandTrigger be2f17.yaml'
- !include 'unpacked/HandTrigger 0285cc.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 TableLegBottomLeft c8edca.yaml'
- !include 'unpacked/Custom_Assetbundle TableLegTopLeft 393bf7.yaml'
@ -402,7 +408,9 @@ ObjectStates:
- !include 'unpacked/Custom_Model_Bag Trash 5f896a.yaml'
- !include 'unpacked/Custom_Model_Bag Trash 147e80.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 Latest FAQ faqfaq.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 3b2550.yaml'
@ -416,8 +424,6 @@ ObjectStates:
- !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 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 Clue Counter 37be78.yaml'
- !include 'unpacked/Custom_Model Clue Counter 1769ed.yaml'
@ -426,8 +432,7 @@ ObjectStates:
- !include 'unpacked/Custom_Token Master Clue Counter 4a3aa4.yaml'
- !include 'unpacked/Custom_Model_Bag Legacy Assets 7165a9.yaml'
- !include 'unpacked/Custom_Token Play Area 721ba2.yaml'
- !include 'unpacked/Bag Additional Player Cards 2cba6b.yaml'
- !include 'unpacked/Custom_Assetbundle_Bag Barkham Horror 308439.yaml'
- !include 'unpacked/Custom_Model_Bag Additional Player Cards 2cba6b.yaml'
- !include 'unpacked/Custom_Token Chaos Bag Stat Tracker 766620.yaml'
- !include 'unpacked/Checker_white Token Spawn Tool 36b4ee.yaml'
- !include 'unpacked/Custom_Model_Bag Standalone Scenarios 77a5f9.yaml'
@ -448,8 +453,6 @@ ObjectStates:
- !include 'unpacked/Custom_Model_Bag Chaos Bag fea079.yaml'
- !include 'unpacked/Custom_Tile Data Helper 708279.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 The Dream-Eaters a16a1a.yaml'
- !include 'unpacked/Custom_Model The Feast of Hemlock Vale c740af.yaml'
@ -459,27 +462,11 @@ ObjectStates:
- !include 'unpacked/Custom_Tile Playermat 2 Orange bd0ff4.yaml'
- !include 'unpacked/Custom_Tile Playermat 3 Green 383d8b.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_Tile ArkhamDB Deck Importer a28140.yaml'
- !include 'unpacked/Checker_white Configuration 03804b.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/Custom_Token Investigator Skill Tracker af7ed7.yaml'
- !include 'unpacked/Custom_Token Investigator Skill Tracker e598c2.yaml'
@ -500,7 +487,6 @@ ObjectStates:
- !include 'unpacked/Custom_Tile Token Remover 2ba7a5.yaml'
- !include 'unpacked/Custom_Tile Token Remover 0a5a29.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 975c39.yaml'
- !include 'unpacked/BlockRectangle Table Divider 75937e.yaml'
@ -509,10 +495,10 @@ ObjectStates:
- !include 'unpacked/Custom_Model Decoration - Ammo b43845.yaml'
- !include 'unpacked/Custom_Model Decoration - Ammo d35ee9.yaml'
- !include 'unpacked/Custom_Token Victory Display 6ccd6d.yaml'
- !include 'unpacked/Custom_Tile Campaign Overview e03c01.yaml'
- !include 'unpacked/Custom_Tile Official Releases Overview 8e22bb.yaml'
- !include 'unpacked/Custom_Tile Fan-Made Expansion Overview de7cae.yaml'
- !include 'unpacked/Bag OptionPanel Source 830bd0.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/3DText d628cc.yaml'
- !include 'unpacked/go_game_piece_black Navigation Overlay Handler 797ede.yaml'
@ -520,7 +506,7 @@ ObjectStates:
- !include 'unpacked/Custom_Token Token Arranger 022907.yaml'
- !include 'unpacked/Custom_Tile Chaos Bag Manager 023240.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_0 0a8592.yaml'
- !include 'unpacked/Custom_Tile Tokencache_-1 b644d2.yaml'
@ -541,6 +527,18 @@ ObjectStates:
- !include 'unpacked/Custom_Tile Tokencache_Curse 16a9a7.yaml'
- !include 'unpacked/Custom_Tile Tokencache_Frost b2b7be.yaml'
- !include 'unpacked/BlockSquare Physics Detector b300d8.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
PlayerCounts:
- 0
@ -548,7 +546,7 @@ PlayerCounts:
PlayingTime:
- 0
- 0
SaveName: Arkham SCE - 3.7.0
SaveName: Arkham SCE - 3.9.0
Sky: Sky_Museum
SkyURL: https://i.imgur.com/GkQqaOF.jpg
SnapPoints:
@ -736,182 +734,80 @@ SnapPoints:
x: -26
y: 1.48
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:
'10':
body: "Created by Whimsical\n\nAnything that passes over the remover that isn't
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
to your play area first."
'1':
body: 'Welcome to Arkham Horror LCG - Super Complete Edition!
Make sure to take the tour that can be started with the token in the middle
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
id: 10
title: Token Remover
id: 1
title: Basic Intro
visibleColor:
b: 0.5
g: 0.5
r: 0.5
'11':
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':
'2':
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,
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
their own hand.
color: Grey
id: 7
id: 2
title: How to Hide Hands
visibleColor:
b: 0.5
g: 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
Tags: []
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")
end)
__bundle_register("playercards/AllCardsBag", function(require, _LOADED, __bundle_register, __bundle_modules)
local cardIdIndex = { }
local classAndLevelIndex = { }
local basicWeaknessList = { }
local uniqueWeaknessList = { }
local cycleIndex = { }
local guidReferenceApi = require("core/GUIDReferenceApi")
local cardIdIndex = {}
local classAndLevelIndex = {}
local basicWeaknessList = {}
local uniqueWeaknessList = {}
local cycleIndex = {}
local indexingDone = false
local otherCardsDetected = false
function onLoad()
self.addContextMenuItem("Rebuild Index", startIndexBuild)
@ -59,13 +62,13 @@ function onLoad()
Wait.frames(startIndexBuild, 30)
end
-- Called by Hotfix bags when they load. If we are still loading indexes, then
-- Called by Hotfix bags when they load. If we are still loading indexes, then
-- the all cards and hotfix bags are being loaded together, and we can ignore
-- this call as the hotfix will be included in the initial indexing. If it is
-- this call as the hotfix will be included in the initial indexing. If it is
-- 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.
function rebuildIndexForHotfix()
if (indexingDone) then
if indexingDone then
startIndexBuild()
end
end
@ -73,23 +76,23 @@ end
-- Resets all current bag indexes
function clearIndexes()
indexingDone = false
cardIdIndex = { }
classAndLevelIndex = { }
classAndLevelIndex["Guardian-upgrade"] = { }
classAndLevelIndex["Seeker-upgrade"] = { }
classAndLevelIndex["Mystic-upgrade"] = { }
classAndLevelIndex["Survivor-upgrade"] = { }
classAndLevelIndex["Rogue-upgrade"] = { }
classAndLevelIndex["Neutral-upgrade"] = { }
classAndLevelIndex["Guardian-level0"] = { }
classAndLevelIndex["Seeker-level0"] = { }
classAndLevelIndex["Mystic-level0"] = { }
classAndLevelIndex["Survivor-level0"] = { }
classAndLevelIndex["Rogue-level0"] = { }
classAndLevelIndex["Neutral-level0"] = { }
cycleIndex = { }
basicWeaknessList = { }
uniqueWeaknessList = { }
cardIdIndex = {}
classAndLevelIndex = {}
classAndLevelIndex["Guardian-upgrade"] = {}
classAndLevelIndex["Seeker-upgrade"] = {}
classAndLevelIndex["Mystic-upgrade"] = {}
classAndLevelIndex["Survivor-upgrade"] = {}
classAndLevelIndex["Rogue-upgrade"] = {}
classAndLevelIndex["Neutral-upgrade"] = {}
classAndLevelIndex["Guardian-level0"] = {}
classAndLevelIndex["Seeker-level0"] = {}
classAndLevelIndex["Mystic-level0"] = {}
classAndLevelIndex["Survivor-level0"] = {}
classAndLevelIndex["Rogue-level0"] = {}
classAndLevelIndex["Neutral-level0"] = {}
cycleIndex = {}
basicWeaknessList = {}
uniqueWeaknessList = {}
end
-- Clears the bag indexes and starts the coroutine to rebuild the indexes
@ -104,287 +107,372 @@ function onObjectLeaveContainer(container, _)
end
end
-- Create the card indexes by iterating all cards in the bag, parsing their
-- metadata, and creating the keyed lookup tables for the cards. This is a
-- coroutine which will spread the workload by processing 20 cards before
-- yielding. Based on the current count of cards this will require
-- approximately 60 frames to complete.
-- Create the card indexes by iterating all cards in the bag, parsing their metadata
-- and creating the keyed lookup tables for the cards. This is a coroutine which will
-- spread the workload by processing 20 cards before yielding.
function buildIndex()
local cardCount = 0
indexingDone = false
if (self.getData().ContainedObjects == nil) then
return 1
end
for i, cardData in ipairs(self.getData().ContainedObjects) do
local cardMetadata = JSON.decode(cardData.GMNotes)
if (cardMetadata ~= nil) then
addCardToIndex(cardData, cardMetadata)
cardCount = cardCount + 1
if cardCount > 9 then
cardCount = 0
coroutine.yield(0)
end
otherCardsDetected = false
-- process the allcardsbag itself
for _, cardData in ipairs(self.getData().ContainedObjects) do
addCardToIndex(cardData)
cardCount = cardCount + 1
if cardCount > 19 then
cardCount = 0
coroutine.yield(0)
end
end
local hotfixBags = getObjectsWithTag("AllCardsHotfix")
for _, hotfixBag in ipairs(hotfixBags) do
if (#hotfixBag.getObjects() > 0) then
for i, cardData in ipairs(hotfixBag.getData().ContainedObjects) do
if cardData.ContainedObjects then
for j, deepCardData in ipairs(cardData.ContainedObjects) do
local deepCardMetadata = JSON.decode(deepCardData.GMNotes)
if deepCardMetadata ~= nil then
addCardToIndex(deepCardData, deepCardMetadata)
cardCount = cardCount + 1
if cardCount > 9 then
cardCount = 0
coroutine.yield(0)
end
end
end
else
local cardMetadata = JSON.decode(cardData.GMNotes)
if cardMetadata ~= nil then
addCardToIndex(cardData, cardMetadata)
cardCount = cardCount + 1
if cardCount > 9 then
cardCount = 0
coroutine.yield(0)
end
-- process hotfix bags (and the additional playercards bag)
for _, hotfixBag in ipairs(getObjectsWithTag("AllCardsHotfix")) do
local hotfixData = hotfixBag.getData()
-- 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
if cardData.ContainedObjects then
-- process containers
for _, deepCardData in ipairs(cardData.ContainedObjects) do
addCardToIndex(deepCardData)
cardCount = cardCount + 1
if cardCount > 19 then
cardCount = 0
coroutine.yield(0)
end
end
else
-- process single cards
addCardToIndex(cardData)
cardCount = cardCount + 1
if cardCount > 19 then
cardCount = 0
coroutine.yield(0)
end
end
end
::nextBag::
end
buildSupplementalIndexes()
updatePlayerCardPanel()
indexingDone = true
return 1
end
-- Adds a card to any indexes it should be a part of, based on its metadata.
-- Adds a card to any indexes it should be a part of, based on its metadata
---@param cardData table TTS object data for the card
---@param cardMetadata table SCED metadata for the card
function addCardToIndex(cardData, cardMetadata)
-- use the ZoopGuid as fallback if no id present
if cardMetadata.id == nil and cardMetadata.TtsZoopGuid then
cardMetadata.id = cardMetadata.TtsZoopGuid
function addCardToIndex(cardData)
-- using the more efficient 'json.parse()' to speed this process up
local status, cardMetadata = pcall(function() return json.parse(cardData.GMNotes) 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
cardMetadata.id = cardMetadata.id or cardMetadata.TtsZoopGuid
cardIdIndex[cardMetadata.id] = { data = cardData, metadata = cardMetadata }
if (cardMetadata.alternate_ids ~= nil) then
-- also add data for alternate ids
if cardMetadata.alternate_ids ~= nil then
for _, alternateId in ipairs(cardMetadata.alternate_ids) do
cardIdIndex[alternateId] = { data = cardData, metadata = cardMetadata }
end
end
end
-- Creates the supplemental indexes for classes, weaknesses etc.
function buildSupplementalIndexes()
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 cardId == cardMetadata.id then
-- Add card to the basic weakness list, if appropriate. Some weaknesses have
-- multiple copies, and are added multiple times
if cardMetadata.weakness then
table.insert(uniqueWeaknessList, cardMetadata.id)
if cardMetadata.basicWeaknessCount ~= nil then
for i = 1, cardMetadata.basicWeaknessCount do
table.insert(basicWeaknessList, cardMetadata.id)
-- 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 == card.metadata.id then
-- Add card to the basic weakness list, if appropriate. Some weaknesses have multiple copies, and are added multiple times
if card.metadata.weakness then
table.insert(uniqueWeaknessList, card.metadata.id)
if card.metadata.basicWeaknessCount ~= nil then
for i = 1, card.metadata.basicWeaknessCount do
table.insert(basicWeaknessList, card.metadata.id)
end
end
end
-- Add the card to the appropriate class and level indexes
local isGuardian = false
local isSeeker = false
local isMystic = false
local isRogue = false
local isSurvivor = false
local isNeutral = false
local upgradeKey
-- Excludes signature cards (which have no class or level) and alternate
-- ID entries
if (cardMetadata.class ~= nil and cardMetadata.level ~= nil) then
isGuardian = string.match(cardMetadata.class, "Guardian")
isSeeker = string.match(cardMetadata.class, "Seeker")
isMystic = string.match(cardMetadata.class, "Mystic")
isRogue = string.match(cardMetadata.class, "Rogue")
isSurvivor = string.match(cardMetadata.class, "Survivor")
isNeutral = string.match(cardMetadata.class, "Neutral")
if (cardMetadata.level > 0) then
-- Excludes signature cards (which have no class or level)
if card.metadata.class ~= nil and card.metadata.level ~= nil then
local upgradeKey = "-level0"
if card.metadata.level > 0 then
upgradeKey = "-upgrade"
else
upgradeKey = "-level0"
end
if (isGuardian) then
table.insert(classAndLevelIndex["Guardian"..upgradeKey], cardMetadata.id)
end
if (isSeeker) then
table.insert(classAndLevelIndex["Seeker"..upgradeKey], cardMetadata.id)
end
if (isMystic) then
table.insert(classAndLevelIndex["Mystic"..upgradeKey], cardMetadata.id)
end
if (isRogue) then
table.insert(classAndLevelIndex["Rogue"..upgradeKey], cardMetadata.id)
end
if (isSurvivor) then
table.insert(classAndLevelIndex["Survivor"..upgradeKey], cardMetadata.id)
end
if (isNeutral) then
table.insert(classAndLevelIndex["Neutral"..upgradeKey], cardMetadata.id)
end
local cycleName = cardMetadata.cycle
-- parse classes (separated by "|") and add the card to the appropriate class and level indices
for str in card.metadata.class:gmatch("([^|]+)") do
table.insert(classAndLevelIndex[str .. upgradeKey], card.metadata.id)
end
-- add to cycle index
local cycleName = card.metadata.cycle
if cycleName ~= nil then
cycleName = string.lower(cycleName)
if string.match(cycleName, "return") then
cycleName = string.sub(cycleName, 11)
end
if cycleName == "the night of the zealot" then
cycleName = "core"
end
if cycleIndex[cycleName] == nil then
cycleIndex[cycleName] = { }
end
table.insert(cycleIndex[cycleName], cardMetadata.id)
-- remove "return to " from cycle names
cycleName = cycleName:gsub("return to ", "")
-- override cycle name for night of the zealot
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
cycleIndex[cycleName] = {}
end
table.insert(cycleIndex[cycleName], card.metadata.id)
end
end
end
-- sort class and level indices
for _, indexTable in pairs(classAndLevelIndex) do
table.sort(indexTable, cardComparator)
end
-- sort cycle indices
for _, indexTable in pairs(cycleIndex) do
table.sort(indexTable)
end
-- sort weakness indices
table.sort(basicWeaknessList, cardComparator)
table.sort(uniqueWeaknessList, cardComparator)
end
-- Comparison function used to sort the class card bag indexes. Sorts by card
-- level, then name, then subname.
-- Comparison function used to sort the class card bag indexes. Sorts by card level, then name, then subname.
function cardComparator(id1, id2)
local card1 = cardIdIndex[id1]
local card2 = cardIdIndex[id2]
if (card1.metadata.level ~= card2.metadata.level) then
if card1.metadata.level ~= card2.metadata.level then
return card1.metadata.level < card2.metadata.level
end
if (card1.data.Nickname ~= card2.data.Nickname) then
elseif card1.data.Nickname ~= card2.data.Nickname then
return card1.data.Nickname < card2.data.Nickname
else
return card1.data.Description < card2.data.Description
end
return card1.data.Description < card2.data.Description
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()
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
end
-- Returns a specific card from the bag, based on ArkhamDB ID
-- Params table:
-- id: String ID of the card to retrieve
-- Return: If the indexes are still being constructed, an empty table is
-- returned. Otherwise, a single table with the following fields
-- cardData: TTS object data, suitable for spawning the card
-- cardMetadata: Table of parsed metadata
---@param params table ID of the card to retrieve
---@return table: If the indexes are still being constructed, returns an empty table.
-- Otherwise, a single table with the following fields
-- data: TTS object data, suitable for spawning the card
-- metadata: Table of parsed metadata
function getCardById(params)
if (not indexingDone) then
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return { }
end
if not isIndexReady() then return {} end
return cardIdIndex[params.id]
end
-- Returns a list of cards from the bag matching a class and level (0 or upgraded)
-- Params table:
-- class: String class to retrieve ("Guardian", "Seeker", etc)
-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0
-- Return: If the indexes are still being constructed, returns an empty table.
-- Otherwise, a list of tables, each with the following fields
-- cardData: TTS object data, suitable for spawning the card
-- cardMetadata: Table of parsed metadata
---@param params table
-- class: String class to retrieve ("Guardian", "Seeker", etc)
-- isUpgraded: true for upgraded cards (Level 1-5), false for Level 0
---@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 getCardsByClassAndLevel(params)
if (not indexingDone) then
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return { }
end
local upgradeKey
if (params.upgraded) then
if not isIndexReady() then return {} end
local upgradeKey = "-level0"
if params.upgraded then
upgradeKey = "-upgrade"
end
return classAndLevelIndex[params.class .. upgradeKey]
end
-- Returns a list of cards from the bag matching a cycle
---@param params table
-- cycle: String cycle to retrieve ("The Scarlet Keys" etc.)
-- 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
-- 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
upgradeKey = "-level0"
return card1.data.Description < card2.data.Description
end
return classAndLevelIndex[params.class..upgradeKey];
end
function getCardsByCycle(cycleName)
if (not indexingDone) then
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
return { }
-- 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 cycleIndex[string.lower(cycleName)]
return classValue
end
-- 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.
-- 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.
-- Parameter array must contain these fields to define the search:
-- name String or string fragment to search for names
-- exact Whether the name match should be exact
-- name: String or string fragment to search for names
-- exact: Whether the name match should be exact
function getCardsByName(params)
local name = params.name
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
local addedCards = { }
local addedCards = {}
for _, cardData in pairs(cardIdIndex) do
if (not addedCards[cardData.metadata.id]) then
if (exact and (string.lower(cardData.data.Nickname) == string.lower(name)))
or (not exact and string.find(string.lower(cardData.data.Nickname), string.lower(name), 1, true)) then
table.insert(results, cardData)
addedCards[cardData.metadata.id] = true
table.insert(results, cardData)
addedCards[cardData.metadata.id] = true
end
end
end
return results
end
-- Gets a random basic weakness from the bag. Once a given ID has been returned
-- it will be removed from the list and cannot be selected again until a reload
-- occurs or the indexes are rebuilt, which will refresh the list to include all
-- weaknesses.
-- Return: String ID of the selected weakness.
-- Gets a random basic weakness from the bag. Once a given ID has been returned it will be
-- removed from the list and cannot be selected again until a reload occurs or the indexes
-- are rebuilt, which will refresh the list to include all weaknesses.
---@return string: ID of the selected weakness
function getRandomWeaknessId()
local availableWeaknesses = buildAvailableWeaknesses()
if (#availableWeaknesses > 0) then
local availableWeaknesses = buildAvailableWeaknesses()
if #availableWeaknesses > 0 then
return availableWeaknesses[math.random(#availableWeaknesses)]
end
end
-- 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
-- Return: Table array of weakness IDs which are valid to choose from
function buildAvailableWeaknesses()
local weaknessesInPlay = { }
---@param traits? string Trait(s) to use as filter
---@return table: Array of weakness IDs which are valid to choose from
function buildAvailableWeaknesses(traits)
local weaknessesInPlay = {}
local allObjects = getAllObjects()
for _, object in ipairs(allObjects) do
if (object.name == "Deck") then
if object.type == "Deck" then
for _, cardData in ipairs(object.getData().ContainedObjects) do
local cardMetadata = JSON.decode(cardData.GMNotes)
incrementWeaknessCount(weaknessesInPlay, cardMetadata)
incrementWeaknessCount(weaknessesInPlay, JSON.decode(cardData.GMNotes))
end
elseif (object.name == "Card") then
local cardMetadata = JSON.decode(object.getGMNotes())
incrementWeaknessCount(weaknessesInPlay, cardMetadata)
elseif object.type == "Card" then
incrementWeaknessCount(weaknessesInPlay, JSON.decode(object.getGMNotes()))
end
end
local availableWeaknesses = { }
local availableWeaknesses = {}
for _, weaknessId in ipairs(basicWeaknessList) do
if (weaknessesInPlay[weaknessId] ~= nil and weaknessesInPlay[weaknessId] > 0) then
weaknessesInPlay[weaknessId] = weaknessesInPlay[weaknessId] - 1
else
table.insert(availableWeaknesses, weaknessId)
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)
break
end
end
else
table.insert(availableWeaknesses, weaknessId)
end
end
end
return availableWeaknesses
@ -400,8 +488,8 @@ end
-- Helper function that adds one to the table entry for the number of weaknesses in play
function incrementWeaknessCount(table, cardMetadata)
if (isBasicWeakness(cardMetadata)) then
if (table[cardMetadata.id] == nil) then
if isBasicWeakness(cardMetadata) then
if table[cardMetadata.id] == nil then
table[cardMetadata.id] = 1
else
table[cardMetadata.id] = table[cardMetadata.id] + 1
@ -411,9 +499,61 @@ end
function isBasicWeakness(cardMetadata)
return cardMetadata ~= nil
and cardMetadata.weakness
and cardMetadata.basicWeaknessCount ~= nil
and cardMetadata.basicWeaknessCount > 0
and cardMetadata.weakness
and cardMetadata.basicWeaknessCount ~= nil
and cardMetadata.basicWeaknessCount > 0
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")

View File

@ -16,6 +16,11 @@ ContainedObjects:
- !include 'Bag All Player Cards 15bb07/Card Reality Acid 0a1b3a.yaml'
- !include 'Bag All Player Cards 15bb07/Card Reality Acid Reference 858b0a.yaml'
- !include 'Bag All Player Cards 15bb07/Card Ravenous 558b0a.yaml'
- !include 'Bag All Player Cards 15bb07/CardCustom Rex Murphy (Parallel) 0a5492.yaml'
- !include 'Bag All Player Cards 15bb07/CardCustom Rex Murphy (Parallel Back) 0a5493.yaml'