diff --git a/config.json b/config.json index 45015cf7..741b5115 100644 --- a/config.json +++ b/config.json @@ -19,6 +19,7 @@ "MusicPlayer_path": "MusicPlayer.json", "Note": "", "ObjectStates_order": [ + "GUIDReferenceHandler.123456", "HandTrigger.5fe087", "HandTrigger.be2f17", "HandTrigger.0285cc", @@ -101,7 +102,7 @@ "ClueCounter.d86b7c", "MasterClueCounter.4a3aa4", "LegacyAssets.7165a9", - "Playarea.721ba2", + "PlayArea.721ba2", "BarkhamHorror.308439", "ChaosBagStatTracker.766620", "Blesstokens.afa06b", @@ -131,11 +132,11 @@ "EdgeoftheEarth.895eaa", "TheDream-Eaters.a16a1a", "ReturntoTheCircleUndone.757324", - "Playermat4Red.0840d5", - "Playermat3Green.383d8b", "OtherDoominPlay.652ff3", "Playermat1White.8b081b", "Playermat2Orange.bd0ff4", + "Playermat3Green.383d8b", + "Playermat4Red.0840d5", "Neutral.2691e1", "Neutral.748245", "Neutral.271b17", @@ -198,6 +199,7 @@ "TokenArranger.022907", "ChaosBagManager.023240", "ArkhamSCE330-1092023-Page1.964222", + "PlaceholderBoxDummy.a93466", "SoulsofDarkness.a94e6b" ], "PlayArea": 1, @@ -218,5 +220,5 @@ "Tags": [], "Turns_path": "Turns.json", "VersionNumber": "v13.2.2", - "XmlUI": "\u003cInclude src=\"Global.xml\"/\u003e" + "XmlUI": "\u003cInclude src=\"Global/Global.xml\"/\u003e" } diff --git a/modsettings/ComponentTags.json b/modsettings/ComponentTags.json index 8d28dfae..df092f6f 100644 --- a/modsettings/ComponentTags.json +++ b/modsettings/ComponentTags.json @@ -12,10 +12,6 @@ "displayed": "LinkedPhaseTracker", "normalized": "linkedphasetracker" }, - { - "displayed": "chaosBag", - "normalized": "chaosBag" - }, { "displayed": "displacement_excluded", "normalized": "displacement_excluded" @@ -60,10 +56,6 @@ "displayed": "chaosBag", "normalized": "chaosbag" }, - { - "displayed": "arkham_setup_memory_object", - "normalized": "arkham_setup_memory_object" - }, { "displayed": "ActionToken", "normalized": "actiontoken" @@ -72,10 +64,6 @@ "displayed": "LargeBox", "normalized": "largebox" }, - { - "displayed": "SoundCube", - "normalized": "soundcube" - }, { "displayed": "CampaignBox", "normalized": "campaignbox" @@ -83,10 +71,6 @@ { "displayed": "CameraZoom_ignore", "normalized": "camerazoom_ignore" - }, - { - "displayed": "TokenArranger", - "normalized": "tokenarranger" } ] } diff --git a/modsettings/CustomUIAssets.json b/modsettings/CustomUIAssets.json index 402f57df..3a808204 100644 --- a/modsettings/CustomUIAssets.json +++ b/modsettings/CustomUIAssets.json @@ -218,5 +218,20 @@ "Name": "FinnIcon", "Type": 0, "URL": "http://cloud-3.steamusercontent.com/ugc/2037357792052848566/5DA900C430E97D3DFF2C9B8A3DB1CB2271791FC7/" + }, + { + "Name": "box-cover-mask-small", + "Type": 0, + "URL": "http://cloud-3.steamusercontent.com/ugc/2115061298536631564/F29C2ED9DD8431A1D1E21C7FFAFF1FFBC0AF0BF3/" + }, + { + "Name": "box-cover-mask-big", + "Type": 0, + "URL": "http://cloud-3.steamusercontent.com/ugc/2115061298536631429/D075D2EECE6EE091AD3BEA5800DEF9C7B02B745B/" + }, + { + "Name": "box-cover-mask-wide", + "Type": 0, + "URL": "http://cloud-3.steamusercontent.com/ugc/2115061298538827369/A20C2ECB8ECDC1B0AD8B2B38F68CA1C1F5E07D37/" } ] diff --git a/objects/AllPlayerCards.15bb07.json b/objects/AllPlayerCards.15bb07.json index f0715db7..8bfd51e7 100644 --- a/objects/AllPlayerCards.15bb07.json +++ b/objects/AllPlayerCards.15bb07.json @@ -1557,7 +1557,14 @@ "StallforTime.7b6ed1", "WrongPlaceRightTime.d5944e", "SparrowMask.975d79", - "Pitchfork.45a724" + "Pitchfork.45a724", + "JimsTrumpet.7dfd5f", + "JimCulverParallel.72bf31", + "JimCulverParallelFront.c5fc80", + "VengefulShade.73bc8e", + "FinalRhapsody.561775", + "JimCulverParallelBack.aba863", + "TheBeyond.37ab47" ], "ContainedObjects_path": "AllPlayerCards.15bb07", "Description": "", diff --git a/objects/AllPlayerCards.15bb07/FinalRhapsody.561775.gmnotes b/objects/AllPlayerCards.15bb07/FinalRhapsody.561775.gmnotes new file mode 100644 index 00000000..2de709b7 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/FinalRhapsody.561775.gmnotes @@ -0,0 +1,8 @@ +{ + "id": "90051", + "type": "Treachery", + "class": "Neutral", + "traits": "Endtimes.", + "weakness": true, + "cycle": "The Dunwich Legacy" +} diff --git a/objects/AllPlayerCards.15bb07/FinalRhapsody.561775.json b/objects/AllPlayerCards.15bb07/FinalRhapsody.561775.json new file mode 100644 index 00000000..7a278bf8 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/FinalRhapsody.561775.json @@ -0,0 +1,61 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 847001, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8470": { + "BackIsHidden": false, + "BackURL": "https://i.imgur.com/EcbhVuh.jpg/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/2149964195986880793/517FBB4FF8F72900B9E123DB865BCAD625F6506C/", + "NumHeight": 2, + "NumWidth": 2, + "Type": 0, + "UniqueBack": false + } + }, + "Description": "Advanced", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/FinalRhapsody.561775.gmnotes", + "GUID": "561775", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": true, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Card", + "Nickname": "Final Rhapsody", + "SidewaysCard": false, + "Snap": true, + "Sticky": true, + "Tags": [ + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 78.419, + "posY": 3.193, + "posZ": 23.541, + "rotX": 0, + "rotY": 270, + "rotZ": 0, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/JimCulverParallel.72bf31.gmnotes b/objects/AllPlayerCards.15bb07/JimCulverParallel.72bf31.gmnotes new file mode 100644 index 00000000..c036d5ae --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimCulverParallel.72bf31.gmnotes @@ -0,0 +1,11 @@ +{ + "id": "02004-p", + "type": "Investigator", + "class": "Mystic", + "traits": "Performer. Cursed.", + "willpowerIcons": 4, + "intellectIcons": 3, + "combatIcons": 3, + "agilityIcons": 2, + "cycle": "The Dunwich Legacy" +} diff --git a/objects/AllPlayerCards.15bb07/JimCulverParallel.72bf31.json b/objects/AllPlayerCards.15bb07/JimCulverParallel.72bf31.json new file mode 100644 index 00000000..f4c4e241 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimCulverParallel.72bf31.json @@ -0,0 +1,62 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 846700, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8467": { + "BackIsHidden": false, + "BackURL": "http://cloud-3.steamusercontent.com/ugc/2149964195987018702/54C63785F3AA474F635F58BC506C86A318432BD7/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/2149964195987018793/0AED4BF62C4FF3206778AD36FDB9C8E482CD3F9E/", + "NumHeight": 2, + "NumWidth": 4, + "Type": 0, + "UniqueBack": true + } + }, + "Description": "The Musician", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/JimCulverParallel.72bf31.gmnotes", + "GUID": "72bf31", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": false, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Card", + "Nickname": "Jim Culver (Parallel)", + "SidewaysCard": true, + "Snap": true, + "Sticky": true, + "Tags": [ + "Investigator", + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 82.182, + "posY": 3.193, + "posZ": 26.386, + "rotX": 0, + "rotY": 180, + "rotZ": 0, + "scaleX": 1.15, + "scaleY": 1, + "scaleZ": 1.15 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/JimCulverParallelBack.aba863.gmnotes b/objects/AllPlayerCards.15bb07/JimCulverParallelBack.aba863.gmnotes new file mode 100644 index 00000000..deb5956d --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimCulverParallelBack.aba863.gmnotes @@ -0,0 +1,11 @@ +{ + "id": "02004-pb", + "type": "Investigator", + "class": "Mystic", + "traits": "Performer.", + "willpowerIcons": 4, + "intellectIcons": 3, + "combatIcons": 3, + "agilityIcons": 2, + "cycle": "The Dunwich Legacy" +} diff --git a/objects/AllPlayerCards.15bb07/JimCulverParallelBack.aba863.json b/objects/AllPlayerCards.15bb07/JimCulverParallelBack.aba863.json new file mode 100644 index 00000000..a889c972 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimCulverParallelBack.aba863.json @@ -0,0 +1,62 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 846805, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8468": { + "BackIsHidden": false, + "BackURL": "http://cloud-3.steamusercontent.com/ugc/2149964195987018702/54C63785F3AA474F635F58BC506C86A318432BD7/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/1656727981627737050/3CFF9E3825033909543AD1CF843361D9243538EE/", + "NumHeight": 2, + "NumWidth": 4, + "Type": 0, + "UniqueBack": true + } + }, + "Description": "The Musician", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/JimCulverParallelBack.aba863.gmnotes", + "GUID": "aba863", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": false, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Card", + "Nickname": "Jim Culver (Parallel Back)", + "SidewaysCard": true, + "Snap": true, + "Sticky": true, + "Tags": [ + "Investigator", + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 82.206, + "posY": 3.193, + "posZ": 18.459, + "rotX": 0, + "rotY": 180, + "rotZ": 0, + "scaleX": 1.15, + "scaleY": 1, + "scaleZ": 1.15 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.gmnotes b/objects/AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.gmnotes new file mode 100644 index 00000000..0ef4a3db --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.gmnotes @@ -0,0 +1,11 @@ +{ + "id": "02004-pf", + "type": "Investigator", + "class": "Mystic", + "traits": "Performer. Cursed.", + "willpowerIcons": 4, + "intellectIcons": 3, + "combatIcons": 3, + "agilityIcons": 2, + "cycle": "The Dunwich Legacy" +} diff --git a/objects/AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.json b/objects/AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.json new file mode 100644 index 00000000..42900417 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.json @@ -0,0 +1,62 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 846905, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8469": { + "BackIsHidden": false, + "BackURL": "http://cloud-3.steamusercontent.com/ugc/1656727981627737648/F371339538812F68E38AAC0D520C525250DAC5C0/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/2149964195987018793/0AED4BF62C4FF3206778AD36FDB9C8E482CD3F9E/", + "NumHeight": 2, + "NumWidth": 4, + "Type": 0, + "UniqueBack": true + } + }, + "Description": "The Musician", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/JimCulverParallelFront.c5fc80.gmnotes", + "GUID": "c5fc80", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": false, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Card", + "Nickname": "Jim Culver (Parallel Front)", + "SidewaysCard": true, + "Snap": true, + "Sticky": true, + "Tags": [ + "Investigator", + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 82.087, + "posY": 3.193, + "posZ": 22.484, + "rotX": 0, + "rotY": 180, + "rotZ": 0, + "scaleX": 1.15, + "scaleY": 1, + "scaleZ": 1.15 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/JimsTrumpet.03c6a7.json b/objects/AllPlayerCards.15bb07/JimsTrumpet.03c6a7.json index 87b03ade..4d0078bf 100644 --- a/objects/AllPlayerCards.15bb07/JimsTrumpet.03c6a7.json +++ b/objects/AllPlayerCards.15bb07/JimsTrumpet.03c6a7.json @@ -22,7 +22,7 @@ "UniqueBack": false } }, - "Description": "", + "Description": "The Dead Listen", "DragSelectable": true, "GMNotes_path": "AllPlayerCards.15bb07/JimsTrumpet.03c6a7.gmnotes", "GUID": "03c6a7", diff --git a/objects/AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.gmnotes b/objects/AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.gmnotes new file mode 100644 index 00000000..9d01e91c --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.gmnotes @@ -0,0 +1,10 @@ +{ + "id": "90050", + "type": "Asset", + "class": "Neutral", + "cost": 2, + "traits": "Item. Instrument. Relic.", + "willpowerIcons": 2, + "wildIcons": 2, + "cycle": "The Dunwich Legacy" +} diff --git a/objects/AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.json b/objects/AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.json new file mode 100644 index 00000000..67d9855d --- /dev/null +++ b/objects/AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.json @@ -0,0 +1,62 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 847000, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8470": { + "BackIsHidden": false, + "BackURL": "https://i.imgur.com/EcbhVuh.jpg/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/2149964195986880793/517FBB4FF8F72900B9E123DB865BCAD625F6506C/", + "NumHeight": 2, + "NumWidth": 2, + "Type": 0, + "UniqueBack": false + } + }, + "Description": "The Dead Speak (Advanced)", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/JimsTrumpet.7dfd5f.gmnotes", + "GUID": "7dfd5f", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": true, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Card", + "Nickname": "Jim's Trumpet", + "SidewaysCard": false, + "Snap": true, + "Sticky": true, + "Tags": [ + "Asset", + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 78.511, + "posY": 3.229, + "posZ": 27.011, + "rotX": 359, + "rotY": 270, + "rotZ": 0, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/TheBeyond.37ab47.gmnotes b/objects/AllPlayerCards.15bb07/TheBeyond.37ab47.gmnotes new file mode 100644 index 00000000..9a48acb1 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/TheBeyond.37ab47.gmnotes @@ -0,0 +1,8 @@ +{ + "id": "90052", + "type": "Asset", + "class": "Neutral", + "permanent": true, + "startsInPlay": true, + "cycle": "Laid to Rest" +} diff --git a/objects/AllPlayerCards.15bb07/TheBeyond.37ab47.json b/objects/AllPlayerCards.15bb07/TheBeyond.37ab47.json new file mode 100644 index 00000000..0f148737 --- /dev/null +++ b/objects/AllPlayerCards.15bb07/TheBeyond.37ab47.json @@ -0,0 +1,62 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 847100, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8471": { + "BackIsHidden": true, + "BackURL": "http://cloud-3.steamusercontent.com/ugc/2149964195986881059/864F6EBBD2900ED6D145B8AF12F2F8C180375C83/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/2149964195986880989/13172BC914A0D729B4EFD9845FA9FDFCAB6F77F7/", + "NumHeight": 1, + "NumWidth": 1, + "Type": 0, + "UniqueBack": false + } + }, + "Description": "Bleak Netherworld", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/TheBeyond.37ab47.gmnotes", + "GUID": "37ab47", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": true, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "CardCustom", + "Nickname": "The Beyond", + "SidewaysCard": false, + "Snap": true, + "Sticky": true, + "Tags": [ + "Asset", + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 78.532, + "posY": 3.193, + "posZ": 17.888, + "rotX": 0, + "rotY": 270, + "rotZ": 0, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/VengefulShade.73bc8e.gmnotes b/objects/AllPlayerCards.15bb07/VengefulShade.73bc8e.gmnotes new file mode 100644 index 00000000..dfe8ea6e --- /dev/null +++ b/objects/AllPlayerCards.15bb07/VengefulShade.73bc8e.gmnotes @@ -0,0 +1,8 @@ +{ + "id": "90053", + "type": "Enemy", + "class": "Neutral", + "traits": "Monster. Geist", + "weakness": true, + "cycle": "Laid to Rest" +} diff --git a/objects/AllPlayerCards.15bb07/VengefulShade.73bc8e.json b/objects/AllPlayerCards.15bb07/VengefulShade.73bc8e.json new file mode 100644 index 00000000..6c08b1dc --- /dev/null +++ b/objects/AllPlayerCards.15bb07/VengefulShade.73bc8e.json @@ -0,0 +1,61 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "CardID": 847002, + "ColorDiffuse": { + "b": 0.71324, + "g": 0.71324, + "r": 0.71324 + }, + "CustomDeck": { + "8470": { + "BackIsHidden": false, + "BackURL": "https://i.imgur.com/EcbhVuh.jpg/", + "FaceURL": "http://cloud-3.steamusercontent.com/ugc/2149964195986880793/517FBB4FF8F72900B9E123DB865BCAD625F6506C/", + "NumHeight": 2, + "NumWidth": 2, + "Type": 0, + "UniqueBack": false + } + }, + "Description": "", + "DragSelectable": true, + "GMNotes_path": "AllPlayerCards.15bb07/VengefulShade.73bc8e.gmnotes", + "GUID": "73bc8e", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": true, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Card", + "Nickname": "Vengeful Shade", + "SidewaysCard": false, + "Snap": true, + "Sticky": true, + "Tags": [ + "PlayerCard" + ], + "Tooltip": true, + "Transform": { + "posX": 78.455, + "posY": 3.193, + "posZ": 20.547, + "rotX": 0, + "rotY": 270, + "rotZ": 0, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/AllPlayerCards.15bb07/WendyAdamsParallel.fd91ea.gmnotes b/objects/AllPlayerCards.15bb07/WendyAdamsParallel.fd91ea.gmnotes index 8b5f597e..e830788b 100644 --- a/objects/AllPlayerCards.15bb07/WendyAdamsParallel.fd91ea.gmnotes +++ b/objects/AllPlayerCards.15bb07/WendyAdamsParallel.fd91ea.gmnotes @@ -2,7 +2,7 @@ "id": "01005-p", "type": "Investigator", "class": "Survivor", - "traits": "Drifter.", + "traits": "Drifter. Blessed. Cursed.", "willpowerIcons": 4, "intellectIcons": 3, "combatIcons": 1, diff --git a/objects/AllPlayerCards.15bb07/WendyAdamsParallelFront.61503e.gmnotes b/objects/AllPlayerCards.15bb07/WendyAdamsParallelFront.61503e.gmnotes index 7a646c64..b9c0e161 100644 --- a/objects/AllPlayerCards.15bb07/WendyAdamsParallelFront.61503e.gmnotes +++ b/objects/AllPlayerCards.15bb07/WendyAdamsParallelFront.61503e.gmnotes @@ -2,7 +2,7 @@ "id": "01005-pf", "type": "Investigator", "class": "Survivor", - "traits": "Drifter.", + "traits": "Drifter. Blessed. Cursed.", "willpowerIcons": 4, "intellectIcons": 3, "combatIcons": 1, diff --git a/objects/ArkhamDeckCutter.445115.json b/objects/ArkhamDeckCutter.445115.json index 33d0eb4f..a34b4112 100644 --- a/objects/ArkhamDeckCutter.445115.json +++ b/objects/ArkhamDeckCutter.445115.json @@ -28,9 +28,6 @@ "Nickname": "Arkham Deck Cutter", "Snap": true, "Sticky": true, - "Tags": [ - "arkham_setup_memory_object" - ], "Tooltip": true, "Transform": { "posX": 78, diff --git a/objects/Blesstokens.afa06b.json b/objects/Blesstokens.afa06b.json index c6945406..002460c9 100644 --- a/objects/Blesstokens.afa06b.json +++ b/objects/Blesstokens.afa06b.json @@ -67,6 +67,7 @@ "Snap": true, "Sticky": true, "Tags": [ + "CleanUpHelper_ignore", "displacement_excluded" ], "Tooltip": true, diff --git a/objects/Cluetokens.11e0cf.json b/objects/Cluetokens.11e0cf.json index 81c5e92d..24b017b6 100644 --- a/objects/Cluetokens.11e0cf.json +++ b/objects/Cluetokens.11e0cf.json @@ -44,6 +44,9 @@ "Nickname": "Clue tokens", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": true, "Transform": { "posX": 2.857, diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/BuffytheVampireSlayer.6bf40f.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/BuffytheVampireSlayer.6bf40f.json index b0cf6ef3..fc5a3615 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/BuffytheVampireSlayer.6bf40f.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/BuffytheVampireSlayer.6bf40f.json @@ -83,4 +83,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CartoonInvestigators.524fbc.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CartoonInvestigators.524fbc.json index 99383f67..76b6d05d 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CartoonInvestigators.524fbc.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CartoonInvestigators.524fbc.json @@ -49,6 +49,9 @@ "Nickname": "Cartoon Investigators", "Snap": true, "Sticky": true, + "Tags": [ + "LargeBox" + ], "Tooltip": true, "Transform": { "posX": -23.615, @@ -61,9 +64,6 @@ "scaleY": 0.14, "scaleZ": 1 }, - "Tags": [ - "LargeBox" - ], "Value": 0, "XmlUI": "" } diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CircusExMortisInvestigatorExpansion.195936.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CircusExMortisInvestigatorExpansion.195936.json index a5885da6..18a97fe3 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CircusExMortisInvestigatorExpansion.195936.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CircusExMortisInvestigatorExpansion.195936.json @@ -83,4 +83,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CityofSecrets.1ee775.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CityofSecrets.1ee775.json index b610bb61..85297ebc 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CityofSecrets.1ee775.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/CityofSecrets.1ee775.json @@ -83,4 +83,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheGhostsOfOnigawaInvestigatorExpansion.c19cfa.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheGhostsOfOnigawaInvestigatorExpansion.c19cfa.json index e02967eb..9116ff90 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheGhostsOfOnigawaInvestigatorExpansion.c19cfa.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheGhostsOfOnigawaInvestigatorExpansion.c19cfa.json @@ -49,6 +49,9 @@ "Nickname": "The Ghosts Of Onigawa (Investigator Expansion)", "Snap": true, "Sticky": true, + "Tags": [ + "LargeBox" + ], "Tooltip": true, "Transform": { "posX": -47.192, @@ -61,9 +64,6 @@ "scaleY": 0.14, "scaleZ": 1 }, - "Tags": [ - "LargeBox" - ], "Value": 0, "XmlUI": "" } diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheRedCoterie.de13e3.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheRedCoterie.de13e3.json index 1d5c0d22..cc3c7e26 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheRedCoterie.de13e3.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheRedCoterie.de13e3.json @@ -83,4 +83,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheSandsOfMemphisInvestigatorExpansion.073201.json b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheSandsOfMemphisInvestigatorExpansion.073201.json index 637f83eb..4d2f39b3 100644 --- a/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheSandsOfMemphisInvestigatorExpansion.073201.json +++ b/objects/Community-CreatedPlayerCardsInvestigators.ed4ca7/TheSandsOfMemphisInvestigatorExpansion.073201.json @@ -49,6 +49,9 @@ "Nickname": "The Sands Of Memphis (Investigator Expansion)", "Snap": true, "Sticky": true, + "Tags": [ + "LargeBox" + ], "Tooltip": true, "Transform": { "posX": -47.192, @@ -61,9 +64,6 @@ "scaleY": 0.14, "scaleZ": 1 }, - "Tags": [ - "LargeBox" - ], "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Connectionmarkers.170f10.json b/objects/Connectionmarkers.170f10.json index 26848bae..0df49af2 100644 --- a/objects/Connectionmarkers.170f10.json +++ b/objects/Connectionmarkers.170f10.json @@ -44,6 +44,9 @@ "Nickname": "Connection markers", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": true, "Transform": { "posX": -51, diff --git a/objects/Cursetokens.bd0253.json b/objects/Cursetokens.bd0253.json index 41b510ac..8a9e2f98 100644 --- a/objects/Cursetokens.bd0253.json +++ b/objects/Cursetokens.bd0253.json @@ -67,6 +67,7 @@ "Snap": true, "Sticky": true, "Tags": [ + "CleanUpHelper_ignore", "displacement_excluded" ], "Tooltip": true, diff --git a/objects/DoomCounter.85c4c6.json b/objects/DoomCounter.85c4c6.json index d93d5341..6f061c53 100644 --- a/objects/DoomCounter.85c4c6.json +++ b/objects/DoomCounter.85c4c6.json @@ -40,6 +40,9 @@ "Nickname": "Doom Counter", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": true, "Transform": { "posX": -5.3, diff --git a/objects/Doomtokens.47ffc3.json b/objects/Doomtokens.47ffc3.json index 51c4fbc0..059108ea 100644 --- a/objects/Doomtokens.47ffc3.json +++ b/objects/Doomtokens.47ffc3.json @@ -44,6 +44,9 @@ "Nickname": "Doom tokens", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": true, "Transform": { "posX": -55.48, diff --git a/objects/Doomtokens.b015d8.json b/objects/Doomtokens.b015d8.json index c4d4cd08..0c614022 100644 --- a/objects/Doomtokens.b015d8.json +++ b/objects/Doomtokens.b015d8.json @@ -44,6 +44,9 @@ "Nickname": "Doom tokens", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": true, "Transform": { "posX": 2.761, diff --git a/objects/Fan-MadeAccessories.aa8b38/Auto-failCounter.a9a321.json b/objects/Fan-MadeAccessories.aa8b38/Auto-failCounter.a9a321.json index c7b63814..2c2bae63 100644 --- a/objects/Fan-MadeAccessories.aa8b38/Auto-failCounter.a9a321.json +++ b/objects/Fan-MadeAccessories.aa8b38/Auto-failCounter.a9a321.json @@ -54,4 +54,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Fan-MadeAccessories.aa8b38/ElderSignCounter.e62cb5.json b/objects/Fan-MadeAccessories.aa8b38/ElderSignCounter.e62cb5.json index bfd05186..c2460aa5 100644 --- a/objects/Fan-MadeAccessories.aa8b38/ElderSignCounter.e62cb5.json +++ b/objects/Fan-MadeAccessories.aa8b38/ElderSignCounter.e62cb5.json @@ -54,4 +54,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Fan-MadeAccessories.aa8b38/Subject5U-21Helper.1335e8.json b/objects/Fan-MadeAccessories.aa8b38/Subject5U-21Helper.1335e8.json index a273d326..b3b58a32 100644 --- a/objects/Fan-MadeAccessories.aa8b38/Subject5U-21Helper.1335e8.json +++ b/objects/Fan-MadeAccessories.aa8b38/Subject5U-21Helper.1335e8.json @@ -73,4 +73,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/HeartofDarkness.cc95ff.json b/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/HeartofDarkness.cc95ff.json index eff7c98e..039e4366 100644 --- a/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/HeartofDarkness.cc95ff.json +++ b/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/HeartofDarkness.cc95ff.json @@ -83,4 +83,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/SoulsofDarkness.a94e6b.json b/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/SoulsofDarkness.a94e6b.json index 98af33ba..1b89cf16 100644 --- a/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/SoulsofDarkness.a94e6b.json +++ b/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/SoulsofDarkness.a94e6b.json @@ -82,4 +82,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/TheSandsOfMemphisCampaignExpansion.2634e3.json b/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/TheSandsOfMemphisCampaignExpansion.2634e3.json index af3d64bb..8492fb67 100644 --- a/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/TheSandsOfMemphisCampaignExpansion.2634e3.json +++ b/objects/Fan-MadeScenariosCampaignsMiscellany.66e97c/Fan-MadeCampaigns.89c32e/TheSandsOfMemphisCampaignExpansion.2634e3.json @@ -66,4 +66,4 @@ }, "Value": 0, "XmlUI": "" -} \ No newline at end of file +} diff --git a/objects/GUIDReferenceHandler.123456.json b/objects/GUIDReferenceHandler.123456.json new file mode 100644 index 00000000..bd13b2f4 --- /dev/null +++ b/objects/GUIDReferenceHandler.123456.json @@ -0,0 +1,45 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "ColorDiffuse": { + "b": 0.116, + "g": 0.116, + "r": 0.716 + }, + "Description": "This object handles GUID references to objects.", + "DragSelectable": true, + "GMNotes": "", + "GUID": "123456", + "Grid": true, + "GridProjection": false, + "Hands": false, + "HideWhenFaceDown": false, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": true, + "LuaScript": "require(\"core/GUIDReferenceHandler\")", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "go_game_piece_white", + "Nickname": "GUID Reference Handler", + "Snap": true, + "Sticky": true, + "Tooltip": true, + "Transform": { + "posX": 78, + "posY": 1.328, + "posZ": -8, + "rotX": 0, + "rotY": 0, + "rotZ": 0, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/MasterClueCounter.4a3aa4.json b/objects/MasterClueCounter.4a3aa4.json index c6b21615..5aed6ddc 100644 --- a/objects/MasterClueCounter.4a3aa4.json +++ b/objects/MasterClueCounter.4a3aa4.json @@ -19,7 +19,7 @@ }, "ImageScalar": 1, "ImageSecondaryURL": "", - "ImageURL": "http://cloud-3.steamusercontent.com/ugc/1758068501357164917/1D06F1DC4D6888B6F57124BD2AFE20D0B0DA15A8/", + "ImageURL": "http://cloud-3.steamusercontent.com/ugc/784129913444610342/7903BA89870C1656A003FD69C79BFA99BD1AAC24/", "WidthScale": 0 }, "Description": "Click to remove all clues from all investigators", @@ -37,9 +37,12 @@ "LuaScriptState": "false", "MeasureMovement": false, "Name": "Custom_Token", - "Nickname": "Master Clue Counter\n", + "Nickname": "Master Clue Counter", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": true, "Transform": { "posX": -5.3, diff --git a/objects/MythosArea.9f334f.json b/objects/MythosArea.9f334f.json index 83ba39e6..1eeead24 100644 --- a/objects/MythosArea.9f334f.json +++ b/objects/MythosArea.9f334f.json @@ -66,6 +66,9 @@ "Nickname": "Mythos Area", "Snap": true, "Sticky": true, + "Tags": [ + "CleanUpHelper_ignore" + ], "Tooltip": false, "Transform": { "posX": -1.309, diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.json index db2728ef..920cb3c3 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.json +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.json @@ -14,11 +14,12 @@ "r": 1 }, "ContainedObjects_order": [ - "BadBlood.451eaa", - "RedTideRising.5302f2", "AllorNothing.72ab92", + "BadBlood.451eaa", + "BytheBook.cc7eb3", + "LaidtoRest.e2dd57", "ReadorDie.9e73fa", - "BytheBook.cc7eb3" + "RedTideRising.5302f2" ], "ContainedObjects_path": "ChallengeScenarios.9f6801", "CustomMesh": { @@ -64,7 +65,7 @@ "Tooltip": true, "Transform": { "posX": -9, - "posY": 1.481, + "posY": 1.482, "posZ": -76, "rotX": 0, "rotY": 270, diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.luascriptstate b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.luascriptstate index 609f01d1..eacd2ea5 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.luascriptstate +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801.luascriptstate @@ -1 +1 @@ -{"ml":{"451eaa":{"lock":false,"pos":{"x":12.2499580383301,"y":1.46560525894165,"z":3.98636198043823},"rot":{"x":359.920135498047,"y":269.999908447266,"z":0.016873624175787}},"5302f2":{"lock":false,"pos":{"x":12.2504663467407,"y":1.45853757858276,"z":-20.013650894165},"rot":{"x":359.920135498047,"y":270.00146484375,"z":0.0168716721236706}},"72ab92":{"lock":false,"pos":{"x":12.2520532608032,"y":1.4679582118988,"z":11.9863719940186},"rot":{"x":359.920135498047,"y":270,"z":0.0168737415224314}},"9e73fa":{"lock":false,"pos":{"x":12.2500581741333,"y":1.46089386940002,"z":-12.0136384963989},"rot":{"x":359.920135498047,"y":269.999847412109,"z":0.0168744903057814}},"cc7eb3":{"lock":false,"pos":{"x":12.2495565414429,"y":1.46325027942657,"z":-4.01364088058472},"rot":{"x":359.920135498047,"y":269.999908447266,"z":0.0168744102120399}}}} +{"ml":{"451eaa":{"lock":false,"pos":{"x":12.252,"y":1.4815,"z":11.986},"rot":{"x":0,"y":269.9999,"z":0}},"5302f2":{"lock":false,"pos":{"x":12.2505,"y":1.4815,"z":-20.0137},"rot":{"x":0,"y":270.0014,"z":0}},"72ab92":{"lock":false,"pos":{"x":12.25,"y":1.4815,"z":19.986},"rot":{"x":0,"y":269.9999,"z":0}},"9e73fa":{"lock":false,"pos":{"x":12.2501,"y":1.4815,"z":-12.0137},"rot":{"x":0,"y":269.9998,"z":0}},"cc7eb3":{"lock":false,"pos":{"x":12.25,"y":1.4815,"z":3.986},"rot":{"x":0,"y":269.9999,"z":0}},"e2dd57":{"lock":false,"pos":{"x":12.25,"y":1.4815,"z":-4.014},"rot":{"x":0,"y":270,"z":0}}}} diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/AllorNothing.72ab92.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/AllorNothing.72ab92.json index 0ed9d952..b150356e 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/AllorNothing.72ab92.json +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/AllorNothing.72ab92.json @@ -71,9 +71,9 @@ "Sticky": true, "Tooltip": true, "Transform": { - "posX": 12.252, - "posY": 1.468, - "posZ": 11.986, + "posX": 12.25, + "posY": 1.481, + "posZ": 19.986, "rotX": 0, "rotY": 270, "rotZ": 0, diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BadBlood.451eaa.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BadBlood.451eaa.json index 7d1189e1..a8fe9cf7 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BadBlood.451eaa.json +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BadBlood.451eaa.json @@ -71,9 +71,9 @@ "Sticky": true, "Tooltip": true, "Transform": { - "posX": 12.25, - "posY": 1.466, - "posZ": 3.986, + "posX": 12.252, + "posY": 1.481, + "posZ": 11.986, "rotX": 0, "rotY": 270, "rotZ": 0, diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BytheBook.cc7eb3.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BytheBook.cc7eb3.json index 00b94b1b..ba1c69ad 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BytheBook.cc7eb3.json +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/BytheBook.cc7eb3.json @@ -72,8 +72,8 @@ "Tooltip": true, "Transform": { "posX": 12.25, - "posY": 1.463, - "posZ": -4.014, + "posY": 1.481, + "posZ": 3.986, "rotX": 0, "rotY": 270, "rotZ": 0, diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/LaidtoRest.e2dd57.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/LaidtoRest.e2dd57.json new file mode 100644 index 00000000..6750e563 --- /dev/null +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/LaidtoRest.e2dd57.json @@ -0,0 +1,86 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "AttachedDecals": [ + { + "CustomDecal": { + "ImageURL": "http://cloud-3.steamusercontent.com/ugc/959719855119695911/931B9829687A20F4DEADB36DA57B7E6D76792231/", + "Name": "dunwich_back", + "Size": 7.4 + }, + "Transform": { + "posX": 0, + "posY": 0, + "posZ": 0, + "rotX": 270, + "rotY": 0, + "rotZ": 0, + "scaleX": 2, + "scaleY": 2, + "scaleZ": 2 + } + } + ], + "Autoraise": true, + "ColorDiffuse": { + "a": 0.27451, + "b": 1, + "g": 1, + "r": 1 + }, + "CustomMesh": { + "CastShadows": true, + "ColliderURL": "", + "Convex": true, + "CustomShader": { + "FresnelStrength": 0, + "SpecularColor": { + "b": 1, + "g": 1, + "r": 1 + }, + "SpecularIntensity": 0, + "SpecularSharpness": 2 + }, + "DiffuseURL": "http://cloud-3.steamusercontent.com/ugc/2115061845788468343/B7611EC7DCD2008B87D6518EBEFF0AD36EFE5B54/", + "MaterialIndex": 3, + "MeshURL": "https://raw.githubusercontent.com/RobMayer/TTSLibrary/master/advboxes/tuckbox_h_MSH.obj", + "NormalURL": "", + "TypeIndex": 0 + }, + "Description": "Challenge Scenario", + "DragSelectable": true, + "GMNotes": "scenarios/challenge_laid_to_rest.json", + "GUID": "e2dd57", + "Grid": true, + "GridProjection": false, + "Hands": true, + "HideWhenFaceDown": false, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": false, + "LuaScript": "require(\"core/DownloadBox\")", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "Custom_Model", + "Nickname": "Laid to Rest", + "Snap": true, + "Sticky": true, + "Tooltip": true, + "Transform": { + "posX": 12.25, + "posY": 1.481, + "posZ": -4.014, + "rotX": 0, + "rotY": 270, + "rotZ": 0, + "scaleX": 2.21, + "scaleY": 0.46, + "scaleZ": 2.42 + }, + "Value": 0, + "XmlUI": "" +} diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/ReadorDie.9e73fa.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/ReadorDie.9e73fa.json index becd0521..2acce7c1 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/ReadorDie.9e73fa.json +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/ReadorDie.9e73fa.json @@ -72,7 +72,7 @@ "Tooltip": true, "Transform": { "posX": 12.25, - "posY": 1.461, + "posY": 1.481, "posZ": -12.014, "rotX": 0, "rotY": 270, diff --git a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/RedTideRising.5302f2.json b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/RedTideRising.5302f2.json index b794a5df..85fdb993 100644 --- a/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/RedTideRising.5302f2.json +++ b/objects/OfficialStandaloneChallengeScenarios.0ef5c8/ChallengeScenarios.9f6801/RedTideRising.5302f2.json @@ -72,7 +72,7 @@ "Tooltip": true, "Transform": { "posX": 12.25, - "posY": 1.459, + "posY": 1.481, "posZ": -20.014, "rotX": 0, "rotY": 270, diff --git a/objects/PlaceholderBoxDummy.a93466.json b/objects/PlaceholderBoxDummy.a93466.json new file mode 100644 index 00000000..f20cff4f --- /dev/null +++ b/objects/PlaceholderBoxDummy.a93466.json @@ -0,0 +1,45 @@ +{ + "AltLookAngle": { + "x": 0, + "y": 0, + "z": 0 + }, + "Autoraise": true, + "ColorDiffuse": { + "b": 0.82353, + "g": 0.20157, + "r": 0 + }, + "Description": "This dummy is there to hold the up-to-date script file for placeholder boxes to be available for placeholder box spawning.", + "DragSelectable": true, + "GMNotes": "", + "GUID": "a93466", + "Grid": true, + "GridProjection": false, + "Hands": false, + "HideWhenFaceDown": false, + "IgnoreFoW": false, + "LayoutGroupSortIndex": 0, + "Locked": true, + "LuaScript": "require(\"core/DownloadBox\")", + "LuaScriptState": "", + "MeasureMovement": false, + "Name": "BlockRectangle", + "Nickname": "Placeholder Box Dummy", + "Snap": true, + "Sticky": true, + "Tooltip": true, + "Transform": { + "posX": 78, + "posY": 1.645, + "posZ": -33, + "rotX": 0, + "rotY": 0, + "rotZ": 0, + "scaleX": 1, + "scaleY": 1, + "scaleZ": 1 + }, + "Value": 0, + "XmlUI": "" +} \ No newline at end of file diff --git a/objects/Playarea.721ba2.json b/objects/Playarea.721ba2.json index 0bbd9af2..b820bf54 100644 --- a/objects/Playarea.721ba2.json +++ b/objects/Playarea.721ba2.json @@ -974,7 +974,7 @@ "LuaScriptState": "{\"trackedLocations\":[]}", "MeasureMovement": false, "Name": "Custom_Token", - "Nickname": "Playarea", + "Nickname": "Play Area", "Snap": true, "Sticky": true, "Tags": [ diff --git a/objects/Playermat1White.8b081b.json b/objects/Playermat1White.8b081b.json index 0cd532bf..afb6d149 100644 --- a/objects/Playermat1White.8b081b.json +++ b/objects/Playermat1White.8b081b.json @@ -343,9 +343,10 @@ "IgnoreFoW": false, "LayoutGroupSortIndex": 0, "Locked": true, + "LuaScript": "require(\"playermat/Playmat\")", "LuaScriptState_path": "Playermat1White.8b081b.luascriptstate", - "LuaScript_path": "Playermat1White.8b081b.ttslua", "MeasureMovement": false, + "Memo": "White", "Name": "Custom_Tile", "Nickname": "Playermat 1: White", "Snap": true, diff --git a/objects/Playermat1White.8b081b.ttslua b/objects/Playermat1White.8b081b.ttslua deleted file mode 100644 index 6d2d842f..00000000 --- a/objects/Playermat1White.8b081b.ttslua +++ /dev/null @@ -1,11 +0,0 @@ ---------------------------------------------------------- --- specific setup (different for each playmat) ---------------------------------------------------------- - -TRASHCAN_GUID = "147e80" -STAT_TRACKER_GUID = "e598c2" -RESOURCE_COUNTER_GUID = "4406f0" -CLUE_COUNTER_GUID = "d86b7c" -CLUE_CLICKER_GUID = "db85d6" - -require("playermat/Playmat") diff --git a/objects/Playermat2Orange.bd0ff4.json b/objects/Playermat2Orange.bd0ff4.json index ccc4720a..a0c7a9a1 100644 --- a/objects/Playermat2Orange.bd0ff4.json +++ b/objects/Playermat2Orange.bd0ff4.json @@ -343,9 +343,10 @@ "IgnoreFoW": false, "LayoutGroupSortIndex": 0, "Locked": true, + "LuaScript": "require(\"playermat/Playmat\")", "LuaScriptState_path": "Playermat2Orange.bd0ff4.luascriptstate", - "LuaScript_path": "Playermat2Orange.bd0ff4.ttslua", "MeasureMovement": false, + "Memo": "Orange", "Name": "Custom_Tile", "Nickname": "Playermat 2: Orange", "Snap": true, diff --git a/objects/Playermat2Orange.bd0ff4.ttslua b/objects/Playermat2Orange.bd0ff4.ttslua deleted file mode 100644 index 4530453c..00000000 --- a/objects/Playermat2Orange.bd0ff4.ttslua +++ /dev/null @@ -1,11 +0,0 @@ ---------------------------------------------------------- --- specific setup (different for each playmat) ---------------------------------------------------------- - -TRASHCAN_GUID = "f7b6c8" -STAT_TRACKER_GUID = "b4a5f7" -RESOURCE_COUNTER_GUID = "816d84" -CLUE_COUNTER_GUID = "1769ed" -CLUE_CLICKER_GUID = "3f22e5" - -require("playermat/Playmat") diff --git a/objects/Playermat3Green.383d8b.json b/objects/Playermat3Green.383d8b.json index 82f2760f..1f22c5f7 100644 --- a/objects/Playermat3Green.383d8b.json +++ b/objects/Playermat3Green.383d8b.json @@ -343,9 +343,10 @@ "IgnoreFoW": false, "LayoutGroupSortIndex": 0, "Locked": true, + "LuaScript": "require(\"playermat/Playmat\")", "LuaScriptState_path": "Playermat3Green.383d8b.luascriptstate", - "LuaScript_path": "Playermat3Green.383d8b.ttslua", "MeasureMovement": false, + "Memo": "Green", "Name": "Custom_Tile", "Nickname": "Playermat 3: Green", "Snap": true, diff --git a/objects/Playermat3Green.383d8b.ttslua b/objects/Playermat3Green.383d8b.ttslua deleted file mode 100644 index 818bd09f..00000000 --- a/objects/Playermat3Green.383d8b.ttslua +++ /dev/null @@ -1,11 +0,0 @@ ---------------------------------------------------------- --- specific setup (different for each playmat) ---------------------------------------------------------- - -TRASHCAN_GUID = "5f896a" -STAT_TRACKER_GUID = "af7ed7" -RESOURCE_COUNTER_GUID = "cd15ac" -CLUE_COUNTER_GUID = "032300" -CLUE_CLICKER_GUID = "891403" - -require("playermat/Playmat") diff --git a/objects/Playermat4Red.0840d5.json b/objects/Playermat4Red.0840d5.json index aeb7a709..96edae24 100644 --- a/objects/Playermat4Red.0840d5.json +++ b/objects/Playermat4Red.0840d5.json @@ -343,9 +343,10 @@ "IgnoreFoW": false, "LayoutGroupSortIndex": 0, "Locked": true, + "LuaScript": "require(\"playermat/Playmat\")", "LuaScriptState_path": "Playermat4Red.0840d5.luascriptstate", - "LuaScript_path": "Playermat4Red.0840d5.ttslua", "MeasureMovement": false, + "Memo": "Red", "Name": "Custom_Tile", "Nickname": "Playermat 4: Red", "Snap": true, diff --git a/objects/Playermat4Red.0840d5.ttslua b/objects/Playermat4Red.0840d5.ttslua deleted file mode 100644 index 6c0fb70f..00000000 --- a/objects/Playermat4Red.0840d5.ttslua +++ /dev/null @@ -1,11 +0,0 @@ ---------------------------------------------------------- --- specific setup (different for each playmat) ---------------------------------------------------------- - -TRASHCAN_GUID = "4b8594" -STAT_TRACKER_GUID = "e74881" -RESOURCE_COUNTER_GUID = "a4b60d" -CLUE_COUNTER_GUID = "37be78" -CLUE_CLICKER_GUID = "4111de" - -require("playermat/Playmat") diff --git a/objects/RulesReference.d99993.json b/objects/RulesReference.d99993.json index 4de14917..efc620d1 100644 --- a/objects/RulesReference.d99993.json +++ b/objects/RulesReference.d99993.json @@ -14,7 +14,7 @@ "PDFPage": 0, "PDFPageOffset": 0, "PDFPassword": "", - "PDFUrl": "https://images-cdn.fantasyflightgames.com/filer_public/c4/b0/c4b0d66c-d79e-411b-bdb5-b5d8c457d4bc/ahc01_rules_reference_web.pdf" + "PDFUrl": "http://cloud-3.steamusercontent.com/ugc/2115061845793806189/6FC67F9AF9224452E2D8F25E63B88D702B21B0DC/" }, "Description": "", "DragSelectable": true, diff --git a/objects/SoundCube.3c988f.json b/objects/SoundCube.3c988f.json index c27821d2..431b9499 100644 --- a/objects/SoundCube.3c988f.json +++ b/objects/SoundCube.3c988f.json @@ -35,9 +35,6 @@ "Nickname": "SoundCube", "Snap": true, "Sticky": true, - "Tags": [ - "SoundCube" - ], "Tooltip": true, "Transform": { "posX": 78, diff --git a/objects/TokenArranger.022907.json b/objects/TokenArranger.022907.json index ec5c2a4a..19ab79ff 100644 --- a/objects/TokenArranger.022907.json +++ b/objects/TokenArranger.022907.json @@ -40,9 +40,6 @@ "Nickname": "Token Arranger", "Snap": true, "Sticky": true, - "Tags": [ - "TokenArranger" - ], "Tooltip": true, "Transform": { "posX": -42.3, diff --git a/src/accessories/CampaignImporterExporter.ttslua b/src/accessories/CampaignImporterExporter.ttslua index 5d7c9a4e..80facb5f 100644 --- a/src/accessories/CampaignImporterExporter.ttslua +++ b/src/accessories/CampaignImporterExporter.ttslua @@ -1,11 +1,12 @@ -local blessCurseApi = require("chaosbag/BlessCurseManagerApi") -local chaosBagApi = require("chaosbag/ChaosBagApi") -local deckImporterApi = require("arkhamdb/DeckImporterApi") -local optionPanelApi = require("core/OptionPanelApi") -local playAreaApi = require("core/PlayAreaApi") +local blessCurseApi = require("chaosbag/BlessCurseManagerApi") +local chaosBagApi = require("chaosbag/ChaosBagApi") +local deckImporterApi = require("arkhamdb/DeckImporterApi") +local guidReferenceApi = require("core/GUIDReferenceApi") +local optionPanelApi = require("core/OptionPanelApi") +local playAreaApi = require("core/PlayAreaApi") +local playmatApi = require("playermat/PlaymatApi") local campaignTokenData = { - GUID = "51b1c9", Name = "Custom_Model", Transform = { posX = -21.25, @@ -18,9 +19,7 @@ local campaignTokenData = { scaleY = 2, scaleZ = 2 }, - Nickname = "Arkham Coin", Description = "SCED Importer Token", - GMNotes = "", Tags = { "ImporterToken" }, @@ -33,56 +32,48 @@ local campaignTokenData = { MaterialIndex = 2, TypeIndex = 0, CustomShader = { - SpecularColor = { - r = 0.7222887, - g = 0.507659256, - b = 0.339915335 - }, - SpecularIntensity = 0.4, - SpecularSharpness = 7.0, - FresnelStrength = 0.0 + SpecularColor = { + r = 0.7222887, + g = 0.507659256, + b = 0.339915335 + }, + SpecularIntensity = 0.4, + SpecularSharpness = 7.0, + FresnelStrength = 0.0 }, CastShadows = true } } +local COLORS = { "White", "Orange", "Green", "Red" } --- counter GUIDS (4x damage and 4x horror) -local DAMAGE_HORROR_GUIDS = { - "eb08d6"; "e64eec"; "1f5a0a"; "591a45"; - "468e88"; "0257d9"; "7b5729"; "beb964"; - } - -local TOUR_GUID = "0e5aa8" -local campaignBoxGUID - -function onLoad(save_state) - campaignBoxGUID = "" - +function onLoad() self.createButton({ click_function = "findCampaignFromToken", function_owner = self, label = "Import", - tooltip = "Load in a campaign save from a token!\n\n(Token can be anywhere on the table, but ensure there is only 1!)", - position = {x=-1, y=0.2, z=0}, + tooltip = "Load in a campaign save from a token!\n\n(Token can be anywhere on the table, but ensure there is only 1!)", + position = { x = -1, y = 0.2, z = 0 }, font_size = 400, width = 1400, height = 600, - scale = {0.5, 1, 0.5}, + scale = { 0.5, 1, 0.5 }, }) self.createButton({ click_function = "createCampaignToken", function_owner = self, label = "Export", tooltip = "Create a campaign save token!\n\n(Ensure all chaos tokens have been unsealed!)", - position = {x=1, y=0.2, z=0}, + position = { x = 1, y = 0.2, z = 0 }, font_size = 400, width = 1400, height = 600, - scale = {0.5, 1, 0.5}, + scale = { 0.5, 1, 0.5 }, }) end --- The main import functions. Due to timing concerns, has to be split up into several separate methods to allow for Wait conditions +--------------------------------------------------------- +-- main import functions (split up to allow for Wait conditions) +--------------------------------------------------------- -- Identifies import token, determines campaign box and downloads it (if needed) function findCampaignFromToken(_, _, _) @@ -91,11 +82,13 @@ function findCampaignFromToken(_, _, _) if #coinObjects == 0 then broadcastToAll("Could not find importer token", Color.Red) elseif #coinObjects > 1 then - broadcastToAll("More than 1 importer token found. Please delete all but 1 importer token", Color.Yellow) + broadcastToAll("More than 1 importer token found. Please delete all but 1 importer token", Color.Yellow) else coin = coinObjects[1] + local importData = JSON.decode(coin.getGMNotes()) campaignBoxGUID = importData["box"] + local campaignBox = getObjectFromGUID(campaignBoxGUID) if campaignBox.type == "Generic" then campaignBox.call("buttonClick_download") @@ -110,24 +103,24 @@ function findCampaignFromToken(_, _, _) end, function() local obj = getObjectFromGUID(campaignBoxGUID) - if obj == nil then - return false + if obj == nil then + return false else return obj.type == "Bag" and obj.getLuaScript() ~= "" end end, 2, function() broadcastToAll("Error loading campaign box") end - ) + ) end end -- After box has been downloaded, places content on table function placeCampaignFromToken(importData) - getObjectFromGUID(campaignBoxGUID).call("buttonClick_place") + getObjectFromGUID(importData["box"]).call("buttonClick_place") Wait.condition( function() createCampaignFromToken(importData) end, - function() return findCampaignLog() ~= nil end, + function() return findUniqueObjectWithTag("CampaignLog") ~= nil end, 2, function() broadcastToAll("Error placing campaign box") end ) @@ -135,82 +128,86 @@ end -- After content is placed on table, conducts all the other import operations function createCampaignFromToken(importData) - findCampaignLog().destruct() - --create campaign log - spawnObjectData({data = importData["log"]}) - --create chaos bag + -- destroy existing campaign log and load saved campaign log + findUniqueObjectWithTag("CampaignLog").destruct() + spawnObjectData({ data = importData["log"] }) + chaosBagApi.setChaosBagState(importData["bag"]) - --populate trauma values + + -- populate trauma values if importData["trauma"] then - updateCounters(importData["trauma"]) + setTrauma(importData["trauma"]) end - --populate ArkhamDB deck IDs + + -- populate ArkhamDB deck IDs if importData["decks"] then deckImporterApi.setUiState(importData["decks"]) end - --set investigator count + playAreaApi.setInvestigatorCount(importData["clueCount"]) - --set campaign guide page - local guide = findCampaignGuide() + + -- set campaign guide page + local guide = findUniqueObjectWithTag("CampaignGuide") if guide then Wait.condition( - -- Called after the condition function returns true - function() - log("Campaign Guide import successful!") - end, - -- Condition function that is called continiously until returs true or timeout is reached - function() - guide.Book.setPage(importData["guide"]) - return guide.Book.getPage() == importData["guide"] - end, + -- Called after the condition function returns true + function() log("Campaign Guide import successful!") end, + -- Condition function that is called continiously until returs true or timeout is reached + function() return guide.Book.setPage(importData["guide"]) end, -- Amount of time in seconds until the Wait times out 1, -- Called if the Wait times out - function() - log("Campaign Guide import failed!") - end + function() log("Campaign Guide import failed!") end ) end - Wait.time( - function() optionPanelApi.loadSettings(importData["options"]) end, - 0.5 - ) - getObjectFromGUID(TOUR_GUID).destruct() + + Wait.time(function() optionPanelApi.loadSettings(importData["options"]) end, 0.5) + + -- destroy Tour Starter token + local tourStarter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TourStarter") + tourStarter.destruct() + + -- restore PlayArea image playAreaApi.updateSurface(importData["playmat"]) + broadcastToAll("Campaign successfully imported!", Color.Green) end - -- Creates a campaign token with save data encoded into GM Notes based on the current state of the table function createCampaignToken(_, playerColor, _) -- clean up chaos tokens blessCurseApi.removeAll(playerColor) chaosBagApi.releaseAllSealedTokens(playerColor) - local campaignBoxGUID = "" -- find active campaign + local campaignBox for _, obj in ipairs(getObjectsWithTag("CampaignBox")) do if obj.type == "Bag" and #obj.getObjects() == 0 then - if campaignBoxGUID ~= "" then + if not campaignBox then + campaignBox = obj + else broadcastToAll("Multiple empty campaign box detected; delete all but one.", Color.Red) return end - campaignBoxGUID = obj.getGUID() end end - if campaignBoxGUID == "" then + if not campaignBox then broadcastToAll("Campaign box with all placed objects not found!", Color.Red) return end - local campaignLog = findCampaignLog() + + local campaignLog = findUniqueObjectWithTag("CampaignLog") if campaignLog == nil then broadcastToAll("Campaign log not found!", Color.Red) return end - local traumaValues = nil + + local traumaValues = { + 0, 0, 0, 0, + 0, 0, 0, 0 + } local counterData = campaignLog.getVar("ref_buttonData") if counterData ~= nil then - traumaValues = {} printToAll("Trauma values found in campaign log!", "Green") for i = 1, 10, 3 do traumaValues[1 + (i - 1) / 3] = counterData.counter[i].value @@ -220,78 +217,49 @@ function createCampaignToken(_, playerColor, _) printToAll("Trauma values could not be found in campaign log!", "Yellow") printToAll("Default values for health and sanity loaded.", "Yellow") end - local campaignGuide = findCampaignGuide() + + local campaignGuide = findUniqueObjectWithTag("CampaignGuide") if campaignGuide == nil then broadcastToAll("Campaign guide not found!", Color.Red) return end - local campaignGuidePage = campaignGuide.Book.getPage() + local campaignData = { - box = campaignBoxGUID, + box = campaignBox.getGUID(), log = campaignLog.getData(), bag = chaosBagApi.getChaosBagState(), trauma = traumaValues, decks = deckImporterApi.getUiState(), clueCount = playAreaApi.getInvestigatorCount(), - guide = campaignGuidePage, + guide = campaignGuide.Book.getPage(), options = optionPanelApi.getOptions(), playmat = playAreaApi.getSurface() } campaignTokenData.GMNotes = JSON.encode(campaignData) - campaignTokenData.Nickname = os.date("%b %d ") .. getObjectFromGUID(campaignBoxGUID).getName() .. " Save" - spawnObjectData({ - data = campaignTokenData, - position = {-21.25, 1.68, 55.59} - }) + campaignTokenData.Nickname = os.date("%b %d ") .. campaignBox.getName() .. " Save" + spawnObjectData({ data = campaignTokenData }) broadcastToAll("Campaign successfully exported! Save coin object to import on a fresh save", Color.Green) end - +--------------------------------------------------------- -- helper functions +--------------------------------------------------------- -function findCampaignLog() - local campaignLog = getObjectsWithTag("CampaignLog") - if campaignLog then - if #campaignLog == 1 then - return campaignLog[1] - else - broadcastToAll("More than 1 campaign log detected; delete all but one.", Color.Red) - return nil - end +function findUniqueObjectWithTag(tag) + local objects = getObjectsWithTag(tag) + if not objects then return end + + if #objects == 1 then + return objects[1] else + broadcastToAll("More than 1 " .. tag .. " detected; delete all but one.", Color.Red) return nil end end -function findCampaignGuide() - local campaignGuide = getObjectsWithTag("CampaignGuide") - if campaignGuide then - if #campaignGuide == 1 then - return campaignGuide[1] - else - broadcastToAll("More than 1 campaign guide detected; delete all but one.", Color.Red) - return nil - end - else - return nil - end -end - -function updateCounters(tableOfNewValues) - if tonumber(tableOfNewValues) then - local value = tableOfNewValues - tableOfNewValues = {} - for i = 1, #DAMAGE_HORROR_GUIDS do - table.insert(tableOfNewValues, value) - end - end - - for i, guid in ipairs(DAMAGE_HORROR_GUIDS) do - local TOKEN = getObjectFromGUID(guid) - if TOKEN ~= nil then - TOKEN.call("updateVal", tableOfNewValues[i]) - else - printToAll(": No. " .. i .. " could not be found.", "Yellow") - end +function setTrauma(trauma) + for i = 1, 4 do + playmatApi.updateCounter(COLORS[i], "DamageCounter", trauma[i]) + playmatApi.updateCounter(COLORS[i], "HorrorCounter", trauma[i + 4]) end end diff --git a/src/accessories/CleanUpHelper.ttslua b/src/accessories/CleanUpHelper.ttslua index 4a3548c0..8fef10e5 100644 --- a/src/accessories/CleanUpHelper.ttslua +++ b/src/accessories/CleanUpHelper.ttslua @@ -3,79 +3,24 @@ - puts everything on playmats and hands into respective trashcans - use the IGNORE_TAG to exclude objects from tidying (default: "CleanUpHelper_Ignore")]] -local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") -local chaosBagApi = require("chaosbag/ChaosBagApi") -local playAreaApi = require("core/PlayAreaApi") -local playmatApi = require("playermat/PlaymatApi") -local soundCubeApi = require("core/SoundCubeApi") -local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") - --- these objects will be ignored -local IGNORE_GUIDS = { - -- big playmat, change image panel and investigator counter - "b7b45b", "f182ee", "721ba2", - -- bless/curse manager - "afa06b", "bd0253", "5933fb", - -- stuff on agenda/act playmat - "85c4c6", "4a3aa4", "fea079", "b015d8", "11e0cf", "9f334f", "70b9f6", "0a5a29", - -- doom/location token bag - "47ffc3", "170f10", - -- table - "4ee1f2" -} +local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") +local chaosBagApi = require("chaosbag/ChaosBagApi") +local guidReferenceApi = require("core/GUIDReferenceApi") +local playAreaApi = require("core/PlayAreaApi") +local playmatApi = require("playermat/PlaymatApi") +local soundCubeApi = require("core/SoundCubeApi") +local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") -- objects with this tag will be ignored local IGNORE_TAG = "CleanUpHelper_ignore" -- colors and order for following tables -local COLORS = { "White", "Orange", "Green", "Red", "Agenda" } - --- counter GUIDS (4x damage and 4x horror) -local DAMAGE_HORROR_GUIDS = { - "eb08d6", "e64eec", "1f5a0a", "591a45", - "468e88", "0257d9", "7b5729", "beb964", -} - +local COLORS = { "White", "Orange", "Green", "Red", "Mythos" } local campaignLog local RESET_VALUES = {} - --- GUIDS of objects (in order of ownership relating to 'COLORS') -local PLAYERMAT_GUIDS = { "8b081b", "bd0ff4", "383d8b", "0840d5" } -local RESOURCE_GUIDS = { "4406f0", "816d84", "cd15ac", "a4b60d" } -local TRACKER_GUIDS = { "e598c2", "b4a5f7", "af7ed7", "e74881" } -local CLUE_GUIDS = { "d86b7c", "1769ed", "032300", "37be78" } -local CLUE_CLICKER_GUIDS = { "db85d6", "3f22e5", "891403", "4111de" } -local TRASHCAN_GUIDS = { "147e80", "f7b6c8", "5f896a", "4b8594", "70b9f6" } - --- values for physics.cast (4 entries for player zones, 5th entry for agenda/act deck, 6th for campaign log) -local PHYSICS_POSITION = { - { -54.5, 2, 21 }, - { -54.5, 2, -21 }, - { -27.0, 2, 26 }, - { -27.0, 2, -26 }, - { -02.0, 2, 10 }, - { -00.0, 2, -27 } -} - -local PHYSICS_ROTATION = { - 270, - 270, - 0, - 180, - 270, - 0 -} - -local PHYSICS_SCALE = { - { 36.6, 1, 14.5 }, - { 36.6, 1, 14.5 }, - { 34.0, 1, 14.5 }, - { 34.0, 1, 14.5 }, - { 55.0, 1, 13.5 }, - { 05.0, 1, 05.0 } -} - +local loadingFailedBefore = false local optionsVisible = false + local options = {} options["importTrauma"] = true options["tidyPlayermats"] = true @@ -84,7 +29,6 @@ options["removeDrawnLines"] = false local buttonParameters = {} buttonParameters.function_owner = self -local loadingFailedBefore = false --------------------------------------------------------- -- option loading and GUI setup @@ -131,14 +75,6 @@ function onLoad(savedData) buttonParameters.position.z = 1.1 buttonParameters.width = 1550 self.createButton(buttonParameters) - - -- create single table for ignoring - for _, v in ipairs(CLUE_GUIDS) do table.insert(IGNORE_GUIDS, v) end - for _, v in ipairs(CLUE_CLICKER_GUIDS) do table.insert(IGNORE_GUIDS, v) end - for _, v in ipairs(RESOURCE_GUIDS) do table.insert(IGNORE_GUIDS, v) end - for _, v in ipairs(TRASHCAN_GUIDS) do table.insert(IGNORE_GUIDS, v) end - for _, v in ipairs(PLAYERMAT_GUIDS) do table.insert(IGNORE_GUIDS, v) end - for _, v in ipairs(DAMAGE_HORROR_GUIDS) do table.insert(IGNORE_GUIDS, v) end end --------------------------------------------------------- @@ -178,13 +114,8 @@ function cleanUp(_, color) getTrauma() -- delay to account for potential state change - Wait.time(function() - updateCounters(RESOURCE_GUIDS, 5, "Resource") - updateCounters(CLUE_CLICKER_GUIDS, 0, "Clue clicker") - updateCounters(DAMAGE_HORROR_GUIDS, RESET_VALUES, "Damage / Horror") - end, 0.2) + Wait.time(updateCounters, 0.2) - resetSkillTrackers() resetDoomCounter() blessCurseManagerApi.removeAll(color) removeLines() @@ -201,39 +132,20 @@ end -- modular functions, called by other functions --------------------------------------------------------- -function updateCounters(tableOfGUIDs, newValues, info) - -- instead of a table, this will be used if just a single value is provided - local singleValue = tonumber(newValues) +function updateCounters() + playmatApi.updateCounter("All", "ResourceCounter" , 5) + playmatApi.updateCounter("All", "ClickableClueCounter" , 0) + playmatApi.resetSkillTracker("All") - for i, guid in ipairs(tableOfGUIDs) do - local TOKEN = getObjectFromGUID(guid) - local newValue = singleValue or newValues[i] - - if TOKEN ~= nil then - TOKEN.call("updateVal", newValue) - else - printToAll(info .. ": No. " .. i .. " could not be found.", "Yellow") - end - end -end - --- set investigator skill trackers to "1, 1, 1, 1" -function resetSkillTrackers() - for i, guid in ipairs(TRACKER_GUIDS) do - local obj = getObjectFromGUID(guid) - - if obj ~= nil then - obj.call("updateStats", { 1, 1, 1, 1 }) - else - printToAll("Skill tracker for " .. COLORS[i] .. " playmat could not be found.", "Yellow") - end + for i = 1, 4 do + playmatApi.updateCounter(COLORS[i], "DamageCounter", RESET_VALUES.Damage[i]) + playmatApi.updateCounter(COLORS[i], "HorrorCounter", RESET_VALUES.Horror[i]) end end -- reset doom on agenda function resetDoomCounter() - local doomCounter = getObjectFromGUID("85c4c6") - + local doomCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomCounter") if doomCounter ~= nil then doomCounter.call("updateVal") else @@ -241,19 +153,19 @@ function resetDoomCounter() end end --- gets the GUID of a custom data helper (if present) and adds it to the ignore list +-- adds the ignore tag to the custom data helper function ignoreCustomDataHelper() local customDataHelper = playAreaApi.getCustomDataHelper() if customDataHelper then - table.insert(IGNORE_GUIDS, customDataHelper.getGUID()) + customDataHelper.addTag(IGNORE_TAG) end end -- read values for trauma from campaign log if enabled function getTrauma() RESET_VALUES = { - 0, 0, 0, 0, - 0, 0, 0, 0 + Damage = { 0, 0, 0, 0 }, + Horror = { 0, 0, 0, 0 } } -- stop here if trauma import is disabled @@ -263,13 +175,12 @@ function getTrauma() end -- get campaign log - campaignLog = findObjects(6)[1] + campaignLog = getObjectsWithTag("CampaignLog")[1] if campaignLog == nil then printToAll("Campaign log not found in standard position!", "Yellow") printToAll("Default values for health and sanity loaded.", "Yellow") return end - campaignLog = campaignLog.hit_object loadTrauma() end @@ -280,7 +191,14 @@ function loadTrauma() if trauma ~= nil then printToAll("Trauma values found in campaign log!", "Green") - RESET_VALUES = campaignLog.call("returnTrauma") + trauma = campaignLog.call("returnTrauma") + for i = 1, 8 do + if i < 5 then + RESET_VALUES.Damage[i] = trauma[i] + else + RESET_VALUES.Horror[i-4] = trauma[i] + end + end loadingFailedBefore = false elseif loadingFailedBefore then printToAll("Trauma values could not be found in campaign log!", "Yellow") @@ -303,7 +221,7 @@ end -- remove drawn lines function removeLines() if options["removeDrawnLines"] then - printToAll("Removing vector lines...", "White") + printToAll("Removing global vector lines...", "White") Global.setVectorLines({}) end end @@ -312,40 +230,40 @@ end function discardHands() if not options["tidyPlayermats"] then return end for i = 1, 4 do - local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[i]) - if trashcan == nil then return end + local trash = guidReferenceApi.getObjectByOwnerAndType(COLORS[i], "Trash") + if trash == nil then return end local hand = Player[playmatApi.getPlayerColor(COLORS[i])].getHandObjects() for j = #hand, 1, -1 do - trashcan.putObject(hand[j]) + trash.putObject(hand[j]) end end end -- clean up for play area function tidyPlayareaCoroutine() - local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[5]) - local PLAYMATZONE = getObjectFromGUID("a2f932") + local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash") + local playAreaZone = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayAreaZone") - if PLAYMATZONE == nil then + if playAreaZone == nil then printToAll("Scripting zone for main play area could not be found!", "Red") - elseif trashcan == nil then + elseif trash == nil then printToAll("Trashcan for main play area could not be found!", "Red") else - for _, obj in ipairs(PLAYMATZONE.getObjects()) do + for _, obj in ipairs(playAreaZone.getObjects()) do -- ignore these elements - if not tableContains(IGNORE_GUIDS, obj.getGUID()) and obj.hasTag(IGNORE_TAG) == false then + if obj.hasTag(IGNORE_TAG) == false and checkMemo(obj) == false then coroutine.yield(0) - trashcan.putObject(obj) + trash.putObject(obj) end end end - printToAll("Tidying playermats and agenda mat...", "White") + printToAll("Tidying playermats and mythos area...", "White") startLuaCoroutine(self, "tidyPlayerMatCoroutine") return 1 end --- clean up for the four playermats and the agenda/act playmat +-- clean up for the four playermats and the mythos area function tidyPlayerMatCoroutine() for i = 1, 5 do -- only continue for playermat (1-4) if option enabled @@ -353,32 +271,38 @@ function tidyPlayerMatCoroutine() -- delay for animation purpose for k = 1, 30 do coroutine.yield(0) end - -- get respective trashcan - local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[i]) - if trashcan == nil then + -- get respective trash + local trash = guidReferenceApi.getObjectByOwnerAndType(COLORS[i], "Trash") + if trash == nil then printToAll("Trashcan for " .. COLORS[i] .. " playmat could not be found!", "Red") return 1 end - for _, entry in ipairs(findObjects(i)) do - local obj = entry.hit_object - local desc_low = string.lower(obj.getDescription()) + local objList + if i < 5 then + objList = playmatApi.searchAroundPlaymat(COLORS[i]) + else + objList = searchMythosArea() + end + for _, obj in ipairs(objList) do -- ignore these elements - if not tableContains(IGNORE_GUIDS, obj.getGUID()) and obj.hasTag(IGNORE_TAG) == false and - desc_low ~= "chaos bag" and desc_low ~= "action token" then + if obj.hasTag(IGNORE_TAG) == false + and obj.hasTag("ActionToken") == false + and obj.hasTag("chaosBag") == false + and checkMemo(obj) == false then coroutine.yield(0) - trashcan.putObject(obj) + trash.putObject(obj) - -- flip action tokens back to ready - elseif desc_low == "action token" and obj.is_face_down then + -- flip action tokens back to ready + elseif obj.hasTag("ActionToken") == false and obj.is_face_down then obj.flip() end end -- reset "activeInvestigatorId" if i < 5 then - local playermat = getObjectFromGUID(PLAYERMAT_GUIDS[i]) + local playermat = guidReferenceApi.getObjectByOwnerAndType(COLORS[i], "Playermat") if playermat then playermat.setVar("activeInvestigatorId", "00000") end @@ -386,7 +310,7 @@ function tidyPlayerMatCoroutine() end end - local datahelper = getObjectFromGUID("708279") + local datahelper = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper") if datahelper then datahelper.setTable("SPAWNED_PLAYER_CARD_GUIDS", {}) end @@ -399,23 +323,31 @@ end -- helper functions --------------------------------------------------------- --- find objects depending on index (1 to 4 for playermats, 5 for agenda/act playmat, 6 for campaign log) -function findObjects(num) - return Physics.cast({ +-- find objects in the mythos area +function searchMythosArea() + local searchResult = Physics.cast({ direction = { 0, 1, 0 }, max_distance = 1, type = 3, - size = PHYSICS_SCALE[num], - origin = PHYSICS_POSITION[num], - orientation = { 0, PHYSICS_ROTATION[num], 0 }, + size = { 55, 1, 13.5 }, + origin = { -2, 2, 10 }, + orientation = { 0, 270, 0 }, debug = false }) + + local objList = {} + for _, v in ipairs(searchResult) do + table.insert(objList, v.hit_object) + end + return objList end --- search a table for a value, return true if found (else returns false) -function tableContains(table, value) - for _, v in ipairs(table) do - if v == value then +-- checks if the object is owned by a playermat or the mythos, returns boolean +function checkMemo(obj) + local memo = obj.getMemo() + if memo then + local decoded = JSON.decode(memo) or {} + if decoded.matColor then return true end end diff --git a/src/accessories/TokenArranger.ttslua b/src/accessories/TokenArranger.ttslua index fb836480..2be2c7cd 100644 --- a/src/accessories/TokenArranger.ttslua +++ b/src/accessories/TokenArranger.ttslua @@ -312,6 +312,10 @@ function layout(_, _, isRightClick) local value = tonumber(objData.Nickname) local precedence = tokenPrecedence[objData.Nickname] + -- remove GUID to avoid issues for high latency clients + objData["GUID"] = nil + + -- store data with value / precendence data[i] = { token = objData, value = value or precedence[1] diff --git a/src/accessories/TokenArrangerApi.ttslua b/src/accessories/TokenArrangerApi.ttslua index 47afb77f..75d44839 100644 --- a/src/accessories/TokenArrangerApi.ttslua +++ b/src/accessories/TokenArrangerApi.ttslua @@ -1,11 +1,12 @@ do local TokenArrangerApi = {} + local guidReferenceApi = require("core/GUIDReferenceApi") -- local function to call the token arranger, if it is on the table ---@param functionName String Name of the function to cal ---@param argument Variant Parameter to pass local function callIfExistent(functionName, argument) - local tokenArranger = getObjectsWithTag("TokenArranger")[1] + local tokenArranger = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TokenArranger") if tokenArranger ~= nil then tokenArranger.call(functionName, argument) end diff --git a/src/arkhamdb/ArkhamDb.ttslua b/src/arkhamdb/ArkhamDb.ttslua index 0b8c9621..eb3dcf41 100644 --- a/src/arkhamdb/ArkhamDb.ttslua +++ b/src/arkhamdb/ArkhamDb.ttslua @@ -198,7 +198,7 @@ do local altArt = { front = "normal", back = "normal" } -- translating front ID - if altFrontId > 90000 and altFrontId < 90047 then + if altFrontId > 90000 and altFrontId < 90050 then altArt.front = "parallel" elseif altFrontId > 01500 and altFrontId < 01506 then altArt.front = "revised" @@ -207,7 +207,7 @@ do end -- translating back ID - if altBackId > 90000 and altBackId < 90047 then + if altBackId > 90000 and altBackId < 90050 then altArt.back = "parallel" elseif altBackId > 01500 and altBackId < 01506 then altArt.back = "revised" @@ -295,7 +295,12 @@ do local card = allCardsBagApi.getCardById(cardId) if (card ~= nil and card.metadata.bonded ~= nil) then for _, bond in ipairs(card.metadata.bonded) do - bondedCards[bond.id] = bond.count + -- add a bonded card for each copy of the parent card (except for Pendant of the Queen) + if bond.id == "06022" then + bondedCards[bond.id] = bond.count + else + bondedCards[bond.id] = bond.count * cardCount + end -- We need to know which cards are bonded to determine their position, remember them bondedList[bond.id] = true -- Also adding taboo versions of bonded cards to the list diff --git a/src/arkhamdb/DeckImporterApi.ttslua b/src/arkhamdb/DeckImporterApi.ttslua index 3250bf97..8628b6c4 100644 --- a/src/arkhamdb/DeckImporterApi.ttslua +++ b/src/arkhamdb/DeckImporterApi.ttslua @@ -1,7 +1,11 @@ do local DeckImporterApi = {} - local DECK_IMPORTER_GUID = "a28140" - + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getDeckImporter() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "DeckImporter") + end + -- Returns a table with the full state of the UI, including options and deck IDs. -- This can be used to persist via onSave(), or provide values for a load operation -- Table values: @@ -14,7 +18,7 @@ do -- investigators: True if investigator cards should be spawned DeckImporterApi.getUiState = function() local passthroughTable = {} - for k,v in pairs(getObjectFromGUID(DECK_IMPORTER_GUID).call("getUiState")) do + for k,v in pairs(getDeckImporter().call("getUiState")) do passthroughTable[k] = v end return passthroughTable @@ -31,7 +35,7 @@ do -- loadNewest: True if the most upgraded version of the deck should be loaded -- investigators: True if investigator cards should be spawned DeckImporterApi.setUiState = function(uiStateTable) - return getObjectFromGUID(DECK_IMPORTER_GUID).call("setUiState", uiStateTable) + return getDeckImporter().call("setUiState", uiStateTable) end return DeckImporterApi diff --git a/src/arkhamdb/DeckImporterMain.ttslua b/src/arkhamdb/DeckImporterMain.ttslua index a258be00..b839bfb3 100644 --- a/src/arkhamdb/DeckImporterMain.ttslua +++ b/src/arkhamdb/DeckImporterMain.ttslua @@ -88,6 +88,7 @@ function loadCards(slots, investigatorId, bondedList, customizations, playerColo handleAncestralKnowledge(cardsToSpawn) handleUnderworldMarket(cardsToSpawn, playerColor) handleHunchDeck(investigatorId, cardsToSpawn, playerColor) + handleSpiritDeck(investigatorId, cardsToSpawn, playerColor) handleCustomizableUpgrades(cardsToSpawn, customizations) handlePeteSignatureAssets(investigatorId, cardsToSpawn) @@ -322,6 +323,48 @@ function handleHunchDeck(investigatorId, cardList, playerColor) end end +-- If the investigator is Parallel Jim Culver, extract all Ally assets to SetAside5 to build the Spirit +-- Deck. +---@param investigatorId String ID for the deck's investigator card. Passed separately because the +--- investigator may not be included in the cardList +---@param cardList Table Deck list being created +---@param playerColor String Color this deck is being loaded for +function handleSpiritDeck(investigatorId, cardList, playerColor) + if investigatorId == "02004-p" or investigatorId == "02004-pb" then -- Parallel Jim Culver + local spiritList = {} + for i, card in ipairs(cardList) do + if card.metadata.id == "90053" or ( + card.metadata.type == "Asset" + and card.metadata.traits ~= nil + and string.match(card.metadata.traits, "Ally") + and card.metadata.level ~= nil + and card.metadata.level < 3) then + table.insert(spiritList, i) + end + end + -- Process allies to move them to the spirit deck. This is done in reverse + -- order because the sorting needs to be reversed (deck sorts for face down) + -- Performance here may be an issue, as table.remove() is an O(n) operation + -- which makes the full shift O(n^2). But keep it simple unless it becomes + -- a problem + for i = #spiritList, 1, -1 do + local moving = cardList[spiritList[i]] + moving.zone = "SetAside5" + table.remove(cardList, spiritList[i]) + table.insert(cardList, moving) + end + if #spiritList < 10 then + printToAll("Jim's spirit deck must have 9 Ally assets but the deck only has " .. (#spiritList - 1) .. + " Ally assets.", playerColor) + elseif #spiritList > 11 then + printToAll("Moved all " .. (#spiritList - 1) .. + " Ally assets to the spirit deck, reduce it to 10 (including Vengeful Shade).", playerColor) + else + printToAll("Built Jim's spirit deck", playerColor) + end + end +end + -- For any customization upgrade cards in the card list, process the metadata from the deck to -- set the save state to show the correct checkboxes/text field values ---@param cardList Table Deck list being created diff --git a/src/arkhamdb/DeckImporterUi.ttslua b/src/arkhamdb/DeckImporterUi.ttslua index 10470d0e..892c087b 100644 --- a/src/arkhamdb/DeckImporterUi.ttslua +++ b/src/arkhamdb/DeckImporterUi.ttslua @@ -58,36 +58,9 @@ end -- loadNewest: True if the most upgraded version of the deck should be loaded -- investigators: True if investigator cards should be spawned function setUiState(uiStateTable) - -- Callback functions aren't triggered when editing buttons/inputs so values must be set manually - - if uiStateTable["greenDeck"] then - greenDeckId = uiStateTable["greenDeck"] - self.editInput({index=0, value=greenDeckId}) - end - if uiStateTable["redDeck"] then - redDeckId = uiStateTable["redDeck"] - self.editInput({index=1, value=redDeckId}) - end - if uiStateTable["whiteDeck"] then - whiteDeckId = uiStateTable["whiteDeck"] - self.editInput({index=2, value=whiteDeckId}) - end - if uiStateTable["orangeDeck"]then - orangeDeckId = uiStateTable["orangeDeck"] - self.editInput({index=3, value=orangeDeckId}) - end - if uiStateTable["private"] then - privateDeck = uiStateTable["private"] - self.editButton { index = 0, label = PRIVATE_TOGGLE_LABELS[privateDeck] } - end - if uiStateTable["loadNewest"] then - loadNewestDeck = uiStateTable["loadNewest"] - self.editButton { index = 1, label = UPGRADED_TOGGLE_LABELS[loadNewestDeck] } - end - if uiStateTable["investigators"] then - loadInvestigators = uiStateTable["investigators"] - self.editButton { index = 2, label = LOAD_INVESTIGATOR_TOGGLE_LABELS[loadInvestigators] } - end + self.clearButtons() + self.clearInputs() + initializeUi(uiStateTable) end -- Sets up the UI for the deck loader, populating fields from the given save state table decoded from onLoad() diff --git a/src/chaosbag/BlessCurseManagerApi.ttslua b/src/chaosbag/BlessCurseManagerApi.ttslua index 694e98b2..7612eae5 100644 --- a/src/chaosbag/BlessCurseManagerApi.ttslua +++ b/src/chaosbag/BlessCurseManagerApi.ttslua @@ -1,10 +1,14 @@ do local BlessCurseManagerApi = {} - local MANAGER_GUID = "5933fb" + 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 = getObjectFromGUID(MANAGER_GUID) + 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) @@ -12,30 +16,30 @@ do -- updates the internal count (called by cards that seal bless/curse tokens) BlessCurseManagerApi.sealedToken = function(type, guid) - getObjectFromGUID(MANAGER_GUID).call("sealedToken", { type = type, guid = guid }) + getManager().call("sealedToken", { type = type, guid = guid }) end -- updates the internal count (called by cards that seal bless/curse tokens) BlessCurseManagerApi.releasedToken = function(type, guid) - getObjectFromGUID(MANAGER_GUID).call("releasedToken", { type = type, guid = guid }) + getManager().call("releasedToken", { 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) - getObjectFromGUID(MANAGER_GUID).call("broadcastStatus", 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) - getObjectFromGUID(MANAGER_GUID).call("doRemove", playerColor) + getManager().call("doRemove", playerColor) end -- adds Wendy's menu to the hovered card (allows sealing of tokens) ---@param color String Color of the player to show the broadcast to BlessCurseManagerApi.addWendysMenu = function(playerColor, hoveredObject) - getObjectFromGUID(MANAGER_GUID).call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject }) + getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject }) end return BlessCurseManagerApi diff --git a/src/core/DoomCounter.ttslua b/src/core/DoomCounter.ttslua index 9d7efd2b..ae649189 100644 --- a/src/core/DoomCounter.ttslua +++ b/src/core/DoomCounter.ttslua @@ -1,3 +1,5 @@ +local guidReferenceApi = require("core/GUIDReferenceApi") + local optionsVisible = false local options = { Agenda = true, @@ -64,10 +66,9 @@ function startReset() if options.Agenda then updateVal(0) end - -- call the "Doom-in-Play"-counter - local DoomInPlayCounter = getObjectFromGUID("652ff3") - if DoomInPlayCounter then - DoomInPlayCounter.call("removeDoom", options) + local doomInPlayCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomInPlayCounter") + if doomInPlayCounter then + doomInPlayCounter.call("removeDoom", options) end end diff --git a/src/core/DoomInPlayCounter.ttslua b/src/core/DoomInPlayCounter.ttslua index d0509a9a..b844939a 100644 --- a/src/core/DoomInPlayCounter.ttslua +++ b/src/core/DoomInPlayCounter.ttslua @@ -1,25 +1,24 @@ --- common parameters -local castParameters = {} -castParameters.direction = { 0, 1, 0 } -castParameters.type = 3 -castParameters.max_distance = 0 +local guidReferenceApi = require("core/GUIDReferenceApi") +local playmatApi = require("playermat/PlaymatApi") -local zone +local ZONE, TRASH, loopID local doomURL = "https://i.imgur.com/EoL7yaZ.png" local IGNORE_TAG = "DoomCounter_ignore" - --- playermats 1 to 4 -local originAndSize = { - { origin = { -55, 1.6, 16.5 }, size = { 12, 1, 25 } }, - { origin = { -55, 1.6, -16.5 }, size = { 12, 1, 25 } }, - { origin = { -25, 1.6, 27 }, size = { 25, 1, 12 } }, - { origin = { -25, 1.6, -27 }, size = { 25, 1, 12 } } +local TOTAL_PLAY_AREA = { + upperLeft = { + x = -10, + z = -35 + }, + lowerRight = { + x = -60, + z = 35 + } } -- create button, context menu and start loop function onLoad() self.createButton({ - label = tostring(0), + label = "0", click_function = "none", function_owner = self, position = { 0, 0.06, 0 }, @@ -31,76 +30,65 @@ function onLoad() color = { 0, 0, 0, 0 } }) - zone = getObjectFromGUID("a2f932") + TRASH = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash") + ZONE = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayAreaZone") loopID = Wait.time(countDoom, 2, -1) end -- main function function countDoom() - local doom = 0 - for i = 1, 5 do doom = doom + search(i) end - self.editButton({ index = 0, label = tostring(doom) }) + local count = 0 + + -- get doom in play + for _, obj in ipairs(getObjects()) do + count = count + getDoomAmount(obj) + end + + self.editButton({ index = 0, label = tostring(count) }) end --- searches playermats (num = 1-4) or the scripting zone (num = 5) -function search(num) - local val = 0 - if num == 5 then - for _, obj in ipairs(zone.getObjects()) do - val = val + isDoom(obj) - end +-- gets quantity (for stacks) of doom +function getDoomAmount(obj) + if (obj.is_face_down and obj.getCustomObject().image_bottom == doomURL) + and not obj.hasTag(IGNORE_TAG) + and inArea(obj.getPosition(), TOTAL_PLAY_AREA) then + return math.abs(obj.getQuantity()) else - castParameters.origin = originAndSize[num].origin - castParameters.size = originAndSize[num].size - - for _, obj in ipairs(Physics.cast(castParameters)) do - val = val + isDoom(obj.hit_object) - end + return 0 end - return val -end - --- checks an object for the doom image and gets quantity (for stacks) -function isDoom(obj) - if (obj.is_face_down and obj.getCustomObject().image_bottom == doomURL) or - (obj.name == "Custom_Token" and obj.getCustomObject().image == doomURL) then - if not obj.hasTag(IGNORE_TAG) then - return math.abs(obj.getQuantity()) - end - end - return 0 end -- removes doom from playermats / playarea function removeDoom(options) - local trashCan = getObjectFromGUID("70b9f6") local count = 0 - if options.Playermats then - for i = 1, 4 do - castParameters.origin = originAndSize[i].origin - castParameters.size = originAndSize[i].size - for _, obj in ipairs(Physics.cast(castParameters)) do - local obj = obj.hit_object - local amount = isDoom(obj) - if amount > 0 then - trashCan.putObject(obj) - count = count + amount - end - end - end + if options.Playermats then + count = removeDoomFromList(playmatApi.searchAroundPlaymat("All")) broadcastToAll(count .. " doom removed from Playermats.", "White") end - local count = 0 if options.Playarea then - for _, obj in ipairs(zone.getObjects()) do - local amount = isDoom(obj) - if amount > 0 then - trashCan.putObject(obj) - count = count + amount - end - end - broadcastToAll(count .. " doom removed from Playarea.", "White") + count = removeDoomFromList(ZONE.getObjects()) + broadcastToAll(count .. " doom removed from Playerarea.", "White") end end + +-- removes doom from provided object list and returns the removed amount +function removeDoomFromList(objList) + local count = 0 + for _, obj in ipairs(objList) do + local amount = getDoomAmount(obj) + if amount > 0 then + TRASH.putObject(obj) + count = count + amount + end + end + return count +end + +function inArea(point, bounds) + return (point.x < bounds.upperLeft.x + and point.x > bounds.lowerRight.x + and point.z > bounds.upperLeft.z + and point.z < bounds.lowerRight.z) +end diff --git a/src/core/GUIDReferenceApi.ttslua b/src/core/GUIDReferenceApi.ttslua new file mode 100644 index 00000000..3728eb09 --- /dev/null +++ b/src/core/GUIDReferenceApi.ttslua @@ -0,0 +1,28 @@ +do + local GUIDReferenceApi = {} + + local function getGuidHandler() + return getObjectFromGUID("123456") + end + + -- returns all matching objects as a table with references + ---@param owner String Parent object for this search + ---@param type String Type of object to search for + 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 + 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 + GUIDReferenceApi.getObjectsByOwner = function(owner) + return getGuidHandler().call("getObjectsByOwner", owner) + end + + return GUIDReferenceApi +end diff --git a/src/core/GUIDReferenceHandler.ttslua b/src/core/GUIDReferenceHandler.ttslua new file mode 100644 index 00000000..53b3c7d9 --- /dev/null +++ b/src/core/GUIDReferenceHandler.ttslua @@ -0,0 +1,101 @@ +local GuidReferences = { + White = { + ClueCounter = "d86b7c", + ClickableClueCounter = "db85d6", + DamageCounter = "eb08d6", + HandZone = "a70eee", + HorrorCounter = "468e88", + InvestigatorSkillTracker = "e598c2", + Playermat = "8b081b", + ResourceCounter = "4406f0", + Trash = "147e80" + }, + Orange = { + ClueCounter = "1769ed", + ClickableClueCounter = "3f22e5", + DamageCounter = "e64eec", + HandZone = "5fe087", + HorrorCounter = "0257d9", + InvestigatorSkillTracker = "b4a5f7", + Playermat = "bd0ff4", + ResourceCounter = "816d84", + Trash = "f7b6c8" + }, + Green = { + ClueCounter = "032300", + ClickableClueCounter = "891403", + DamageCounter = "1f5a0a", + HandZone = "0285cc", + HorrorCounter = "7b5729", + InvestigatorSkillTracker = "af7ed7", + Playermat = "383d8b", + ResourceCounter = "cd15ac", + Trash = "5f896a" + }, + Red = { + ClueCounter = "37be78", + ClickableClueCounter = "4111de", + DamageCounter = "591a45", + HandZone = "be2f17", + HorrorCounter = "beb964", + InvestigatorSkillTracker = "e74881", + Playermat = "0840d5", + ResourceCounter = "a4b60d", + Trash = "4b8594" + }, + Mythos = { + AllCardsBag = "15bb07", + BlessCurseManager = "5933fb", + CampaignThePathToCarcosa = "aca04c", + DataHelper = "708279", + DeckImporter = "a28140", + DoomCounter = "85c4c6", + DoomInPlayCounter = "652ff3", + InvestigatorCounter = "f182ee", + MasterClueCounter = "4a3aa4", + MythosArea = "9f334f", + NavigationOverlayHandler = "797ede", + OptionPanelSource = "830bd0", + PlaceholderBoxDummy = "a93466", + PlayArea = "721ba2", + PlayAreaZone = "a2f932", + PlayerCardPanel = "2d30ee", + ResourceTokenBag = "9fadf9", + RulesReference = "d99993", + SoundCube = "3c988f", + TokenArranger = "022907", + TokenSource = "124381", + TokenSpawnTracker = "e3ffc9", + TourStarter = "0e5aa8", + Trash = "70b9f6", + VictoryDisplay = "6ccd6d" + } +} + +function getObjectByOwnerAndType(params) + local owner = params.owner or "Mythos" + local type = params.type + return getObjectFromGUID(GuidReferences[owner][type]) +end + +function getObjectsByType(type) + local objList = {} + for owner, objects in pairs(GuidReferences) do + local obj = getObjectFromGUID(objects[type]) + if obj then + objList[owner] = obj + end + end + return objList +end + +function getObjectsByOwner(owner) + local objList = {} + for type, guid in pairs(GuidReferences[owner]) do + local obj = getObjectFromGUID(guid) + if obj then + objList[type] = obj + end + end + return objList +end diff --git a/src/core/GameKeyHandler.ttslua b/src/core/GameKeyHandler.ttslua index 192defd7..5fb80afd 100644 --- a/src/core/GameKeyHandler.ttslua +++ b/src/core/GameKeyHandler.ttslua @@ -1,4 +1,5 @@ local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") +local guidReferenceApi = require("core/GUIDReferenceApi") local optionPanelApi = require("core/OptionPanelApi") local playmatApi = require("playermat/PlaymatApi") local victoryDisplayApi = require("core/VictoryDisplayApi") @@ -41,7 +42,8 @@ end -- adds 1 doom to the agenda function addDoomToAgenda() - getObjectFromGUID("85c4c6").call("addVal", 1) + local doomCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomCounter") + doomCounter.call("addVal", 1) end -- moves the hovered card to the victory display @@ -105,7 +107,7 @@ function takeClueFromLocation(playerColor, hoveredObject) local pos = nil if clickableClues then pos = {x = 0.49, y = 2.66, z = 0.00} - playmatApi.updateClueClicker(playerColor, playmatApi.getClueCount(clickableClues, playerColor) + 1) + playmatApi.updateCounter(matColor, "ClickableClueCounter", _, 1) else pos = playmatApi.transformLocalPosition({x = -1.12, y = 0.05, z = 0.7}, matColor) end diff --git a/src/core/Global.ttslua b/src/core/Global.ttslua index 39394941..92e088bf 100644 --- a/src/core/Global.ttslua +++ b/src/core/Global.ttslua @@ -1,4 +1,5 @@ local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") +local guidReferenceApi = require("core/GUIDReferenceApi") local mythosAreaApi = require("core/MythosAreaApi") local navigationOverlayApi = require("core/NavigationOverlayApi") local playAreaApi = require("core/PlayAreaApi") @@ -12,11 +13,8 @@ local tokenManager = require("core/token/TokenManager") -- general setup --------------------------------------------------------- -ENCOUNTER_DECK_POS = {-3.93, 1, 5.76} -ENCOUNTER_DECK_DISCARD_POSITION = {-3.85, 1, 10.38} - --- GUID of data helper -DATA_HELPER_GUID = "708279" +ENCOUNTER_DECK_POS = { -3.93, 1, 5.76 } +ENCOUNTER_DECK_DISCARD_POSITION = { -3.85, 1, 10.38 } -- GUIDs that will not be interactable (e.g. parts of the table) local NOT_INTERACTABLE = { @@ -37,14 +35,28 @@ chaosTokens = {} local chaosTokensLastMat = nil local bagSearchers = {} -local MAT_COLORS = {"White", "Orange", "Green", "Red"} +local MAT_COLORS = { "White", "Orange", "Green", "Red" } local hideTitleSplashWaitFunctionId = nil -- online functionality related variables local MOD_VERSION = "3.3.0" local SOURCE_REPO = 'https://raw.githubusercontent.com/chr1z93/loadable-objects/main' -local library, requestObj, modMeta, notificationVisible +local library, requestObj, modMeta local acknowledgedUpgradeVersions = {} +local contentToShow = "campaigns" +local currentListItem = 1 +local xmlVisibility = { + downloadWindow = false, + optionPanel = false, + updateNotification = false +} +local tabIdTable = { + tab1 = "campaigns", + tab2 = "scenarios", + tab3 = "fanmadeCampaigns", + tab4 = "fanmadeScenarios", + tab5 = "fanmadePlayerCards" +} -- optionPanel data optionPanel = {} @@ -101,14 +113,6 @@ ID_URL_MAP = { -- data for chaos token stat tracker --------------------------------------------------------- -local MAT_GUID_TO_COLOR = { - ["Overall"] = "Overall", - ["8b081b"] = "White", - ["bd0ff4"] = "Orange", - ["383d8b"] = "Green", - ["0840d5"] = "Red" -} - local tokenDrawingStats = { ["Overall"] = {}, ["8b081b"] = {}, @@ -122,7 +126,12 @@ local tokenDrawingStats = { --------------------------------------------------------- -- saving state of optionPanel to restore later -function onSave() return JSON.encode({ optionPanel = optionPanel, acknowledgedUpgradeVersions = acknowledgedUpgradeVersions }) end +function onSave() + return JSON.encode({ + optionPanel = optionPanel, + acknowledgedUpgradeVersions = acknowledgedUpgradeVersions + }) +end function onLoad(savedData) if savedData then @@ -142,6 +151,11 @@ function onLoad(savedData) resetChaosTokenStatTracker() getModVersion() math.randomseed(os.time()) + + -- initialization of loadable objects library (delay to let Navigation Overlay build) + Wait.time(function() + WebRequest.get(SOURCE_REPO .. '/library.json', libraryDownloadCallback) + end, 1) end -- Event hook for any object search. When chaos tokens are manipulated while the chaos bag @@ -289,8 +303,8 @@ function handleStatTrackerClick(_, _, isRightClick) if isRightClick then resetChaosTokenStatTracker() else - local squidKing = "Nobody" - local maxSquid = 0 + local squidKing = "Nobody" + local maxSquid = 0 local foundAnyStats = false for key, personalStats in pairs(tokenDrawingStats) do @@ -300,7 +314,9 @@ function handleStatTrackerClick(_, _, isRightClick) playerColor = "White" playerName = "Overall" else - playerColor = playmatApi.getPlayerColor(MAT_GUID_TO_COLOR[key]) + -- get mat color + local matColor = playmatApi.getMatColorByPosition(getObjectFromGUID(key).getPosition()) + playerColor = playmatApi.getPlayerColor(matColor) playerName = Player[playerColor].steam_name or playerColor local playerSquidCount = personalStats["Auto-fail"] @@ -320,8 +336,8 @@ function handleStatTrackerClick(_, _, isRightClick) if totalCount > 0 then foundAnyStats = true printToAll("------------------------------") - printToAll(playerName .. " Stats", playerColor) - + printToAll(playerName .. " Stats", playerColor) + for tokenName, value in pairs(personalStats) do if value ~= 0 then printToAll(tokenName .. ': ' .. tostring(value)) @@ -334,7 +350,7 @@ function handleStatTrackerClick(_, _, isRightClick) -- detect if any player drew tokens if foundAnyStats then printToAll("------------------------------") - printToAll(squidKing .. " is an auto-fail magnet.", {255, 0, 0}) + printToAll(squidKing .. " is an auto-fail magnet.", { 255, 0, 0 }) else printToAll("No tokens have been drawn yet.", "Yellow") end @@ -363,11 +379,11 @@ function createSetupButtons(args) if data ~= nil then local buttonParameters = {} buttonParameters.function_owner = args.object - buttonParameters.position = {0, 0.1, -0.15} - buttonParameters.scale = {0.47, 1, 0.47} + buttonParameters.position = { 0, 0.1, -0.15 } + buttonParameters.scale = { 0.47, 1, 0.47 } buttonParameters.height = 200 buttonParameters.width = 1150 - buttonParameters.color = {0.87, 0.8, 0.7} + buttonParameters.color = { 0.87, 0.8, 0.7 } if data.easy ~= nil then buttonParameters.label = "Easy" @@ -450,7 +466,8 @@ function fillContainer(args) end function getDataValue(storage, key) - local data = getObjectFromGUID(DATA_HELPER_GUID).getTable(storage) + local DATA_HELPER = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper") + local data = DATA_HELPER.getTable(storage) if data ~= nil then local value = data[key] if value ~= nil then @@ -495,7 +512,6 @@ function getChaosBagState() end return tokens - end -- respawns the chaos bag with a new state of tokens @@ -524,7 +540,7 @@ function setChaosBagState(tokenList) -- overwrite chaos bag content and respawn it chaosbagData.ContainedObjects = containedObjects chaosbag.destruct() - spawnObjectData({data = chaosbagData}) + spawnObjectData({ data = chaosbagData }) -- remove tokens that are still in play for _, token in pairs(chaosTokens) do @@ -553,7 +569,7 @@ function spawnChaosToken(id) type = 'Custom_Tile', position = { 0.49, 3, 0 }, scale = { 0.81, 1.0, 0.81 }, - rotation = {0, 270, 0}, + rotation = { 0, 270, 0 }, callback_function = function(obj) obj.setName(ID_URL_MAP[id].name) chaosbag.putObject(obj) @@ -603,7 +619,7 @@ function emptyChaosBag() local chaosbag = findChaosBag() for _, object in ipairs(chaosbag.getObjects()) do - chaosbag.takeObject({callback_function = function(item) item.destruct() end}) + chaosbag.takeObject({ callback_function = function(item) item.destruct() end }) end end @@ -619,176 +635,410 @@ end -- Content Importing and XML functions --------------------------------------------------------- -function onClick_refreshList() - local request = WebRequest.get(SOURCE_REPO .. '/library.json', completed_list_update) - requestObj = request - startLuaCoroutine(Global, 'downloadCoroutine') +-- forwards the requested content type to the update function and sets highlight to clicked tab +---@param tabId String Id of the clicked tab +function onClick_tab(_, _, tabId) + for listId, listContent in pairs(tabIdTable) do + if listId == tabId then + UI.setClass(listId, 'downloadTab activeTab') + contentToShow = listContent + else + UI.setClass(listId, 'downloadTab') + end + end + currentListItem = 1 + updateDownloadItemList() end -function onClick_select(player, params) - params = JSON.decode(urldecode(params)) +-- click function for the items in the download window +-- updates backgroundcolor for row panel and fontcolor for list item +function onClick_select(_, _, identificationKey) + UI.setAttribute("panel" .. currentListItem, "color", "clear") + UI.setAttribute(contentToShow .. "_" .. currentListItem, "color", "white") + + -- parses the identification key (contentToShow_currentListItem) + if identificationKey then + contentToShow = nil + currentListItem = nil + for str in string.gmatch(identificationKey, "([^_]+)") do + if not contentToShow then + -- grab the first part to know the content type + contentToShow = str + else + -- get the index + currentListItem = tonumber(str) + break + end + end + end + + UI.setAttribute("panel" .. currentListItem, "color", "grey") + UI.setAttribute(contentToShow .. "_" .. currentListItem, "color", "black") + updatePreviewWindow() +end + +-- click function for the download button in the preview window +function onClick_download() + placeholder_download(library[contentToShow][currentListItem]) +end + +-- the download button on the placeholder objects calls this to directly initiate a download +---@param param Table contains url and guid of replacement object +function placeholder_download(params) local url = SOURCE_REPO .. '/' .. params.url - local request = WebRequest.get(url, function (request) complete_obj_download(request, params) end ) - requestObj = request + requestObj = WebRequest.get(url, function (request) contentDownloadCallback(request, params) end) startLuaCoroutine(Global, 'downloadCoroutine') end -function onClick_load() - UI.show('progress_display') - UI.hide('load_button') +function downloadCoroutine() + -- show progress bar + UI.setAttribute('download_progress', 'active', true) + + -- update progress bar + while requestObj do + UI.setAttribute('download_progress', 'percentage', requestObj.download_progress * 100) + coroutine.yield(0) + end + UI.setAttribute('download_progress', 'percentage', 100) + + -- wait 30 frames + for i = 1, 30 do + coroutine.yield(0) + end + + -- hide progress bar + UI.setAttribute('download_progress', 'active', false) + + -- hide download window + if xmlVisibility.downloadWindow then + xmlVisibility.downloadWindow = false + UI.hide('downloadWindow') + end + return 1 end +-- spawns a bag that contains every object from the library +function onClick_downloadAll() + broadcastToAll("Download initiated - this will take a few minutes!") + + -- hide download window + if xmlVisibility.downloadWindow then + xmlVisibility.downloadWindow = false + UI.hide('downloadWindow') + end + + startLuaCoroutine(Global, "coroutineDownloadAll") +end + +function coroutineDownloadAll() + local JSON = [[ + { + "Name": "Bag", + "Transform": { + "posX": -39.5, + "posY": 2, + "posZ": -87, + "rotX": 0, + "rotY": 270, + "rotZ": 0, + "scaleX": 1.0, + "scaleY": 1.0, + "scaleZ": 1.0 + }, + "Nickname": "All Downloadable Content", + "Bag": { + "Order": 0 + }, + "ContainedObjects": [ + ]] + + local contained = "" + local downloadedItems = 0 + local skippedItems = 0 + + -- loop through the library to add content + for contentType, objectList in pairs(library) do + broadcastToAll("Downloading " .. contentType .. "...") + for _, params in ipairs(objectList) do + local request = WebRequest.get(SOURCE_REPO .. '/' .. params.url) + local start = os.time() + while true do + if request.is_done then + contained = contained .. request.text .. "," + downloadedItems = downloadedItems + 1 + break + -- time-out if item can't be loaded in 5s + elseif request.is_error or (os.time() - start) > 5 then + skippedItems = skippedItems + 1 + break + end + coroutine.yield(0) + end + end + end + + JSON = JSON .. contained .. "]}" + spawnObjectJSON({json = JSON}) + + broadcastToAll(downloadedItems .. " objects downloaded.", "Green") + broadcastToAll(skippedItems .. " objects had a time-out / error.", "Orange") + return 1 +end + +-- spawns a placeholder box for the selected object +function onClick_spawnPlaceholder() + -- get object references + local item = library[contentToShow][currentListItem] + local dummy = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlaceholderBoxDummy") + + -- error handling + if not item.boxsize or item.boxsize == "" or not item.boxart or item.boxart == "" then + print("Error loading object.") + return + end + + -- get data for placeholder + local spawnPos = {-39.5, 2, -87} + + local meshTable = { + big = "https://raw.githubusercontent.com/RobMayer/TTSLibrary/master/advboxes/core_h_MSH.obj", + small = "https://raw.githubusercontent.com/RobMayer/TTSLibrary/master/advboxes/tuckbox_h_MSH.obj", + wide = "http://pastebin.com/raw.php?i=uWAmuNZ2" + } + + local scaleTable = { + big = {1.00, 0.14, 1.00}, + small = {2.21, 0.46, 2.42}, + wide = {2.00, 0.11, 1.69} + } + + local placeholder = spawnObject({ + type = "Custom_Model", + position = spawnPos, + rotation = {0, 270, 0}, + scale = scaleTable[item.boxsize], + }) + + placeholder.setCustomObject({ + mesh = meshTable[item.boxsize], + diffuse = item.boxart, + material = 3 + }) + + placeholder.setColorTint({1, 1, 1, 71/255}) + placeholder.setName(item.name) + placeholder.setDescription("by " .. (item.author or "Unknown")) + placeholder.setGMNotes(item.url) + placeholder.setLuaScript(dummy.getLuaScript()) + Player.getPlayers()[1].pingTable(spawnPos) + + -- hide download window + if xmlVisibility.downloadWindow then + xmlVisibility.downloadWindow = false + UI.hide('downloadWindow') + end +end + +-- toggles the visibility of the respective UI +---@param player LuaPlayer Player that triggered this +---@param title String Name of the UI to toggle function onClick_toggleUi(player, title) if title == "Navigation Overlay" then navigationOverlayApi.cycleVisibility(player.color) return end - UI.hide('optionPanel') - UI.hide('load_ui') - - -- when same button is clicked or close window button is pressed, don't open UI - if UI.getValue('title') ~= title and title ~= 'Hidden' then - UI.setValue('title', title) - - if title == "Options" then - UI.show('optionPanel') - else - update_window_content(title) - UI.show('load_ui') - end + if xmlVisibility[title] then + -- small delay to allow button click sounds to play + Wait.time(function() UI.hide(title) end, 0.1) else - UI.setValue('title', "Hidden") + UI.show(title) + end + xmlVisibility[title] = not xmlVisibility[title] +end + +-- updates the preview window +function updatePreviewWindow() + local item = library[contentToShow][currentListItem] + local tempImage = "http://cloud-3.steamusercontent.com/ugc/2115061845788345842/2CD6ABC551555CCF58F9D0DDB7620197BA398B06/" + + -- set default image if not defined + if item.boxsize == nil or item.boxsize == "" or item.boxart == nil or item.boxart == "" then + item.boxsize = "big" + item.boxart = "http://cloud-3.steamusercontent.com/ugc/762723517667628371/18438B0A0045038A7099648AA3346DFCAA267C66/" + end + + UI.setValue("previewTitle", item.name) + UI.setValue("previewAuthor", "by " .. (item.author or "- Author not found -")) + UI.setValue("previewDescription", item.description or "- Description not found -") + + -- update mask according to size (hardcoded values to align image in mask) + local maskData = {} + if item.boxsize == "big" then + maskData = { + image = "box-cover-mask-big", + width = "870", + height = "435", + offsetXY = "154 60" + } + elseif item.boxsize == "small" then + maskData = { + image = "box-cover-mask-small", + width = "792", + height = "594", + offsetXY = "135 13" + } + elseif item.boxsize == "wide" then + maskData = { + image = "box-cover-mask-wide", + width = "756", + height = "630", + offsetXY = "-190 -70" + } + end + + -- loading empty image as placeholder until real image is loaded + UI.setAttribute("previewArtImage", "image", tempImage) + + -- insert the image itself + UI.setAttribute("previewArtImage", "image", item.boxart) + UI.setAttributes("previewArtMask", maskData) +end + +-- formats the json response from the webrequest into a key-value lua table +-- strips the prefix from the community content items +function formatLibrary(json_response) + library = {} + library["campaigns"] = json_response.campaigns + library["scenarios"] = json_response.scenarios + library["extras"] = json_response.extras + library["fanmadeCampaigns"] = {} + library["fanmadeScenarios"] = {} + library["fanmadePlayerCards"] = {} + + for _, item in ipairs(json_response.community) do + local identifier = nil + for str in string.gmatch(item.name, "([^:]+)") do + if not identifier then + -- grab the first part to know the content type + identifier = str + else + -- update the name without the content type + item.name = str + break + end + end + + if identifier == "Fan Investigators" then + table.insert(library["fanmadePlayerCards"], item) + elseif identifier == "Fan Campaign" then + table.insert(library["fanmadeCampaigns"], item) + elseif identifier == "Fan Scenario" then + table.insert(library["fanmadeScenarios"], item) + end end end -function downloadCoroutine() - while requestObj do - UI.setAttribute('download_progress', 'percentage', requestObj.download_progress * 100) - coroutine.yield(0) - end - return 1 -end +-- updates the window content to the requested content +function updateDownloadItemList() + if not library then return end -function update_list(objects) - local ui = UI.getXmlTable() - local update_height = find_tag_with_id(ui, 'ui_update_height') - local update_children = find_tag_with_id(update_height.children, 'ui_update_point') + -- addition of list items according to library file + local globalXml = UI.getXmlTable() + local contentList = getXmlTableElementById(globalXml, 'contentList') - update_children.children = {} - - for _, v in ipairs(objects) do - local s = JSON.encode(v); - table.insert(update_children.children, - { tag = 'Text', - value = v.name, - attributes = { onClick = 'onClick_select(' .. urlencode(JSON.encode(v)) .. ')', alignment = 'MiddleLeft' } + contentList.children = {} + for i, v in ipairs(library[contentToShow]) do + table.insert(contentList.children, + { + tag = "Panel", + attributes = { id = "panel" .. i }, + children = { + tag = 'Text', + value = v.name, + attributes = { + id = contentToShow .. "_" .. i, + onClick = 'onClick_select', + alignment = 'MiddleLeft' + } + } }) end - update_height.attributes.height = #(update_children.children) * 24 - UI.setXmlTable(ui) + contentList.attributes.height = #contentList.children * 27 + UI.setXmlTable(globalXml) + + -- select the first item + Wait.time(onClick_select, 0.2) end -function update_window_content(new_title) - if not library then return end +-- called after the webrequest of downloading an item +-- deletes the placeholder and spawns the downloaded item +function contentDownloadCallback(request, params) + requestObj = nil - if new_title == 'Campaigns' then - update_list(library.campaigns) - elseif new_title == 'Standalone Scenarios' then - update_list(library.scenarios) - elseif new_title == 'Investigators' then - update_list(library.investigators) - elseif new_title == 'Community Content' then - update_list(library.community) - elseif new_title == 'Extras' then - update_list(library.extras) - else - update_list({}) - end -end - -function complete_obj_download(request, params) - assert(request.is_done) + -- error handling if request.is_error or request.response_code ~= 200 then - print('error: ' .. request.error) - else - if pcall(function() - local replaced_object - pcall(function() - if params.replace then - replaced_object = getObjectFromGUID(params.replace) - end - end) - local json = request.text - if replaced_object then - local pos = replaced_object.getPosition() - local rot = replaced_object.getRotation() - destroyObject(replaced_object) - Wait.frames(function() - spawnObjectJSON({json = json, position = pos, rotation = rot}) - end, 1) - else - spawnObjectJSON({json = json}) - end - end) then - print('Object loaded.') - else - print('Error loading object.') + print('Error: ' .. request.error) + return + end + + -- initiate content spawning + local spawnTable = { json = request.text } + if params.replace then + local replacedObject = getObjectFromGUID(params.replace) + if replacedObject then + spawnTable.position = replacedObject.getPosition() + spawnTable.rotation = replacedObject.getRotation() + spawnTable.scale = replacedObject.getScale() + destroyObject(replacedObject) end end - requestObj = nil - UI.setAttribute('download_progress', 'percentage', 100) -end - --- the download button on the placeholder objects calls this to directly initiate a download --- params is a table with url and guid of replacement object, which happens to match what onClick_select wants -function placeholder_download(params) - onClick_select(nil, JSON.encode(params)) -end - -function completed_list_update(request) - assert(request.is_done) - if request.is_error or request.response_code ~= 200 then - print('error: ' .. request.error) - else - local json_response = nil - if pcall(function () json_response = JSON.decode(request.text) end) then - library = json_response - update_window_content(UI.getValue('title')) - else - print('error parsing downloaded library') + -- if spawned from menu, ping the position + if params.name then + spawnTable["callback_function"] = function(obj) + Player.getPlayers()[1].pingTable(obj.getPosition()) end end - requestObj = nil - UI.setAttribute('download_progress', 'percentage', 100) + if pcall(function() spawnObjectJSON(spawnTable) end) then + print('Object loaded.') + else + print('Error loading object.') + end end -function find_tag_with_id(ui, id) +-- downloading of the library file +function libraryDownloadCallback(request) + if request.is_error or request.response_code ~= 200 then + print('error: ' .. request.error) + return + end + + local json_response = nil + if pcall(function () json_response = JSON.decode(request.text) end) then + formatLibrary(json_response) + updateDownloadItemList() + else + print('error parsing downloaded library') + end +end + +-- loops through an XML table and returns the specified object +---@param ui Table XmlTable (get this via getXmlTable) +---@param id String Id of the object to return +function getXmlTableElementById(ui, id) for _, obj in ipairs(ui) do if obj.attributes and obj.attributes.id and obj.attributes.id == id then return obj end if obj.children then - local result = find_tag_with_id(obj.children, id) + local result = getXmlTableElementById(obj.children, id) if result then return result end end end return nil end -function urlencode(str) - local str = string.gsub(str, "([^A-Za-z0-9-_.~])", - function (c) return string.format("%%%02X", string.byte(c)) end) - return str -end - -function urldecode(str) - local str = string.gsub(str, "%%(%x%x)", - function (h) return string.char(tonumber(h, 16)) end) - return str -end - --------------------------------------------------------- -- Option Panel related functionality --------------------------------------------------------- @@ -875,7 +1125,8 @@ function applyOptionPanelChange(id, state) optionPanel[id] = state -- update master clue counter - getObjectFromGUID("4a3aa4").setVar("useClickableCounters", state) + local counter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "MasterClueCounter") + counter.setVar("useClickableCounters", state) -- option: Play area snap tags elseif id == "playAreaSnapTags" then @@ -944,8 +1195,9 @@ end -- copies the specified tool (by name) from the option panel source bag ---@param name String Name of the object that should be copied ---@param position Table Desired position of the object +---@param rotation Table Desired rotation of the object (defaults to object's rotation) function spawnHelperObject(name, position, rotation) - local sourceBag = getObjectFromGUID("830bd0") + local sourceBag = guidReferenceApi.getObjectByOwnerAndType("Mythos","OptionPanelSource") -- error handling for missing sourceBag if not sourceBag then @@ -953,7 +1205,7 @@ function spawnHelperObject(name, position, rotation) return end - local spawnTable = {position = position} + local spawnTable = { position = position } -- only overrride rotation if there is one provided (object's rotation used instead) if rotation then @@ -1046,7 +1298,6 @@ end -- splash scenario title on setup function titleSplash(scenarioName) if optionPanel['showTitleSplash'] then - -- if there's any ongoing title being displayed, hide it and cancel the waiting function if hideTitleSplashWaitFunctionId then Wait.stop(hideTitleSplashWaitFunctionId) @@ -1088,7 +1339,7 @@ function compareVersion(request) -- stop here if on latest version if MOD_VERSION == modMeta["latestVersion"] then return end - + -- stop here if "don't show again" was clicked for this version before if acknowledgedUpgradeVersions[modMeta["latestVersion"]] then return end @@ -1110,25 +1361,14 @@ function updateNotificationLoading() highlightText = highlightText .. "\n• " .. entry end end - + -- update the XML UI - UI.setValue("notificationHeader", "New version available: ".. modMeta["latestVersion"]) + UI.setValue("notificationHeader", "New version available: " .. modMeta["latestVersion"]) UI.setValue("releaseHighlightText", highlightText) UI.setAttribute("highlightRow", "preferredHeight", 20*#highlights) UI.setAttribute("updateNotification", "height", 20*#highlights + 125) end --- triggered by clicking on the Finn Icon -function onClick_FinnIcon() - if notificationVisible then - UI.hide("updateNotification") - notificationVisible = false - else - UI.show("updateNotification") - notificationVisible = true - end -end - -- close / don't show again buttons on the update notification function onClick_notification(_, parameter) if parameter == "dontShowAgain" then @@ -1137,4 +1377,5 @@ function onClick_notification(_, parameter) end UI.hide("FinnIcon") UI.hide("updateNotification") + xmlVisibility["updateNotification"] = false end diff --git a/src/core/MasterClueCounter.ttslua b/src/core/MasterClueCounter.ttslua index 29bdef48..9b1d837e 100644 --- a/src/core/MasterClueCounter.ttslua +++ b/src/core/MasterClueCounter.ttslua @@ -20,11 +20,10 @@ function onLoad(savedData) width = 900, scale = { 1.5, 1.5, 1.5 }, font_size = 650, - font_color = { 0, 0, 0, 100 }, + font_color = { 1, 1, 1, 100 }, color = { 0, 0, 0, 0 } }) - - loopID = Wait.time(sumClues, 2, -1) + Wait.time(sumClues, 2, -1) end -- removes all player clues by calling the respective function from the counting bowls / clickers diff --git a/src/core/MythosArea.ttslua b/src/core/MythosArea.ttslua index 46b4fda7..fada0072 100644 --- a/src/core/MythosArea.ttslua +++ b/src/core/MythosArea.ttslua @@ -1,3 +1,4 @@ +local guidReferenceApi = require("core/GUIDReferenceApi") local playAreaApi = require("core/PlayAreaApi") local tokenArrangerApi = require("accessories/TokenArrangerApi") local tokenChecker = require("core/token/TokenChecker") @@ -20,11 +21,8 @@ local isReshuffling = false -- scenario metadata local currentScenario, useFrontData, tokenData --- GUID of data helper -local DATA_HELPER_GUID = "708279" - -local TRASHCAN -local TRASHCAN_GUID = "70b9f6" +-- object references +local TRASH, DATA_HELPER -- we use this to turn off collision handling until onLoad() is complete local collisionEnabled = false @@ -36,7 +34,8 @@ function onLoad(saveState) useFrontData = loadedState.useFrontData or true tokenData = loadedState.tokenData or {} end - TRASHCAN = getObjectFromGUID(TRASHCAN_GUID) + TRASH = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash") + DATA_HELPER = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper") collisionEnabled = true end @@ -153,7 +152,7 @@ function actualEncounterCardDraw(card, params) local faceUpRotation = 0 if not params.alwaysFaceUp then local metadata = JSON.decode(card.getGMNotes()) or {} - if metadata.hidden or getObjectFromGUID(DATA_HELPER_GUID).call('checkHiddenCard', card.getName()) then + if metadata.hidden or DATA_HELPER.call('checkHiddenCard', card.getName()) then faceUpRotation = 180 end end @@ -209,7 +208,7 @@ function removeTokensFromObject(object) obj.memo ~= nil and obj.getLock() == false and not tokenChecker.isChaosToken(obj) then - TRASHCAN.putObject(obj) + TRASH.putObject(obj) end end end diff --git a/src/core/MythosAreaApi.ttslua b/src/core/MythosAreaApi.ttslua index 4eeeb693..08caeb1d 100644 --- a/src/core/MythosAreaApi.ttslua +++ b/src/core/MythosAreaApi.ttslua @@ -1,15 +1,19 @@ do local MythosAreaApi = {} - local MYTHOS_AREA_GUID = "9f334f" + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getMythosArea() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "MythosArea") + end -- returns the chaos token metadata (if provided through scenario reference card) MythosAreaApi.returnTokenData = function() - return getObjectFromGUID(MYTHOS_AREA_GUID).call("returnTokenData") + return getMythosArea().call("returnTokenData") end - + -- draw an encounter card to the requested position/rotation MythosAreaApi.drawEncounterCard = function(pos, rotY, alwaysFaceUp) - getObjectFromGUID(MYTHOS_AREA_GUID).call("drawEncounterCard", { + getMythosArea().call("drawEncounterCard", { pos = pos, rotY = rotY, alwaysFaceUp = alwaysFaceUp diff --git a/src/core/NavigationOverlayApi.ttslua b/src/core/NavigationOverlayApi.ttslua index 2a462807..a39b0879 100644 --- a/src/core/NavigationOverlayApi.ttslua +++ b/src/core/NavigationOverlayApi.ttslua @@ -1,21 +1,25 @@ do local NavigationOverlayApi = {} - local HANDLER_GUID = "797ede" + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getNOHandler() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "NavigationOverlayHandler") + end -- Copies the visibility for the Navigation overlay ---@param startColor String Color of the player to copy from ---@param targetColor String Color of the targeted player NavigationOverlayApi.copyVisibility = function(startColor, targetColor) - getObjectFromGUID(HANDLER_GUID).call("copyVisibility", { + getNOHandler().call("copyVisibility", { startColor = startColor, targetColor = targetColor }) - end + end -- Changes the Navigation Overlay view ("Full View" --> "Play Areas" --> "Closed" etc.) ---@param playerColor String Color of the player to update the visibility for NavigationOverlayApi.cycleVisibility = function(playerColor) - getObjectFromGUID(HANDLER_GUID).call("cycleVisibility", playerColor) + getNOHandler().call("cycleVisibility", playerColor) end return NavigationOverlayApi diff --git a/src/core/NavigationOverlayHandler.ttslua b/src/core/NavigationOverlayHandler.ttslua index 9aea6188..77fdc345 100644 --- a/src/core/NavigationOverlayHandler.ttslua +++ b/src/core/NavigationOverlayHandler.ttslua @@ -328,7 +328,7 @@ function loadCamera(player, index) end -- search on the playmat for objects - local bounds = getDynamicViewBounds(playmatApi.searchPlaymat(matColor)) + local bounds = getDynamicViewBounds(playmatApi.searchAroundPlaymat(matColor)) lookHere = { position = { bounds.middleX, 0, bounds.middleZ }, diff --git a/src/core/PlayArea.ttslua b/src/core/PlayArea.ttslua index 69cbba2c..453de993 100644 --- a/src/core/PlayArea.ttslua +++ b/src/core/PlayArea.ttslua @@ -3,60 +3,60 @@ --------------------------------------------------------- -- Location connection directional options -local BIDIRECTIONAL = 0 -local ONE_WAY = 1 -local INCOMING_ONE_WAY = 2 +local BIDIRECTIONAL = 0 +local ONE_WAY = 1 +local INCOMING_ONE_WAY = 2 -- Connector draw parameters -local CONNECTION_THICKNESS = 0.015 +local CONNECTION_THICKNESS = 0.015 local DRAGGING_CONNECTION_THICKNESS = 0.15 -local DRAGGING_CONNECTION_COLOR = { 0.8, 0.8, 0.8, 1 } -local CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 } -local DIRECTIONAL_ARROW_DISTANCE = 3.5 -local ARROW_ARM_LENGTH = 0.9 -local ARROW_ANGLE = 25 +local DRAGGING_CONNECTION_COLOR = { 0.8, 0.8, 0.8, 1 } +local CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 } +local DIRECTIONAL_ARROW_DISTANCE = 3.5 +local ARROW_ARM_LENGTH = 0.9 +local ARROW_ANGLE = 25 -- Height to draw the connector lines, places them just above the table and always below cards -local CONNECTION_LINE_Y = 1.529 +local CONNECTION_LINE_Y = 1.529 -- we use this to turn off collision handling until onLoad() is complete -local collisionEnabled = false +local collisionEnabled = false -- used for recreating the link to a custom data helper after image change -customDataHelper = nil +customDataHelper = nil -local DEFAULT_URL = "http://cloud-3.steamusercontent.com/ugc/998015670465071049/FFAE162920D67CF38045EFBD3B85AD0F916147B2/" +local DEFAULT_URL = +"http://cloud-3.steamusercontent.com/ugc/998015670465071049/FFAE162920D67CF38045EFBD3B85AD0F916147B2/" -local SHIFT_OFFSETS = { +local SHIFT_OFFSETS = { left = { x = 0.00, y = 0, z = 7.67 }, right = { x = 0.00, y = 0, z = -7.67 }, up = { x = 6.54, y = 0, z = 0.00 }, down = { x = -6.54, y = 0, z = 0.00 } } -local SHIFT_EXCLUSION = { +local SHIFT_EXCLUSION = { ["b7b45b"] = true, ["f182ee"] = true, ["721ba2"] = true } -local LOC_LINK_EXCLUDE_SCENARIOS = { +local LOC_LINK_EXCLUDE_SCENARIOS = { ["The Witching Hour"] = true, ["The Heart of Madness"] = true } -local tokenManager = require("core/token/TokenManager") -local INVESTIGATOR_COUNTER_GUID = "f182ee" -local PLAY_AREA_ZONE_GUID = "a2f932" +local guidReferenceApi = require("core/GUIDReferenceApi") +local tokenManager = require("core/token/TokenManager") -local clueData = {} -local spawnedLocationGUIDs = {} -local locations = {} -local locationConnections = {} -local draggingGuids = {} +local clueData = {} +local spawnedLocationGUIDs = {} +local locations = {} +local locationConnections = {} +local draggingGuids = {} local locationData local currentScenario -local missingData = {} -local countedVP = {} +local missingData = {} +local countedVP = {} --------------------------------------------------------- -- general code @@ -71,8 +71,8 @@ end function onLoad(saveState) -- records locations we have spawned clues for - local save = JSON.decode(saveState) or { } - locations = save.trackedLocations or { } + local save = JSON.decode(saveState) or {} + locations = save.trackedLocations or {} currentScenario = save.currentScenario self.interactable = false @@ -93,13 +93,13 @@ function updateSurface(newURL) local customInfo = self.getCustomObject() if newURL ~= "" and newURL ~= nil and newURL ~= DEFAULT_URL then - customInfo.image = newURL - broadcastToAll("New Playmat Image Applied", { 0.2, 0.9, 0.2 }) + customInfo.image = newURL + broadcastToAll("New Playmat Image Applied", { 0.2, 0.9, 0.2 }) else - customInfo.image = DEFAULT_URL - broadcastToAll("Default Playmat Image Applied", { 0.2, 0.9, 0.2 }) + customInfo.image = DEFAULT_URL + broadcastToAll("Default Playmat Image Applied", { 0.2, 0.9, 0.2 }) end - + self.setCustomObject(customInfo) local guid = nil @@ -108,7 +108,7 @@ function updateSurface(newURL) self.reload() if guid ~= nil then - Wait.time(function() updateLocations({ guid }) end, 1) + Wait.time(function() updateLocations({ guid }) end, 1) end end @@ -129,12 +129,14 @@ function onCollisionEnter(collisionInfo) if shouldSpawnTokens(card) then tokenManager.spawnForCard(card) end + -- If this card was being dragged, clear the dragging connections. A multi-drag/drop may send -- the dropped card immediately into a deck, so this has to be done here if draggingGuids[card.getGUID()] ~= nil then card.setVectorLines(nil) draggingGuids[card.getGUID()] = nil end + maybeTrackLocation(card) end @@ -167,20 +169,20 @@ function onObjectPickUp(player, object) -- should be tracking if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then local pickedUpGuid = object.getGUID() - local metadata = JSON.decode(object.getGMNotes()) or { } + local metadata = JSON.decode(object.getGMNotes()) or {} if metadata.type == "Location" then -- onCollisionExit sometimes comes 1 frame after onObjectPickUp (rather than before it or in -- the same frame). This causes a mismatch in the data between dragging the on-table, and -- that one frame draws connectors on the card which then show up as shadows for snap points. -- Waiting ensures we always do thing in the expected Exit->PickUp order Wait.frames(function() - if object.is_face_down then - draggingGuids[pickedUpGuid] = metadata.locationBack - else - draggingGuids[pickedUpGuid] = metadata.locationFront - end - rebuildConnectionList() - end, 2) + if object.is_face_down then + draggingGuids[pickedUpGuid] = metadata.locationBack + else + draggingGuids[pickedUpGuid] = metadata.locationFront + end + rebuildConnectionList() + end, 2) end end end @@ -273,11 +275,11 @@ end -- drawBaseConnections() function rebuildConnectionList() if not showLocationLinks() then - locationConnections = { } + locationConnections = {} return end - local iconCardList = { } + local iconCardList = {} -- Build a list of cards with each icon as their location ID for cardId, metadata in pairs(draggingGuids) do @@ -288,7 +290,7 @@ function rebuildConnectionList() end -- Pair up all the icons - locationConnections = { } + locationConnections = {} for cardId, metadata in pairs(draggingGuids) do buildConnection(cardId, iconCardList, metadata) end @@ -307,7 +309,7 @@ function buildLocListByIcon(cardId, iconCardList, locData) if locData ~= nil and locData.icons ~= nil then for icon in string.gmatch(locData.icons, "%a+") do if iconCardList[icon] == nil then - iconCardList[icon] = { } + iconCardList[icon] = {} end table.insert(iconCardList[icon], cardId) end @@ -321,19 +323,19 @@ end ---@param locData Table A table containing the metadata for the card (for the correct side) function buildConnection(cardId, iconCardList, locData) if locData ~= nil and locData.connections ~= nil then - locationConnections[cardId] = { } + locationConnections[cardId] = {} for icon in string.gmatch(locData.connections, "%a+") do if iconCardList[icon] ~= nil then for _, connectedGuid in ipairs(iconCardList[icon]) do -- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way if locationConnections[connectedGuid] ~= nil and (locationConnections[connectedGuid][cardId] == ONE_WAY - or locationConnections[connectedGuid][cardId] == BIDIRECTIONAL) then + or locationConnections[connectedGuid][cardId] == BIDIRECTIONAL) then locationConnections[connectedGuid][cardId] = BIDIRECTIONAL locationConnections[cardId][connectedGuid] = nil else if locationConnections[connectedGuid] == nil then - locationConnections[connectedGuid] = { } + locationConnections[connectedGuid] = {} end locationConnections[cardId][connectedGuid] = ONE_WAY locationConnections[connectedGuid][cardId] = INCOMING_ONE_WAY @@ -348,10 +350,10 @@ end -- Constructed vectors will be set to the playmat function drawBaseConnections() if not showLocationLinks() then - locationConnections = { } + locationConnections = {} return end - local cardConnectionLines = { } + local cardConnectionLines = {} for originGuid, targetGuids in pairs(locationConnections) do -- Objects should reliably exist at this point, but since this can be called during onUpdate the @@ -380,8 +382,8 @@ function drawDraggingConnections() if not showLocationLinks() then return end - local cardConnectionLines = { } - local ownedVectors = { } + local cardConnectionLines = {} + local ownedVectors = {} for originGuid, _ in pairs(draggingGuids) do targetGuids = locationConnections[originGuid] @@ -389,7 +391,7 @@ function drawDraggingConnections() -- object checks are conservative just to make sure. local origin = getObjectFromGUID(originGuid) if draggingGuids[originGuid] and origin ~= nil and targetGuids ~= nil then - ownedVectors[originGuid] = { } + ownedVectors[originGuid] = {} for targetGuid, direction in pairs(targetGuids) do local target = getObjectFromGUID(targetGuid) if target != nil then @@ -427,9 +429,9 @@ function addBidirectionalVector(card1, card2, vectorOwner, lines) local pos2 = vectorOwner.positionToLocal(cardPos2) table.insert(lines, { - points = { pos1, pos2 }, - color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR, - thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS, + points = { pos1, pos2 }, + color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR, + thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS, }) end @@ -451,11 +453,13 @@ function addOneWayVector(origin, target, vectorOwner, lines) -- Calculate card distance to be closer for horizontal positions than vertical, since cards are -- taller than they are wide local heading = Vector(originPos):sub(targetPos):heading("y") - local distanceFromCard = DIRECTIONAL_ARROW_DISTANCE * 0.7 + DIRECTIONAL_ARROW_DISTANCE * 0.3 * math.abs(math.sin(math.rad(heading))) + local distanceFromCard = DIRECTIONAL_ARROW_DISTANCE * 0.7 + + DIRECTIONAL_ARROW_DISTANCE * 0.3 * math.abs(math.sin(math.rad(heading))) -- Calculate the three possible arrow positions. These are offset by half the arrow length to -- make them visually balanced by keeping the arrows centered, not tracking the point - local midpoint = Vector(originPos):add(targetPos):scale(Vector(0.5, 0.5, 0.5)):moveTowards(targetPos, ARROW_ARM_LENGTH / 2) + local midpoint = Vector(originPos):add(targetPos):scale(Vector(0.5, 0.5, 0.5)):moveTowards(targetPos, + ARROW_ARM_LENGTH / 2) local closeToOrigin = Vector(originPos):moveTowards(targetPos, distanceFromCard + ARROW_ARM_LENGTH / 2) local closeToTarget = Vector(targetPos):moveTowards(originPos, distanceFromCard - ARROW_ARM_LENGTH / 2) @@ -474,14 +478,16 @@ end --- positioning and scaling, as well as highlighting connections during a drag operation ---@param lines Table List of vector line elements. Mutable, will be updated to add this arrow function addArrowLines(arrowheadPos, originPos, vectorOwner, lines) - local arrowArm1 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", -1 * ARROW_ANGLE):add(arrowheadPos) - local arrowArm2 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", ARROW_ANGLE):add(arrowheadPos) + local arrowArm1 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", + -1 * ARROW_ANGLE):add(arrowheadPos) + local arrowArm2 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", + ARROW_ANGLE):add(arrowheadPos) local head = vectorOwner.positionToLocal(arrowheadPos) local arm1 = vectorOwner.positionToLocal(arrowArm1) local arm2 = vectorOwner.positionToLocal(arrowArm2) table.insert(lines, { - points = { arm1, head, arm2}, + points = { arm1, head, arm2 }, color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR, thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS, }) @@ -508,7 +514,7 @@ function shiftContentsRight(playerColor) end function shiftContents(playerColor, direction) - local zone = getObjectFromGUID(PLAY_AREA_ZONE_GUID) + local zone = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayAreaZone") if not zone then broadcastToColor("Scripting zone couldn't be found.", playerColor, "Red") return @@ -530,8 +536,8 @@ function isInPlayArea(object) local bounds = self.getBounds() local position = object.getPosition() -- Corners are arbitrary since it's all global - c1 goes down both axes, c2 goes up - local c1 = { x = bounds.center.x - bounds.size.x / 2, z = bounds.center.z - bounds.size.z / 2} - local c2 = { x = bounds.center.x + bounds.size.x / 2, z = bounds.center.z + bounds.size.z / 2} + local c1 = { x = bounds.center.x - bounds.size.x / 2, z = bounds.center.z - bounds.size.z / 2 } + local c2 = { x = bounds.center.x + bounds.size.x / 2, z = bounds.center.z + bounds.size.z / 2 } return position.x > c1.x and position.x < c2.x and position.z > c1.z and position.z < c2.z end @@ -582,7 +588,7 @@ function countVP() local cardVP = tonumber(metadata.victory) or 0 if cardVP ~= 0 and not cardHasClues(cardId) then totalVP = totalVP + cardVP - if cardVP >0 then + if cardVP > 0 then table.insert(countedVP, getObjectFromGUID(cardId)) end end @@ -651,8 +657,8 @@ end -- rebuilds local snap points (could be useful in the future again) function buildSnaps() - local upperleft = { x = 1.53, z = -1.09} - local lowerright = {x = -1.53, z = 1.55} + local upperleft = { x = 1.53, z = -1.09 } + local lowerright = { x = -1.53, z = 1.55 } local snaps = {} -- creates 81 snap points, for uneven rows + columns it makes a rotation snap point @@ -666,7 +672,7 @@ function buildSnaps() -- enable rotation snaps for uneven rows / columns if (i % 2 ~= 0) and (j % 2 ~= 0) then - snap.rotation = {0, 0, 0} + snap.rotation = { 0, 0, 0 } snap.rotation_snap = true end @@ -678,6 +684,6 @@ end -- utility function function round(num, numDecimalPlaces) - local mult = 10^(numDecimalPlaces or 0) + local mult = 10 ^ (numDecimalPlaces or 0) return math.floor(num * mult + 0.5) / mult end diff --git a/src/core/PlayAreaApi.ttslua b/src/core/PlayAreaApi.ttslua index ad6a5b7a..f1281b40 100644 --- a/src/core/PlayAreaApi.ttslua +++ b/src/core/PlayAreaApi.ttslua @@ -1,105 +1,109 @@ do - local PlayAreaApi = { } - local PLAY_AREA_GUID = "721ba2" - local INVESTIGATOR_COUNTER_GUID = "f182ee" + local PlayAreaApi = {} + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getPlayArea() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayArea") + end + + local function getInvestigatorCounter() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "InvestigatorCounter") + end -- Returns the current value of the investigator counter from the playmat ---@return Integer. Number of investigators currently set on the counter PlayAreaApi.getInvestigatorCount = function() - return getObjectFromGUID(INVESTIGATOR_COUNTER_GUID).getVar("val") + return getInvestigatorCounter().getVar("val") end -- Updates the current value of the investigator counter from the playmat ---@param count Number of investigators to set on the counter PlayAreaApi.setInvestigatorCount = function(count) - return getObjectFromGUID(INVESTIGATOR_COUNTER_GUID).call("updateVal", count) + getInvestigatorCounter().call("updateVal", count) end -- Move all contents on the play area (cards, tokens, etc) one slot in the given direction. Certain - -- fixed objects will be ignored, as will anything the player has tagged with - -- 'displacement_excluded' - ---@param playerColor Color of the player requesting the shift. Used solely to send an error - --- message in the unlikely case that the scripting zone has been deleted + -- fixed objects will be ignored, as will anything the player has tagged with 'displacement_excluded' + ---@param playerColor Color Color of the player requesting the shift for messages PlayAreaApi.shiftContentsUp = function(playerColor) - return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsUp", playerColor) + return getPlayArea().call("shiftContentsUp", playerColor) end PlayAreaApi.shiftContentsDown = function(playerColor) - return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsDown", playerColor) + return getPlayArea().call("shiftContentsDown", playerColor) end PlayAreaApi.shiftContentsLeft = function(playerColor) - return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsLeft", playerColor) + return getPlayArea().call("shiftContentsLeft", playerColor) end PlayAreaApi.shiftContentsRight = function(playerColor) - return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsRight", playerColor) + return getPlayArea().call("shiftContentsRight", playerColor) end -- Reset the play area's tracking of which cards have had tokens spawned. PlayAreaApi.resetSpawnedCards = function() - return getObjectFromGUID(PLAY_AREA_GUID).call("resetSpawnedCards") + return getPlayArea().call("resetSpawnedCards") end -- Event to be called when the current scenario has changed. ---@param scenarioName Name of the new scenario PlayAreaApi.onScenarioChanged = function(scenarioName) - getObjectFromGUID(PLAY_AREA_GUID).call("onScenarioChanged", scenarioName) + getPlayArea().call("onScenarioChanged", scenarioName) end -- Sets this playmat's snap points to limit snapping to locations or not. -- If matchTypes is false, snap points will be reset to snap all cards. ---@param matchTypes Boolean Whether snap points should only snap for the matching card types. PlayAreaApi.setLimitSnapsByType = function(matchCardTypes) - getObjectFromGUID(PLAY_AREA_GUID).call("setLimitSnapsByType", matchCardTypes) + getPlayArea().call("setLimitSnapsByType", matchCardTypes) end -- Receiver for the Global tryObjectEnterContainer event. Used to clear vector lines from dragged -- cards before they're destroyed by entering the container PlayAreaApi.tryObjectEnterContainer = function(container, object) - getObjectFromGUID(PLAY_AREA_GUID).call("tryObjectEnterContainer", - { container = container, object = object }) + getPlayArea().call("tryObjectEnterContainer", { container = container, object = object }) end -- counts the VP on locations in the play area PlayAreaApi.countVP = function() - return getObjectFromGUID(PLAY_AREA_GUID).call("countVP") + return getPlayArea().call("countVP") end -- highlights all locations in the play area without metadata ---@param state Boolean True if highlighting should be enabled PlayAreaApi.highlightMissingData = function(state) - return getObjectFromGUID(PLAY_AREA_GUID).call("highlightMissingData", state) + return getPlayArea().call("highlightMissingData", state) end -- highlights all locations in the play area with VP ---@param state Boolean True if highlighting should be enabled PlayAreaApi.highlightCountedVP = function(state) - return getObjectFromGUID(PLAY_AREA_GUID).call("highlightCountedVP", state) + return getPlayArea().call("highlightCountedVP", state) end -- Checks if an object is in the play area (returns true or false) PlayAreaApi.isInPlayArea = function(object) - return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object) + return getPlayArea().call("isInPlayArea", object) end PlayAreaApi.getSurface = function() - return getObjectFromGUID(PLAY_AREA_GUID).getCustomObject().image + return getPlayArea().getCustomObject().image end PlayAreaApi.updateSurface = function(url) - return getObjectFromGUID(PLAY_AREA_GUID).call("updateSurface", url) + return getPlayArea().call("updateSurface", url) end -- Called by Custom Data Helpers to push their location data into the Data Helper. This adds the -- data to the local token manager instance. ---@param args Table Single-value array holding the GUID of the Custom Data Helper making the call PlayAreaApi.updateLocations = function(args) - getObjectFromGUID(PLAY_AREA_GUID).call("updateLocations", args) + getPlayArea().call("updateLocations", args) end PlayAreaApi.getCustomDataHelper = function() - return getObjectFromGUID(PLAY_AREA_GUID).getVar("customDataHelper") + return getPlayArea().getVar("customDataHelper") end return PlayAreaApi diff --git a/src/core/SoundCubeApi.ttslua b/src/core/SoundCubeApi.ttslua index de97f117..8aec117b 100644 --- a/src/core/SoundCubeApi.ttslua +++ b/src/core/SoundCubeApi.ttslua @@ -1,5 +1,6 @@ do local SoundCubeApi = {} + local guidReferenceApi = require("core/GUIDReferenceApi") -- this table links the name of a trigger effect to its index local soundIndices = { @@ -9,7 +10,8 @@ do } local function playTriggerEffect(index) - getObjectsWithTag("SoundCube")[1].AssetBundle.playTriggerEffect(index) + local SoundCube = guidReferenceApi.getObjectByOwnerAndType("Mythos", "SoundCube") + SoundCube.AssetBundle.playTriggerEffect(index) end -- plays the by name requested sound diff --git a/src/core/VictoryDisplay.ttslua b/src/core/VictoryDisplay.ttslua index bf8a5502..c4c95a0e 100644 --- a/src/core/VictoryDisplay.ttslua +++ b/src/core/VictoryDisplay.ttslua @@ -1,4 +1,5 @@ local chaosBagApi = require("chaosbag/ChaosBagApi") +local guidReferenceApi = require("core/GUIDReferenceApi") local playAreaApi = require("core/PlayAreaApi") local tokenChecker = require("core/token/TokenChecker") @@ -10,13 +11,8 @@ local countedVP = {} local highlightMissing = false local highlightCounted = false -local TRASHCAN -local TRASHCAN_GUID = "70b9f6" - -- button creation when loading the game function onLoad() - TRASHCAN = getObjectFromGUID(TRASHCAN_GUID) - -- index 0: VP - "Display" local buttonParameters = {} buttonParameters.label = "0" @@ -236,8 +232,7 @@ end function highlightCountedVP() self.editButton({ index = 4, - tooltip = (highlightCounted and "Enable" or "Disable") .. - " highlighting of cards with VP." + tooltip = (highlightCounted and "Enable" or "Disable") .. " highlighting of cards with VP." }) for _, obj in pairs(countedVP) do if obj ~= nil then @@ -254,6 +249,8 @@ end -- places the provided card in the first empty spot function placeCard(card) + local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash") + -- check snap point states local snaps = self.getSnapPoints() table.sort(snaps, function(a, b) return a.position.x > b.position.x end) @@ -283,7 +280,7 @@ function placeCard(card) local chaosBag = chaosBagApi.findChaosBag() chaosBag.putObject(obj) elseif obj.memo ~= nil and obj.getLock() == false then - TRASHCAN.putObject(obj) + trash.putObject(obj) end end @@ -327,13 +324,3 @@ function checkSnapPointState(pos) origin = pos }) end - --- search a table for a value, return true if found (else returns false) -function tableContains(table, value) - for _, v in ipairs(table) do - if v == value then - return true - end - end - return false -end diff --git a/src/core/VictoryDisplayApi.ttslua b/src/core/VictoryDisplayApi.ttslua index 2401ba43..ae057130 100644 --- a/src/core/VictoryDisplayApi.ttslua +++ b/src/core/VictoryDisplayApi.ttslua @@ -1,18 +1,22 @@ do local VictoryDisplayApi = {} - local VD_GUID = "6ccd6d" + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getVictoryDisplay() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "VictoryDisplay") + end -- triggers an update of the Victory count ---@param delay Number Delay in seconds after which the update call is executed VictoryDisplayApi.update = function(delay) - getObjectFromGUID(VD_GUID).call("startUpdate", delay) + getVictoryDisplay().call("startUpdate", delay) end -- moves a card to the victory display (in the first empty spot) ---@param object Object Object that should be checked and potentially moved VictoryDisplayApi.placeCard = function(object) if object ~= nil and object.tag == "Card" then - getObjectFromGUID(VD_GUID).call("placeCard", object) + getVictoryDisplay().call("placeCard", object) end end diff --git a/src/core/token/TokenManager.ttslua b/src/core/token/TokenManager.ttslua index cee5204d..a5cae52a 100644 --- a/src/core/token/TokenManager.ttslua +++ b/src/core/token/TokenManager.ttslua @@ -1,4 +1,5 @@ do + local guidReferenceApi = require("core/GUIDReferenceApi") local optionPanelApi = require("core/OptionPanelApi") local playAreaApi = require("core/PlayAreaApi") local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") @@ -119,15 +120,10 @@ do ["supply"] = 7 } - -- Source for tokens - local TOKEN_SOURCE_GUID = "124381" - -- Table of data extracted from the token source bag, keyed by the Memo on each token which -- should match the token type keys ("resource", "clue", etc) local tokenTemplates - local DATA_HELPER_GUID = "708279" - local playerCardData local locationData @@ -225,9 +221,11 @@ do -- Copy the offsets to make sure we don't change the static values local baseOffsets = offsets offsets = { } + + -- get a vector for the shifting (downwards local to the card) + local shiftDownVector = Vector(0, 0, shiftDown):rotateOver("y", card.getRotation().y) for i, baseOffset in ipairs(baseOffsets) do - offsets[i] = baseOffset - offsets[i][3] = offsets[i][3] + shiftDown + offsets[i] = baseOffset + shiftDownVector end end @@ -340,8 +338,8 @@ do if tokenTemplates ~= nil then return end - tokenTemplates = { } - local tokenSource = getObjectFromGUID(TOKEN_SOURCE_GUID) + tokenTemplates = {} + local tokenSource = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TokenSource") for _, tokenTemplate in ipairs(tokenSource.getData().ContainedObjects) do local tokenName = tokenTemplate.Memo tokenTemplates[tokenName] = tokenTemplate @@ -353,7 +351,7 @@ do if playerCardData ~= nil then return end - local dataHelper = getObjectFromGUID(DATA_HELPER_GUID) + local dataHelper = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper") playerCardData = dataHelper.getTable('PLAYER_CARD_DATA') locationData = dataHelper.getTable('LOCATIONS_DATA') end @@ -368,18 +366,16 @@ do if uses == nil then return end -- go through tokens to spawn - local type, token, tokenCount + local tokenCount for i, useInfo in ipairs(uses) do - type = useInfo.type - token = useInfo.token - tokenCount = (useInfo.count or 0) - + (useInfo.countPerInvestigator or 0) * playAreaApi.getInvestigatorCount() - if extraUses ~= nil and extraUses[type] ~= nil then - tokenCount = tokenCount + extraUses[type] + tokenCount = (useInfo.count or 0) + (useInfo.countPerInvestigator or 0) * playAreaApi.getInvestigatorCount() + if extraUses ~= nil and extraUses[useInfo.type] ~= nil then + tokenCount = tokenCount + extraUses[useInfo.type] end -- Shift each spawned group after the first down so they don't pile on each other - TokenManager.spawnTokenGroup(card, token, tokenCount, (i - 1) * 0.8, type) + TokenManager.spawnTokenGroup(card, useInfo.token, tokenCount, (i - 1) * 0.8, useInfo.type) end + tokenSpawnTrackerApi.markTokensSpawned(card.getGUID()) end @@ -403,9 +399,8 @@ do ---@param playerData Table Player card data structure retrieved from the DataHelper. Should be -- the right data for this card. internal.spawnPlayerCardTokensFromDataHelper = function(card, playerData) - token = playerData.tokenType - tokenCount = playerData.tokenCount - --log("Spawning data helper tokens for "..card.getName()..'['..card.getDescription()..']: '..tokenCount.."x "..token) + local token = playerData.tokenType + local tokenCount = playerData.tokenCount TokenManager.spawnTokenGroup(card, token, tokenCount) tokenSpawnTrackerApi.markTokensSpawned(card.getGUID()) end @@ -438,7 +433,6 @@ do return 0 end - --log(card.getName() .. ' : ' .. locationData.type .. ' : ' .. locationData.value .. ' : ' .. locationData.clueSide) if ((card.is_face_down and locationData.clueSide == 'back') or (not card.is_face_down and locationData.clueSide == 'front')) then if locationData.type == 'fixed' then diff --git a/src/core/token/TokenSpawnTracker.ttslua b/src/core/token/TokenSpawnTracker.ttslua index c8186937..02aaf415 100644 --- a/src/core/token/TokenSpawnTracker.ttslua +++ b/src/core/token/TokenSpawnTracker.ttslua @@ -1,26 +1,15 @@ -local spawnedCardGuids = { } +local spawnedCardGuids = {} -local HAND_ZONES = { } -HAND_ZONES["a70eee"] = true -- White -HAND_ZONES["0285cc"] = true -- Green -HAND_ZONES["5fe087"] = true -- Orange -HAND_ZONES["be2f17"] = true -- Red +function onSave() return JSON.encode({ cards = spawnedCardGuids }) end function onLoad(saveState) if saveState ~= nil then - local saveTable = JSON.decode(saveState) or { } - spawnedCardGuids = saveTable.cards or { } + local saveTable = JSON.decode(saveState) or {} + spawnedCardGuids = saveTable.cards or {} end - createResetMenuItems() end -function onSave() - return JSON.encode({ - cards = spawnedCardGuids - }) -end - function createResetMenuItems() self.addContextMenuItem("Reset All", resetAll) self.addContextMenuItem("Reset Locations", resetAllLocations) @@ -39,14 +28,20 @@ function resetTokensSpawned(cardGuid) spawnedCardGuids[cardGuid] = nil end -function resetAllAssetAndEvents() - local resetList = { } +function resetAll() spawnedCardGuids = {} end + +function resetAllLocations() resetSpecificTypes("Location") end + +function resetAllAssetAndEvents() resetSpecificTypes("Asset", "Event") end + +function resetSpecificTypes(type1, type2) + local resetList = {} for cardGuid, _ in pairs(spawnedCardGuids) do local card = getObjectFromGUID(cardGuid) if card ~= nil then - local cardMetadata = JSON.decode(card.getGMNotes()) or { } + local cardMetadata = JSON.decode(card.getGMNotes()) or {} -- Check this by type rather than the PlayerCard tag so we don't reset weaknesses - if cardMetadata.type == "Asset" or cardMetadata.type == "Event" then + if cardMetadata.type == type1 or cardMetadata.type == type2 then resetList[cardGuid] = true end end @@ -56,30 +51,9 @@ function resetAllAssetAndEvents() end end -function resetAllLocations() - local resetList = { } - for cardGuid, _ in pairs(spawnedCardGuids) do - local card = getObjectFromGUID(cardGuid) - if card ~= nil then - local cardMetadata = JSON.decode(card.getGMNotes()) or { } - -- Check this by type rather than the PlayerCard tag so we don't reset weaknesses - if cardMetadata.type == "Location" then - resetList[cardGuid] = true - end - end - end - for cardGuid, _ in pairs(resetList) do - spawnedCardGuids[cardGuid] = nil - end -end - -function resetAll() - spawnedCardGuids = { } -end - -- Listener to reset card token spawns when they enter a hand. function onObjectEnterZone(zone, enterObject) - if HAND_ZONES[zone.getGUID()] then + if zone.type == "Hand" and enterObject.type == "Card" then resetTokensSpawned(enterObject.getGUID()) end end diff --git a/src/core/token/TokenSpawnTrackerApi.ttslua b/src/core/token/TokenSpawnTrackerApi.ttslua index dd8d7002..1ba4c261 100644 --- a/src/core/token/TokenSpawnTrackerApi.ttslua +++ b/src/core/token/TokenSpawnTrackerApi.ttslua @@ -1,29 +1,33 @@ do - local TokenSpawnTracker = { } - local SPAWN_TRACKER_GUID = "e3ffc9" + local TokenSpawnTracker = {} + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getSpawnTracker() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "TokenSpawnTracker") + end TokenSpawnTracker.hasSpawnedTokens = function(cardGuid) - return getObjectFromGUID(SPAWN_TRACKER_GUID).call("hasSpawnedTokens", cardGuid) + return getSpawnTracker().call("hasSpawnedTokens", cardGuid) end TokenSpawnTracker.markTokensSpawned = function(cardGuid) - return getObjectFromGUID(SPAWN_TRACKER_GUID).call("markTokensSpawned", cardGuid) + return getSpawnTracker().call("markTokensSpawned", cardGuid) end TokenSpawnTracker.resetTokensSpawned = function(cardGuid) - return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetTokensSpawned", cardGuid) + return getSpawnTracker().call("resetTokensSpawned", cardGuid) end TokenSpawnTracker.resetAllAssetAndEvents = function() - return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetAllAssetAndEvents") + return getSpawnTracker().call("resetAllAssetAndEvents") end TokenSpawnTracker.resetAllLocations = function() - return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetAllLocations") + return getSpawnTracker().call("resetAllLocations") end TokenSpawnTracker.resetAll = function() - return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetAll") + return getSpawnTracker().call("resetAll") end return TokenSpawnTracker diff --git a/src/core/tour/TourManager.ttslua b/src/core/tour/TourManager.ttslua index d0a37183..c27939ee 100644 --- a/src/core/tour/TourManager.ttslua +++ b/src/core/tour/TourManager.ttslua @@ -1,8 +1,9 @@ do require("core/tour/TourScript") require("core/tour/TourCard") - local TourManager = { } - local internal = { } + local TourManager = {} + local internal = {} + local guidReferenceApi = require("core/GUIDReferenceApi") -- Base IDs for various tour card UI elements. Actual IDs will have _[playerColor] appended local CARD_ID = "tourCard" @@ -123,8 +124,9 @@ do delay = delay + 0.5 end local lookPos - if TOUR_SCRIPT[cardIndex].showObj ~= nil then - local lookAtObj = getObjectFromGUID(TOUR_SCRIPT[cardIndex].showObj) + local objReferenceData = TOUR_SCRIPT[cardIndex].objReferenceData + if objReferenceData ~= nil then + local lookAtObj = guidReferenceApi.getObjectByOwnerAndType(objReferenceData.owner, objReferenceData.type) lookPos = lookAtObj.getPosition() lookPos.y = TOUR_SCRIPT[cardIndex].distanceFromObj or 0 -- Since camera isn't directly above the hook, changing the Y affects the visual position of diff --git a/src/core/tour/TourScript.ttslua b/src/core/tour/TourScript.ttslua index 2d92848a..6caf67e7 100644 --- a/src/core/tour/TourScript.ttslua +++ b/src/core/tour/TourScript.ttslua @@ -15,7 +15,7 @@ TOUR_SCRIPT = { { narrator = "Daisy", text = "If you're new to the game, the library here has everything you'll need. A little research can go a long way, and looking into old newspapers for the weird and unusual can yield some surprisingly helpful information.\n\nI put a few right there that might prove enlightening.", - showObj = "d99993", + objReferenceData = { owner = "Mythos", type = "RulesReference" }, distanceFromObj = 20, position = "west", speakerSide = "right" @@ -23,7 +23,7 @@ TOUR_SCRIPT = { { narrator = "Mandy", text = "To survive what's coming you'll need a deck. If it's safely hidden away on ArkhamDB you can load it here, and even find the newest version after an upgrade without changing the ID.\n\nNo need to publish all your decks, use 'Private' and you can see it. Just make sure to select 'Make your decks public' in ArkhamDB.", - showObj = "a28140", + objReferenceData = { owner = "Mythos", type = "DeckImporter" }, distanceFromObj = -5, position = "northwest", skipCentering = true, @@ -31,7 +31,7 @@ TOUR_SCRIPT = { { narrator = "Daniela", text = "I prefer the hands-on approach to building things, if you do too you can build a deck yourself.\n\nAll the cards you could ever need are here, laid out like a disassembled engine. Place the cards on the table, copy them for your deck, and you'll be ready for anything.", - showObj = "2d30ee", + objReferenceData = { owner = "Mythos", type = "PlayerCardPanel" }, distanceFromObj = -7, position = "south", speakerSide = "right" @@ -39,7 +39,7 @@ TOUR_SCRIPT = { { narrator = "Finn", text = "Ready to face the unknown? We've smuggled shocking revelations and devious enemies from all over the world. Download the campaign you want to play, then Place it on the table to see the scenarios.\n\nJust remember - if it turns out to be too much for you, I was never here.", - showObj = "aca04c", + objReferenceData = { owner = "Mythos", type = "CampaignThePathToCarcosa" }, distanceFromObj = 20, position = "northwest", }, @@ -77,7 +77,7 @@ TOUR_SCRIPT = { { narrator = "Preston", text = "I can afford to buy what I need, but for those less well-off we've provided an endless pool of tokens to track your game. Simply drag one out of the pools here.\n\nResources are my favorite of course, but damage and horror are as inevitable as taxes. I leave those to my bookkeeper though. Those tokens can work like counters, use the number keys to change the value.", - showObj = "9fadf9", + objReferenceData = { owner = "Mythos", type = "ResourceTokenBag" }, position = "north", skipCentering = true, speakerSide = "right" diff --git a/src/playercards/AllCardsBagApi.ttslua b/src/playercards/AllCardsBagApi.ttslua index c9223331..83cf6463 100644 --- a/src/playercards/AllCardsBagApi.ttslua +++ b/src/playercards/AllCardsBagApi.ttslua @@ -1,29 +1,33 @@ do local AllCardsBagApi = {} - local ALL_CARDS_BAG_GUID = "15bb07" + local guidReferenceApi = require("core/GUIDReferenceApi") + + local function getAllCardsBag() + return guidReferenceApi.getObjectByOwnerAndType("Mythos", "AllCardsBag") + end -- Returns a specific card from the bag, based on ArkhamDB ID - -- @param 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 id table String ID of the card to retrieve + ---@return table table + -- 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 AllCardsBagApi.getCardById = function(id) - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getCardById", {id = id}) + return getAllCardsBag().call("getCardById", {id = id}) 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. + ---@return id String ID of the selected weakness. AllCardsBagApi.getRandomWeaknessId = function() - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getRandomWeaknessId") + return getAllCardsBag().call("getRandomWeaknessId") end AllCardsBagApi.isIndexReady = function() - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("isIndexReady") + return getAllCardsBag().call("isIndexReady") end -- Called by Hotfix bags when they load. If we are still loading indexes, then @@ -32,40 +36,38 @@ do -- 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. AllCardsBagApi.rebuildIndexForHotfix = function() - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("rebuildIndexForHotfix") + return getAllCardsBag().call("rebuildIndexForHotfix") 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. - -- @param - -- name String or string fragment to search for names - -- exact Whether the name match should be exact + ---@param name String or string fragment to search for names + ---@param exact Boolean Whether the name match should be exact AllCardsBagApi.getCardsByName = function(name, exact) - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getCardsByName", {name = name, exact = exact}) + return getAllCardsBag().call("getCardsByName", {name = name, exact = exact}) end AllCardsBagApi.isBagPresent = function() - return getObjectFromGUID(ALL_CARDS_BAG_GUID) and true + return getAllCardsBag() and true end -- Returns a list of cards from the bag matching a class and level (0 or upgraded) - -- @param - -- class: String class to retrieve ("Guardian", "Seeker", etc) - -- upgraded: true for upgraded cards (Level 1-5), false for Level 0 - -- @return: If the indexes are still being constructed, returns an empty table. + ---@param class String class to retrieve ("Guardian", "Seeker", etc) + ---@param upgraded Boolean 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 AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded) - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getCardsByClassAndLevel", {class = class, upgraded = upgraded}) + return getAllCardsBag().call("getCardsByClassAndLevel", {class = class, upgraded = upgraded}) end AllCardsBagApi.getCardsByCycle = function(cycle) - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getCardsByCycle", cycle) + return getAllCardsBag().call("getCardsByCycle", cycle) end AllCardsBagApi.getUniqueWeaknesses = function() - return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getUniqueWeaknesses") + return getAllCardsBag().call("getUniqueWeaknesses") end return AllCardsBagApi diff --git a/src/playercards/PlayerCardPanelData.ttslua b/src/playercards/PlayerCardPanelData.ttslua index 0ca5bc75..fff27191 100644 --- a/src/playercards/PlayerCardPanelData.ttslua +++ b/src/playercards/PlayerCardPanelData.ttslua @@ -245,9 +245,9 @@ INVESTIGATORS["Jenny Barnes"] = { starterDeck = "2624961" } INVESTIGATORS["Jim Culver"] = { - cards = { "02004" }, + cards = { "02004", "02004-p", "02004-pf", "02004-pb" }, minicards = { "02004-m" }, - signatures = { "02012", "02013" }, + signatures = { "02012", "02013", "90050", "90051", "90052", "90053" }, starterDeck = "2624965" } INVESTIGATORS["\"Ashcan\" Pete"] = { diff --git a/src/playercards/cards/FamilyInheritance.ttslua b/src/playercards/cards/FamilyInheritance.ttslua index 0bbab561..b33b177c 100644 --- a/src/playercards/cards/FamilyInheritance.ttslua +++ b/src/playercards/cards/FamilyInheritance.ttslua @@ -16,8 +16,8 @@ function searchSelf() for _, obj in ipairs(searchArea(self.getPosition(), { 2.5, 0.5, 3.5 })) do local obj = obj.hit_object - if obj.getCustomObject().image == - "http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then + local image = obj.getCustomObject().image + if image == "http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then foundTokens = foundTokens + math.abs(obj.getQuantity()) obj.destruct() elseif obj.getMemo() == "resourceCounter" then @@ -47,7 +47,7 @@ end function takeAll(playerColor) searchSelf() local matColor = playmatApi.getMatColorByPosition(self.getPosition()) - playmatApi.gainResources(foundTokens, matColor) + playmatApi.updateCounter(matColor, "ResourceCounter", foundTokens) if clickableResourceCounter then clickableResourceCounter.call("updateVal", 0) diff --git a/src/playercards/cards/ShortSupply.ttslua b/src/playercards/cards/ShortSupply.ttslua index 71c5d9bf..102696ae 100644 --- a/src/playercards/cards/ShortSupply.ttslua +++ b/src/playercards/cards/ShortSupply.ttslua @@ -1,4 +1,4 @@ -local playmatAPI = require("playermat/PlaymatApi") +local playmatApi = require("playermat/PlaymatApi") function onLoad() self.addContextMenuItem("Discard 10 cards", shortSupply) @@ -6,11 +6,11 @@ end -- called by context menu entry function shortSupply(color) - local matColor = playmatAPI.getMatColorByPosition(self.getPosition()) + local matColor = playmatApi.getMatColorByPosition(self.getPosition()) -- get draw deck and discard position - local drawDeck = playmatAPI.getDrawDeck(matColor) - local discardPos = playmatAPI.getDiscardPosition(matColor) + local drawDeck = playmatApi.getDrawDeck(matColor) + local discardPos = playmatApi.getDiscardPosition(matColor) -- error handling if discardPos == nil then @@ -21,7 +21,7 @@ function shortSupply(color) if drawDeck == nil then broadcastToColor("Deck not found!", color, "Yellow") return - elseif drawDeck.tag ~= "Deck" then + elseif drawDeck.type ~= "Deck" then broadcastToColor("Deck only contains a single card!", color, "Yellow") return end diff --git a/src/playercards/cards/WellConnected.ttslua b/src/playercards/cards/WellConnected.ttslua index 7e7fda73..31f90625 100644 --- a/src/playercards/cards/WellConnected.ttslua +++ b/src/playercards/cards/WellConnected.ttslua @@ -1,72 +1,53 @@ -- this script is shared between both the level 0 and the upgraded level 3 version of the card local playmatApi = require("playermat/PlaymatApi") -local display = false -local count = 0 -local modValue = 5 -- level 0 Well Connected -local loopId = nil - -local b_display = { - click_function = "toggleCounter", - function_owner = self, - position = {0.88,0.5,-1.33}, - font_size = 150, - width = 175, - height = 175 +local modValue, loopId +local buttonParameters = { + click_function = "toggleCounter", + tooltip = "disable counter", + function_owner = self, + position = { 0.88, 0.5, -1.33 }, + font_size = 150, + width = 175, + height = 175 } -function onLoad(saved_data) - local notes = JSON.decode(self.getGMNotes()) +function onSave() return JSON.encode({ loopId = loopId }) end - if notes.id == "54006" then -- hardcoded card id for upgraded Well Connected (3) - modValue = 4 -- Well Connected (3) +function onLoad(savedData) + -- use metadata to detect level and adjust modValue accordingly + if JSON.decode(self.getGMNotes()).level == 0 then + modValue = 5 + else + modValue = 4 + end + + if savedData ~= "" then + local loadedData = JSON.decode(savedData) + if loadedData.loopId then + self.createButton(buttonParameters) + loopId = Wait.time(updateDisplay, 2, -1) end + end - if saved_data != '' then - local loaded_data = JSON.decode(saved_data) - display = not loaded_data.saved_display - - self.clearButtons() - toggleCounter() - end - - self.addContextMenuItem('Toggle Counter', toggleCounter) -end - -function onSave() - return JSON.encode({saved_display = display}) + self.addContextMenuItem("Toggle Counter", toggleCounter) end function toggleCounter() - display = not display - - if display then - createUpdateDisplay() - loopId = Wait.time(|| createUpdateDisplay(), 2, -1) - else - if loopId ~= nil then - Wait.stop(loopId) - end - - self.clearButtons() - loopId = nil - end + if loopId ~= nil then + Wait.stop(loopId) + loopId = nil + self.clearButtons() + else + self.createButton(buttonParameters) + updateDisplay() + loopId = Wait.time(updateDisplay, 2, -1) + end end -function createUpdateDisplay() - count = math.max(math.floor(getPlayerResources() / modValue), 0) - - b_display.label = tostring(count) - - if loopId == nil then - self.createButton(b_display) - else - self.editButton(b_display) - end +function updateDisplay() + local matColor = playmatApi.getMatColorByPosition(self.getPosition()) + local resources = playmatApi.getCounterValue(matColor, "ResourceCounter") + local count = tostring(math.floor(resources / modValue)) + self.editButton({ index = 0, label = count }) end - -function getPlayerResources() - local matColor = playmatApi.getMatColorByPosition(self.getPosition()) - - return playmatApi.getResourceCount(matColor) -end \ No newline at end of file diff --git a/src/playercards/customizable/FriendsinLowPlacesUpgradeSheet.ttslua b/src/playercards/customizable/FriendsinLowPlacesUpgradeSheet.ttslua index 7862666f..f0870600 100644 --- a/src/playercards/customizable/FriendsinLowPlacesUpgradeSheet.ttslua +++ b/src/playercards/customizable/FriendsinLowPlacesUpgradeSheet.ttslua @@ -25,16 +25,16 @@ customizations = { checkboxes = { posZ = -0.44, count = 2, + }, + textField = { + position = { 0.6295, 0.25, -0.44 }, + width = 290 } }, [4] = { checkboxes = { posZ = -0.05, count = 2, - }, - textField = { - position = { 0.6295, 0.25, -0.44 }, - width = 290 } }, [5] = { diff --git a/src/playermat/ClueCounter.ttslua b/src/playermat/ClueCounter.ttslua index 3dcaa490..cce78085 100644 --- a/src/playermat/ClueCounter.ttslua +++ b/src/playermat/ClueCounter.ttslua @@ -7,13 +7,12 @@ local validCountItemList = { ["Clue"] = 1, [""] = 1 } -local trashGUID = "70b9f6" exposedValue = 0 function onLoad() self.createButton({ label = "", - click_function = "removeAllClues", + click_function = "countItems", function_owner = self, position = { 0, 0.1, 0 }, height = 0, @@ -27,16 +26,15 @@ end -- Activated once per second, counts items in bowls function countItems() local totalValue = 0 - local countableItems = findValidItemsInSphere() - for _, entry in ipairs(countableItems) do - local descValue = tonumber(entry.hit_object.getDescription()) - local stackMult = math.abs(entry.hit_object.getQuantity()) + for _, item in ipairs(findValidItemsInSphere()) do + local descValue = tonumber(item.getDescription()) + local stackMult = math.abs(item.getQuantity()) -- Use value in description if available if descValue ~= nil then totalValue = totalValue + descValue * stackMult else -- Otherwise use the value in validCountItemList - totalValue = totalValue + validCountItemList[entry.hit_object.getName()] * stackMult + totalValue = totalValue + validCountItemList[item.getName()] * stackMult end end exposedValue = totalValue @@ -49,38 +47,22 @@ function findValidItemsInSphere() direction = { 0, 1, 0 }, type = 2, max_distance = 0, - size = { 2, 2, 2 }, - --debug=true + size = { 2, 2, 2 } }) - retval = {} + local validItemList = {} for _, entry in ipairs(items) do - --Ignore the bowl if entry.hit_object ~= self then - --Ignore if not in validCountItemList - local tableEntry = validCountItemList[entry.hit_object.getName()] - if tableEntry ~= nil then - table.insert(retval, entry) + if validCountItemList[entry.hit_object.getName()] ~= nil then + table.insert(validItemList, entry.hit_object) end end end - return retval + return validItemList end -function removeAllClues() - startLuaCoroutine(self, "clueRemovalCoroutine") -end - -function clueRemovalCoroutine() - for _, entry in ipairs(findValidItemsInSphere()) do - -- Do not put the table in the garbage - if entry.hit_object.getGUID() ~= "4ee1f2" then - -- delay for animation purposes - for k = 1, 10 do - coroutine.yield(0) - end - getObjectFromGUID(trashGUID).putObject(entry.hit_object) - end +function removeAllClues(trash) + for _, obj in ipairs(findValidItemsInSphere()) do + trash.putObject(obj) end - return 1 -end +end \ No newline at end of file diff --git a/src/playermat/Playmat.ttslua b/src/playermat/Playmat.ttslua index ef2a99d3..3ddefdfb 100644 --- a/src/playermat/Playmat.ttslua +++ b/src/playermat/Playmat.ttslua @@ -1,4 +1,5 @@ local chaosBagApi = require("chaosbag/ChaosBagApi") +local guidReferenceApi = require("core/GUIDReferenceApi") local mythosAreaApi = require("core/MythosAreaApi") local navigationOverlayApi = require("core/NavigationOverlayApi") local tokenChecker = require("core/token/TokenChecker") @@ -66,7 +67,7 @@ local DECK_DISCARD_AREA = { }, size = { x = 0.4, - y = 0.1, + y = 1, z = 1.1 } } @@ -81,7 +82,11 @@ local ENCOUNTER_DISCARD_POSITION = { x = -3.85, y = 1.5, z = 10.38} -- global variable so it can be reset by the Clean Up Helper activeInvestigatorId = "00000" -local TRASHCAN, STAT_TRACKER, RESOURCE_COUNTER +-- table of type-object reference pairs of all owned objects +local ownedObjects = {} +local matColor = self.getMemo() + +-- variable to track the status of the "Show Draw Button" option local isDrawButtonVisible = false -- global variable to report "Dream-Enhancing Serum" status @@ -98,9 +103,8 @@ end function onLoad(saveState) self.interactable = DEBUG - TRASHCAN = getObjectFromGUID(TRASHCAN_GUID) - STAT_TRACKER = getObjectFromGUID(STAT_TRACKER_GUID) - RESOURCE_COUNTER = getObjectFromGUID(RESOURCE_COUNTER_GUID) + -- get object references to owned objects + ownedObjects = guidReferenceApi.getObjectsByOwner(matColor) -- button creation for i = 1, 6 do @@ -145,9 +149,7 @@ function onLoad(saveState) end showDrawButton(isDrawButtonVisible) - collisionEnabled = true - math.randomseed(os.time()) end @@ -163,7 +165,7 @@ function searchArea(origin, size, filter) orientation = self.getRotation(), type = 3, size = size, - max_distance = 1 + max_distance = 0 }) local objList = {} @@ -180,11 +182,12 @@ function isCard(x) return x.type == 'Card' end function isDeck(x) return x.type == 'Deck' end function isCardOrDeck(x) return x.type == 'Card' or x.type == 'Deck' end --- Finds all objects on the playmat and associated set aside zone. +-- finds all objects on the playmat and associated set aside zone. function searchAroundSelf(filter) local bounds = self.getBoundsNormalized() -- Increase the width to cover the set aside zone bounds.size.x = bounds.size.x + SEARCH_AROUND_SELF_X_BUFFER + bounds.size.y = 1 -- Since the cast is centered on the position, shift left or right to keep the non-set aside edge -- of the cast at the edge of the playmat -- setAsideDirection accounts for the set aside zone being on the left or right, depending on the @@ -211,6 +214,14 @@ function doNotReady(card) return card.getVar("do_not_ready") or false end +-- rounds a number to the specified amount of decimal places +---@param num Number Initial value +---@param numDecimalPlaces Number Amount of decimal places +function round(num, numDecimalPlaces) + local mult = 10^(numDecimalPlaces or 0) + return math.floor(num * mult + 0.5) / mult +end + --------------------------------------------------------- -- Discard buttons --------------------------------------------------------- @@ -233,7 +244,7 @@ function makeDiscardHandlerFor(searchPosition) chaosBag.putObject(obj) -- don't touch the table or this playmat itself elseif obj.guid ~= "4ee1f2" and obj ~= self then - TRASHCAN.putObject(obj) + ownedObjects.Trash.putObject(obj) end end end @@ -370,10 +381,10 @@ function doUpkeep(_, clickedByColor, isRightClick) -- gain a resource (or two if playing Jenny Barnes) if string.match(activeInvestigatorId, "%d%d%d%d%d") == "02003" then - gainResources(2) + updateCounter({type = "ResourceCounter", modifier = 2}) printToColor("Gaining 2 resources (Jenny)", messageColor) else - gainResources(1) + updateCounter({type = "ResourceCounter", modifier = 1}) end -- draw a card (with handling for Patrice and Forced Learning) @@ -399,18 +410,6 @@ function doUpkeep(_, clickedByColor, isRightClick) end end --- adds the specified amount of resources to the resource counter -function gainResources(amount) - local count = RESOURCE_COUNTER.getVar("val") - local add = tonumber(amount) or 0 - RESOURCE_COUNTER.call("updateVal", count + add) -end - --- returns the resource counter amount -function getResourceCount() - return RESOURCE_COUNTER.getVar("val") -end - -- function for "draw 1 button" (that can be added via option panel) function doDrawOne(_, color) -- send messages to player who clicked button if no seated player found @@ -425,10 +424,13 @@ function drawCardsWithReshuffle(numCards) -- Norman Withers handling if string.match(activeInvestigatorId, "%d%d%d%d%d") == "08004" then local harbinger = false - if topCard ~= nil and topCard.getName() == "The Harbinger" then harbinger = true + if topCard ~= nil and topCard.getName() == "The Harbinger" then + harbinger = true elseif drawDeck ~= nil and not drawDeck.is_face_down then local cards = drawDeck.getObjects() - if cards[#cards].name == "The Harbinger" then harbinger = true end + if cards[#cards].name == "The Harbinger" then + harbinger = true + end end if harbinger then @@ -554,32 +556,8 @@ function changeColor(clickedByColor) -- show the option dialog for color selection to the player that triggered this Player[clickedByColor].showOptionsDialog("Select a new color:", colorList, _, function(color) - local HAND_ZONE_GUIDS = { - "a70eee", -- White - "5fe087", -- Orange - "0285cc", -- Green - "be2f17" -- Red - } - local index - local startPos = self.getPosition() - - -- get respective hand zone by position - if startPos.x < -42 then - if startPos.z > 0 then - index = 1 - else - index = 2 - end - else - if startPos.z > 0 then - index = 3 - else - index = 4 - end - end - -- update the color of the hand zone - local handZone = getObjectFromGUID(HAND_ZONE_GUIDS[index]) + local handZone = ownedObjects.HandZone handZone.setValue(color) -- if the seated player clicked this, reseat him to the new color @@ -643,17 +621,17 @@ function spawnTokensFor(object) tokenManager.spawnForCard(object, extraUses) end -function onCollisionEnter(collision_info) - local object = collision_info.collision_object - - -- detect if "Dream-Enhancing Serum" is placed - if object.getName() == "Dream-Enhancing Serum" then isDES = true end +function onCollisionEnter(collisionInfo) + local object = collisionInfo.collision_object -- only continue if loading is completed if not collisionEnabled then return end -- only continue for cards - if object.type ~= "Card" then return end + if not isCard(object) then return end + + -- detect if "Dream-Enhancing Serum" is placed + if object.getName() == "Dream-Enhancing Serum" then isDES = true end maybeUpdateActiveInvestigator(object) syncCustomizableMetadata(object) @@ -668,8 +646,8 @@ function onCollisionEnter(collision_info) end -- detect if "Dream-Enhancing Serum" is removed -function onCollisionExit(collision_info) - if collision_info.collision_object.getName() == "Dream-Enhancing Serum" then isDES = false end +function onCollisionExit(collisionInfo) + if collisionInfo.collision_object.getName() == "Dream-Enhancing Serum" then isDES = false end end -- checks if tokens should be spawned for the provided card @@ -705,14 +683,12 @@ function shouldSpawnTokens(card) end function onObjectEnterContainer(container, object) - Wait.frames(function() resetTokensIfInDeckZone(container, object) end, 1) -end + if not isCard(object) then return end -function resetTokensIfInDeckZone(container, object) - local pos = self.positionToLocal(container.getPosition()) - if inArea(pos, DECK_DISCARD_AREA) then + local localCardPos = self.positionToLocal(object.getPosition()) + if inArea(localCardPos, DECK_DISCARD_AREA) then tokenManager.resetTokensSpawned(object) - removeTokensFromObject(container) + removeTokensFromObject(object) end end @@ -727,7 +703,7 @@ function removeTokensFromObject(object) obj.getLock() == false and obj.getDescription() ~= "Action Token" and not tokenChecker.isChaosToken(obj) then - TRASHCAN.putObject(obj) + ownedObjects.Trash.putObject(obj) end end end @@ -746,11 +722,16 @@ function maybeUpdateActiveInvestigator(card) if notes.id == activeInvestigatorId then return end class = notes.class activeInvestigatorId = notes.id - STAT_TRACKER.call("updateStats", {notes.willpowerIcons, notes.intellectIcons, notes.combatIcons, notes.agilityIcons}) + ownedObjects.InvestigatorSkillTracker.call("updateStats", { + notes.willpowerIcons, + notes.intellectIcons, + notes.combatIcons, + notes.agilityIcons + }) elseif activeInvestigatorId ~= "00000" then class = "Neutral" activeInvestigatorId = "00000" - STAT_TRACKER.call("updateStats", {1, 1, 1, 1}) + ownedObjects.InvestigatorSkillTracker.call("updateStats", {1, 1, 1, 1}) else return end @@ -797,6 +778,40 @@ function setObjectState(obj, stateId) if obj.getStateId() ~= stateId then obj.setState(stateId) end end +--------------------------------------------------------- +-- manipulation of owned objects +--------------------------------------------------------- + +-- updates the specific owned counter +---@param param Table Contains the information to update: +--- type: String Counter to target +--- newValue: Number Value to set the counter to +--- modifier: Number If newValue is not provided, the existing value will be adjusted by this modifier +function updateCounter(param) + local counter = ownedObjects[param.type] + if counter ~= nil then + counter.call("updateVal", param.newValue or (counter.getVar("val") + param.modifier)) + else + printToAll(param.type .. " for " .. matColor .. " could not be found.", "Yellow") + end +end + +-- returns the resource counter amount +---@param type String Counter to target +function getCounterValue(type) + return ownedObjects[type].getVar("val") +end + +-- set investigator skill tracker to "1, 1, 1, 1" +function resetSkillTracker() + local obj = ownedObjects.InvestigatorSkillTracker + if obj ~= nil then + obj.call("updateStats", { 1, 1, 1, 1 }) + else + printToAll("Skill tracker for " .. matColor .. " playmat could not be found.", "Yellow") + end +end + --------------------------------------------------------- -- calls to 'Global' / functions for calls from outside --------------------------------------------------------- @@ -847,31 +862,29 @@ end -- Spawns / destroys a clickable clue counter for this playmat with the correct amount of clues ---@param showCounter Boolean Whether the clickable clue counter should be present function clickableClues(showCounter) - local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID) - local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID) - local clickerPos = CLUE_CLICKER.getPosition() + local clickerPos = ownedObjects.ClickableClueCounter.getPosition() local clueCount = 0 if showCounter then -- current clue count - clueCount = CLUE_COUNTER.getVar("exposedValue") + clueCount = ownedObjects.ClueCounter.getVar("exposedValue") -- remove clues - CLUE_COUNTER.call("removeAllClues") + ownedObjects.ClueCounter.call("removeAllClues", ownedObjects.Trash) -- set value for clue clickers - CLUE_CLICKER.call("updateVal", clueCount) + ownedObjects.ClickableClueCounter.call("updateVal", clueCount) -- move clue counters up clickerPos.y = 1.52 - CLUE_CLICKER.setPosition(clickerPos) + ownedObjects.ClickableClueCounter.setPosition(clickerPos) else -- current clue count - clueCount = CLUE_CLICKER.getVar("val") + clueCount = ownedObjects.ClickableClueCounter.getVar("val") -- move clue counters down clickerPos.y = 1.3 - CLUE_CLICKER.setPosition(clickerPos) + ownedObjects.ClickableClueCounter.setPosition(clickerPos) -- spawn clues local pos = self.positionToWorld({x = -1.12, y = 0.05, z = 0.7}) @@ -884,26 +897,18 @@ end -- removes all clues (moving tokens to the trash and setting counters to 0) function removeClues() - local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID) - local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID) - - CLUE_COUNTER.call("removeAllClues") - CLUE_CLICKER.call("updateVal", 0) + ownedObjects.ClueCounter.call("removeAllClues", ownedObjects.Trash) + ownedObjects.ClickableClueCounter.call("updateVal", 0) end -- reports the clue count ---@param useClickableCounters Boolean Controls which type of counter is getting checked function getClueCount(useClickableCounters) - local count = 0 - if useClickableCounters then - local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID) - count = tonumber(CLUE_CLICKER.getVar("val")) + return ownedObjects.ClickableClueCounter.getVar("val") else - local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID) - count = tonumber(CLUE_COUNTER.getVar("exposedValue")) + return ownedObjects.ClueCounter.getVar("exposedValue") end - return count end -- Sets this playermat's snap points to limit snapping to matching card types or not. If matchTypes @@ -962,11 +967,3 @@ function updatePlayerCards(args) local playerCardData = customDataHelper.getTable("PLAYER_CARD_DATA") tokenManager.addPlayerCardData(playerCardData) end - --- utility function for rounding ----@param num Number Initial value ----@param numDecimalPlaces Number Amount of decimal places -function round(num, numDecimalPlaces) - local mult = 10^(numDecimalPlaces or 0) - return math.floor(num * mult + 0.5) / mult -end diff --git a/src/playermat/PlaymatApi.ttslua b/src/playermat/PlaymatApi.ttslua index 9df18be2..a051cfe6 100644 --- a/src/playermat/PlaymatApi.ttslua +++ b/src/playermat/PlaymatApi.ttslua @@ -1,225 +1,213 @@ do - local PlaymatApi = { } - local internal = { } + local PlaymatApi = {} + local guidReferenceApi = require("core/GUIDReferenceApi") - local MAT_IDS = { - White = "8b081b", - Orange = "bd0ff4", - Green = "383d8b", - Red = "0840d5" - } - - local CLUE_COUNTER_GUIDS = { - White = "37be78", - Orange = "1769ed", - Green = "032300", - Red = "d86b7c" - } - - local CLUE_CLICKER_GUIDS = { - White = "db85d6", - Orange = "3f22e5", - Green = "891403", - Red = "4111de" - } - - -- Returns the color of the by position requested playermat as string - ---@param startPos Table Position of the search, table get's roughly cut into 4 quarters to assign a playermat - PlaymatApi.getMatColorByPosition = function(startPos) - if startPos.x < -42 then - if startPos.z > 0 then - return "White" - else - return "Orange" - end + -- 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 + ---@return array Table Single-element if only single playmat is requested + local function getMatForColor(matColor) + if matColor == "All" then + return guidReferenceApi.getObjectsByType("Playermat") else - if startPos.z > 0 then - return "Green" - else - return "Red" + return { matColor = guidReferenceApi.getObjectByOwnerAndType(matColor, "Playermat") } + end + end + + -- Returns the color of the closest playmat + ---@param startPos Table Starting position to get the closest mat from + PlaymatApi.getMatColorByPosition = function(startPos) + local result, smallestDistance + for matColor, mat in pairs(getMatForColor("All")) do + local distance = Vector.between(startPos, mat.getPosition()):magnitude() + if smallestDistance == nil or distance < smallestDistance then + smallestDistance = distance + result = matColor + end + end + return result + end + + -- Returns the color of the player's hand that is seated next to the playmat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") + PlaymatApi.getPlayerColor = function(matColor) + for _, mat in pairs(getMatForColor(matColor)) do + return mat.getVar("playerColor") + end + end + + -- Returns the color of the playmat that owns the playercolor's hand + ---@param handColor String Color of the playmat + PlaymatApi.getMatColor = function(handColor) + for matColor, mat in pairs(getMatForColor("All")) do + local playerColor = mat.getVar("playerColor") + if playerColor == handColor then + return matColor end end end - -- Returns the color of the player's hand that is seated next to the playermat - ---@param matColor String Color of the playermat - PlaymatApi.getPlayerColor = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.getVar("playerColor") - end - - -- Returns the color of the playermat that owns the playercolor's hand - ---@param handColor String Color of the playermat - PlaymatApi.getMatColor = function(handColor) - local matColors = {"White", "Orange", "Green", "Red"} - for i, mat in ipairs(internal.getMatForColor("All")) do - local color = mat.getVar("playerColor") - if color == handColor then return matColors[i] end - end - return "NOT_FOUND" - end - - -- Returns the result of a cast in the specificed playermat's area - ---@param matColor String Color of the playermat - PlaymatApi.searchPlaymat = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.call("searchAroundSelf") - end - - -- Returns if there is the card "Dream-Enhancing Serum" on the requested playermat - ---@param matColor String Color of the playermat + -- Returns if there is the card "Dream-Enhancing Serum" on the requested playmat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.isDES = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.getVar("isDES") + for _, mat in pairs(getMatForColor(matColor)) do + return mat.getVar("isDES") + end end -- Returns the draw deck of the requested playmat - ---@param matColor String Color of the playermat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.getDrawDeck = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - mat.call("getDrawDiscardDecks") - return mat.getVar("drawDeck") + for _, mat in pairs(getMatForColor(matColor)) do + mat.call("getDrawDiscardDecks") + return mat.getVar("drawDeck") + end end -- Returns the position of the discard pile of the requested playmat - ---@param matColor String Color of the playermat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.getDiscardPosition = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.call("returnGlobalDiscardPosition") + for _, mat in pairs(getMatForColor(matColor)) do + return mat.call("returnGlobalDiscardPosition") + end end -- Transforms a local position into a global position ---@param localPos Table Local position to be transformed - ---@param matColor String Color of the playermat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.transformLocalPosition = function(localPos, matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.positionToWorld(localPos) + for _, mat in pairs(getMatForColor(matColor)) do + return mat.positionToWorld(localPos) + end end -- Returns the rotation of the requested playmat - ---@param matColor String Color of the playermat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.returnRotation = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.getRotation() + for _, mat in pairs(getMatForColor(matColor)) do + return mat.getRotation() + end end -- Triggers the Upkeep for the requested playmat - ---@param matColor String Color of the playermat + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All ---@param playerColor String Color of the calling player (for messages) PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.call("doUpkeepFromHotkey", playerColor) + for _, mat in pairs(getMatForColor(matColor)) do + mat.call("doUpkeepFromHotkey", playerColor) + end end -- Returns the active investigator id - ---@param matColor String Color of the playermat + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") PlaymatApi.returnInvestigatorId = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.getVar("activeInvestigatorId") + for _, mat in pairs(getMatForColor(matColor)) do + return mat.getVar("activeInvestigatorId") + end end - -- Sets the requested playermat's snap points to limit snapping to matching card types or not. If + -- Sets the requested playmat'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 -- investigator area point will only snap Investigators. If matchTypes is false, snap points will -- be reset to snap all cards. - ---@param matchCardTypes Boolean. Whether snap points should only snap for the matching card - -- types. - ---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also - -- accepts "All" as a special value which will apply the setting to all four mats. + ---@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 PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor) - for _, mat in ipairs(internal.getMatForColor(matColor)) do + for _, mat in pairs(getMatForColor(matColor)) do mat.call("setLimitSnapsByType", matchCardTypes) end end - -- Sets the requested playermat's draw 1 button to visible - ---@param isDrawButtonVisible Boolean. Whether the draw 1 button should be visible or not - ---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also - -- accepts "All" as a special value which will apply the setting to all four mats. + -- Sets the requested playmat's draw 1 button to visible + ---@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 PlaymatApi.showDrawButton = function(isDrawButtonVisible, matColor) - for _, mat in ipairs(internal.getMatForColor(matColor)) do + for _, mat in pairs(getMatForColor(matColor)) do mat.call("showDrawButton", isDrawButtonVisible) end end - -- Shows or hides the clickable clue counter for the requested playermat - ---@param showCounter Boolean. Whether the clickable counter should be present or not - ---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also - -- accepts "All" as a special value which will apply the setting to all four mats. + -- Shows or hides the clickable clue counter for the requested playmat + ---@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 PlaymatApi.clickableClues = function(showCounter, matColor) - for _, mat in ipairs(internal.getMatForColor(matColor)) do + for _, mat in pairs(getMatForColor(matColor)) do mat.call("clickableClues", showCounter) end end - -- Removes all clues (to the trash for tokens and counters set to 0) for the requested playermat - ---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also - -- accepts "All" as a special value which will apply the setting to all four mats. + -- Removes all clues (to the trash for tokens and counters set to 0) for the requested playmat + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All PlaymatApi.removeClues = function(matColor) - for _, mat in ipairs(internal.getMatForColor(matColor)) do + for _, mat in pairs(getMatForColor(matColor)) do mat.call("removeClues") end end - -- Reports the clue count for the requested playermat + -- Reports the clue count for the requested playmat ---@param useClickableCounters Boolean Controls which type of counter is getting checked PlaymatApi.getClueCount = function(useClickableCounters, matColor) local count = 0 - for _, mat in ipairs(internal.getMatForColor(matColor)) do - count = count + tonumber(mat.call("getClueCount", useClickableCounters)) + for _, mat in pairs(getMatForColor(matColor)) do + count = count + mat.call("getClueCount", useClickableCounters) end return count end - -- Adds the specified amount of resources to the requested playermat's resource counter - PlaymatApi.gainResources = function(amount, matColor) - for _, mat in ipairs(internal.getMatForColor(matColor)) do - mat.call("gainResources", amount) + -- updates the specified owned counter + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All + ---@param type String Counter to target + ---@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 + PlaymatApi.updateCounter = function(matColor, type, newValue, modifier) + for _, mat in pairs(getMatForColor(matColor)) do + mat.call("updateCounter", { type = type, newValue = newValue, modifier = modifier }) end end - -- Returns the resource counter amount for the requested playermat - PlaymatApi.getResourceCount = function(matColor) - local mat = getObjectFromGUID(MAT_IDS[matColor]) - return mat.call("getResourceCount") + -- returns the resource counter amount + ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") + ---@param type String Counter to target + PlaymatApi.getCounterValue = function(matColor, type) + for _, mat in pairs(getMatForColor(matColor)) do + return mat.call("getCounterValue", type) + end + end + + -- resets the specified skill tracker to "1, 1, 1, 1" + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All + PlaymatApi.resetSkillTracker = function(matColor) + for _, mat in pairs(getMatForColor(matColor)) do + mat.call("resetSkillTracker") + end + end + + -- finds all objects on the playmat and associated set aside zone and returns a table + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All + ---@param filter Function Optional filter function (return true for desired objects) + PlaymatApi.searchAroundPlaymat = function(matColor, filter) + local objList = {} + for _, mat in pairs(getMatForColor(matColor)) do + for _, obj in ipairs(mat.call("searchAroundSelf", filter)) do + table.insert(objList, obj) + end + end + return objList end -- Discard a non-hidden card from the corresponding player's hand + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All PlaymatApi.doDiscardOne = function(matColor) - for _, mat in ipairs(internal.getMatForColor(matColor)) do + for _, mat in pairs(getMatForColor(matColor)) do mat.call("doDiscardOne") end end + -- Triggers the metadata sync for all playmats PlaymatApi.syncAllCustomizableCards = function() - for _, mat in ipairs(internal.getMatForColor("All")) do + for _, mat in pairs(getMatForColor("All")) do mat.call("syncAllCustomizableCards") end end - PlaymatApi.updateClueClicker = function(playerColor, val) - return getObjectFromGUID(CLUE_CLICKER_GUIDS[playerColor]).call("updateVal", val) - end - - -- Convenience function to look up a mat's object by color, or get all mats. - ---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also - -- accepts "All" as a special value which will return all four mats. - ---@return: Array of playermat objects. If a single mat is requested, will return a single-element - -- array to simplify processing by consumers. - internal.getMatForColor = function(matColor) - local targetMatGuid = MAT_IDS[matColor] - if targetMatGuid != nil then - return { getObjectFromGUID(targetMatGuid) } - end - if matColor == "All" then - return { - getObjectFromGUID(MAT_IDS.White), - getObjectFromGUID(MAT_IDS.Orange), - getObjectFromGUID(MAT_IDS.Green), - getObjectFromGUID(MAT_IDS.Red), - } - end - end - return PlaymatApi end diff --git a/src/playermat/Zones.ttslua b/src/playermat/Zones.ttslua index 2cb978d9..41a97373 100644 --- a/src/playermat/Zones.ttslua +++ b/src/playermat/Zones.ttslua @@ -20,14 +20,9 @@ -- SetAside5: Hunch Deck for Joe Diamond -- SetAside6: currently unused do + local playmatApi = require("playermat/PlaymatApi") local Zones = { } - local playerMatGuids = {} - playerMatGuids["Red"] = "0840d5" - playerMatGuids["Orange"] = "bd0ff4" - playerMatGuids["White"] = "8b081b" - playerMatGuids["Green"] = "383d8b" - local commonZones = {} commonZones["Investigator"] = { -1.17702, 0, 0.00209 } commonZones["Deck"] = { -1.822724, 0, -0.02940192 } @@ -119,7 +114,7 @@ do and playerColor ~= "Green") then return nil end - return getObjectFromGUID(playerMatGuids[playerColor]).positionToWorld(zoneData[playerColor][zoneName]) + return playmatApi.transformLocalPosition(zoneData[playerColor][zoneName], playerColor) end -- Return the global rotation for a card on the given player mat, based on its metadata. @@ -129,13 +124,11 @@ do -- Y rotation to orient the card on the given player mat as well as a -- Z rotation to place the card face up or face down. Zones.getDefaultCardRotation = function(playerColor, zone) - local deckRotation = getObjectFromGUID(playerMatGuids[playerColor]).getRotation() - + local cardRotation = playmatApi.returnRotation(playerColor) if zone == "Deck" then - deckRotation = deckRotation + Vector(0, 0, 180) + cardRotation = cardRotation + Vector(0, 0, 180) end - - return deckRotation + return cardRotation end return Zones diff --git a/xml/Global.xml b/xml/Global.xml deleted file mode 100644 index 5d9fb47d..00000000 --- a/xml/Global.xml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/xml/Global/BottomBar.xml b/xml/Global/BottomBar.xml new file mode 100644 index 00000000..55f595a5 --- /dev/null +++ b/xml/Global/BottomBar.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + PreviewTitle + by PreviewAuthor + + + + + + + + + + + + + PreviewDescription + + + + + + + + + + + + \ No newline at end of file diff --git a/xml/Global/Global.xml b/xml/Global/Global.xml new file mode 100644 index 00000000..6f6f0d4d --- /dev/null +++ b/xml/Global/Global.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/xml/NavigationOverlay.xml b/xml/Global/NavigationOverlay.xml similarity index 100% rename from xml/NavigationOverlay.xml rename to xml/Global/NavigationOverlay.xml diff --git a/xml/OptionPanel.xml b/xml/Global/OptionPanel.xml similarity index 98% rename from xml/OptionPanel.xml rename to xml/Global/OptionPanel.xml index 9cbbf41c..b82bb725 100644 --- a/xml/OptionPanel.xml +++ b/xml/Global/OptionPanel.xml @@ -79,9 +79,10 @@ + offsetXY="-50 80" + raycastTarget="true"> @@ -95,7 +96,9 @@ - + @@ -356,7 +359,7 @@ + onClick="onClick_toggleUi(optionPanel)">Close diff --git a/xml/TitleSplash.xml b/xml/Global/TitleSplash.xml similarity index 100% rename from xml/TitleSplash.xml rename to xml/Global/TitleSplash.xml diff --git a/xml/UpdateNotification.xml b/xml/Global/UpdateNotification.xml similarity index 94% rename from xml/UpdateNotification.xml rename to xml/Global/UpdateNotification.xml index 21dedd89..6649ea90 100644 --- a/xml/UpdateNotification.xml +++ b/xml/Global/UpdateNotification.xml @@ -10,10 +10,11 @@ offsetXY="420 -5" height="90" width="90" - onClick="onClick_FinnIcon" + onClick="onClick_toggleUi(updateNotification)" image="FinnIcon" tooltip="Update notification" - tooltipBackgroundColor="rgba(0,0,0,0.8)"/> + tooltipPosition="Right" + tooltipBackgroundColor="rgba(0,0,0,1)"/>