Merge pull request #385 from argonui/playermats

Addition of GUID reference handler and api cleanup
This commit is contained in:
Entrox-Licher 2023-10-19 11:15:50 -04:00 committed by GitHub
commit 7d36aaf241
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 1038 additions and 1099 deletions

View File

@ -3,6 +3,7 @@
"ComponentTags_path": "ComponentTags.json", "ComponentTags_path": "ComponentTags.json",
"CustomUIAssets_path": "CustomUIAssets.json", "CustomUIAssets_path": "CustomUIAssets.json",
"DecalPallet_path": "DecalPallet.json", "DecalPallet_path": "DecalPallet.json",
"Decals": [],
"GameComplexity": "", "GameComplexity": "",
"GameMode": "Arkham Horror LCG - Super Complete Edition", "GameMode": "Arkham Horror LCG - Super Complete Edition",
"GameType": "", "GameType": "",
@ -19,6 +20,7 @@
"MusicPlayer_path": "MusicPlayer.json", "MusicPlayer_path": "MusicPlayer.json",
"Note": "", "Note": "",
"ObjectStates_order": [ "ObjectStates_order": [
"GUIDReferenceHandler.123456",
"HandTrigger.5fe087", "HandTrigger.5fe087",
"HandTrigger.be2f17", "HandTrigger.be2f17",
"HandTrigger.0285cc", "HandTrigger.0285cc",
@ -101,7 +103,7 @@
"ClueCounter.d86b7c", "ClueCounter.d86b7c",
"MasterClueCounter.4a3aa4", "MasterClueCounter.4a3aa4",
"LegacyAssets.7165a9", "LegacyAssets.7165a9",
"Playarea.721ba2", "PlayArea.721ba2",
"BarkhamHorror.308439", "BarkhamHorror.308439",
"ChaosBagStatTracker.766620", "ChaosBagStatTracker.766620",
"Blesstokens.afa06b", "Blesstokens.afa06b",
@ -131,11 +133,11 @@
"EdgeoftheEarth.895eaa", "EdgeoftheEarth.895eaa",
"TheDream-Eaters.a16a1a", "TheDream-Eaters.a16a1a",
"ReturntoTheCircleUndone.757324", "ReturntoTheCircleUndone.757324",
"Playermat4Red.0840d5",
"Playermat3Green.383d8b",
"OtherDoominPlay.652ff3", "OtherDoominPlay.652ff3",
"Playermat1White.8b081b", "Playermat1White.8b081b",
"Playermat2Orange.bd0ff4", "Playermat2Orange.bd0ff4",
"Playermat3Green.383d8b",
"Playermat4Red.0840d5",
"Neutral.2691e1", "Neutral.2691e1",
"Neutral.748245", "Neutral.748245",
"Neutral.271b17", "Neutral.271b17",

View File

@ -12,10 +12,6 @@
"displayed": "LinkedPhaseTracker", "displayed": "LinkedPhaseTracker",
"normalized": "linkedphasetracker" "normalized": "linkedphasetracker"
}, },
{
"displayed": "chaosBag",
"normalized": "chaosBag"
},
{ {
"displayed": "displacement_excluded", "displayed": "displacement_excluded",
"normalized": "displacement_excluded" "normalized": "displacement_excluded"
@ -72,10 +68,6 @@
"displayed": "LargeBox", "displayed": "LargeBox",
"normalized": "largebox" "normalized": "largebox"
}, },
{
"displayed": "SoundCube",
"normalized": "soundcube"
},
{ {
"displayed": "CampaignBox", "displayed": "CampaignBox",
"normalized": "campaignbox" "normalized": "campaignbox"
@ -83,10 +75,6 @@
{ {
"displayed": "CameraZoom_ignore", "displayed": "CameraZoom_ignore",
"normalized": "camerazoom_ignore" "normalized": "camerazoom_ignore"
},
{
"displayed": "TokenArranger",
"normalized": "tokenarranger"
} }
] ]
} }

View File

@ -67,6 +67,7 @@
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [ "Tags": [
"CleanUpHelper_ignore",
"displacement_excluded" "displacement_excluded"
], ],
"Tooltip": true, "Tooltip": true,

View File

@ -44,6 +44,9 @@
"Nickname": "Clue tokens", "Nickname": "Clue tokens",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 2.857, "posX": 2.857,

View File

@ -83,4 +83,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -49,6 +49,9 @@
"Nickname": "Cartoon Investigators", "Nickname": "Cartoon Investigators",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"LargeBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -23.615, "posX": -23.615,
@ -61,9 +64,6 @@
"scaleY": 0.14, "scaleY": 0.14,
"scaleZ": 1 "scaleZ": 1
}, },
"Tags": [
"LargeBox"
],
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -83,4 +83,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -83,4 +83,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -49,6 +49,9 @@
"Nickname": "The Ghosts Of Onigawa (Investigator Expansion)", "Nickname": "The Ghosts Of Onigawa (Investigator Expansion)",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"LargeBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -47.192, "posX": -47.192,
@ -61,9 +64,6 @@
"scaleY": 0.14, "scaleY": 0.14,
"scaleZ": 1 "scaleZ": 1
}, },
"Tags": [
"LargeBox"
],
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -83,4 +83,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -49,6 +49,9 @@
"Nickname": "The Sands Of Memphis (Investigator Expansion)", "Nickname": "The Sands Of Memphis (Investigator Expansion)",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"LargeBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -47.192, "posX": -47.192,
@ -61,9 +64,6 @@
"scaleY": 0.14, "scaleY": 0.14,
"scaleZ": 1 "scaleZ": 1
}, },
"Tags": [
"LargeBox"
],
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -44,6 +44,9 @@
"Nickname": "Connection markers", "Nickname": "Connection markers",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -51, "posX": -51,

View File

@ -67,6 +67,7 @@
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [ "Tags": [
"CleanUpHelper_ignore",
"displacement_excluded" "displacement_excluded"
], ],
"Tooltip": true, "Tooltip": true,

View File

@ -40,6 +40,9 @@
"Nickname": "Doom Counter", "Nickname": "Doom Counter",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -5.3, "posX": -5.3,

View File

@ -44,6 +44,9 @@
"Nickname": "Doom tokens", "Nickname": "Doom tokens",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -55.48, "posX": -55.48,

View File

@ -44,6 +44,9 @@
"Nickname": "Doom tokens", "Nickname": "Doom tokens",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 2.761, "posX": 2.761,

View File

@ -83,4 +83,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -82,4 +82,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -66,4 +66,4 @@
}, },
"Value": 0, "Value": 0,
"XmlUI": "" "XmlUI": ""
} }

View File

@ -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": ""
}

View File

@ -37,9 +37,12 @@
"LuaScriptState": "false", "LuaScriptState": "false",
"MeasureMovement": false, "MeasureMovement": false,
"Name": "Custom_Token", "Name": "Custom_Token",
"Nickname": "Master Clue Counter\n", "Nickname": "Master Clue Counter",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -5.3, "posX": -5.3,

View File

@ -66,6 +66,9 @@
"Nickname": "Mythos Area", "Nickname": "Mythos Area",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CleanUpHelper_ignore"
],
"Tooltip": false, "Tooltip": false,
"Transform": { "Transform": {
"posX": -1.309, "posX": -1.309,

View File

@ -974,7 +974,7 @@
"LuaScriptState": "{\"trackedLocations\":[]}", "LuaScriptState": "{\"trackedLocations\":[]}",
"MeasureMovement": false, "MeasureMovement": false,
"Name": "Custom_Token", "Name": "Custom_Token",
"Nickname": "Playarea", "Nickname": "Play Area",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [ "Tags": [

View File

@ -343,9 +343,10 @@
"IgnoreFoW": false, "IgnoreFoW": false,
"LayoutGroupSortIndex": 0, "LayoutGroupSortIndex": 0,
"Locked": true, "Locked": true,
"LuaScript": "require(\"playermat/Playmat\")",
"LuaScriptState_path": "Playermat1White.8b081b.luascriptstate", "LuaScriptState_path": "Playermat1White.8b081b.luascriptstate",
"LuaScript_path": "Playermat1White.8b081b.ttslua",
"MeasureMovement": false, "MeasureMovement": false,
"Memo": "White",
"Name": "Custom_Tile", "Name": "Custom_Tile",
"Nickname": "Playermat 1: White", "Nickname": "Playermat 1: White",
"Snap": true, "Snap": true,

View File

@ -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")

View File

@ -343,9 +343,10 @@
"IgnoreFoW": false, "IgnoreFoW": false,
"LayoutGroupSortIndex": 0, "LayoutGroupSortIndex": 0,
"Locked": true, "Locked": true,
"LuaScript": "require(\"playermat/Playmat\")",
"LuaScriptState_path": "Playermat2Orange.bd0ff4.luascriptstate", "LuaScriptState_path": "Playermat2Orange.bd0ff4.luascriptstate",
"LuaScript_path": "Playermat2Orange.bd0ff4.ttslua",
"MeasureMovement": false, "MeasureMovement": false,
"Memo": "Orange",
"Name": "Custom_Tile", "Name": "Custom_Tile",
"Nickname": "Playermat 2: Orange", "Nickname": "Playermat 2: Orange",
"Snap": true, "Snap": true,

View File

@ -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")

View File

@ -343,9 +343,10 @@
"IgnoreFoW": false, "IgnoreFoW": false,
"LayoutGroupSortIndex": 0, "LayoutGroupSortIndex": 0,
"Locked": true, "Locked": true,
"LuaScript": "require(\"playermat/Playmat\")",
"LuaScriptState_path": "Playermat3Green.383d8b.luascriptstate", "LuaScriptState_path": "Playermat3Green.383d8b.luascriptstate",
"LuaScript_path": "Playermat3Green.383d8b.ttslua",
"MeasureMovement": false, "MeasureMovement": false,
"Memo": "Green",
"Name": "Custom_Tile", "Name": "Custom_Tile",
"Nickname": "Playermat 3: Green", "Nickname": "Playermat 3: Green",
"Snap": true, "Snap": true,

View File

@ -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")

View File

@ -343,13 +343,18 @@
"IgnoreFoW": false, "IgnoreFoW": false,
"LayoutGroupSortIndex": 0, "LayoutGroupSortIndex": 0,
"Locked": true, "Locked": true,
"LuaScript": "require(\"playermat/Playmat\")",
"LuaScriptState_path": "Playermat4Red.0840d5.luascriptstate", "LuaScriptState_path": "Playermat4Red.0840d5.luascriptstate",
"LuaScript_path": "Playermat4Red.0840d5.ttslua",
"MeasureMovement": false, "MeasureMovement": false,
"Memo": "Red",
"Name": "Custom_Tile", "Name": "Custom_Tile",
"Nickname": "Playermat 4: Red", "Nickname": "Playermat 4: Red",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"Red",
"Playermat"
],
"Tooltip": false, "Tooltip": false,
"Transform": { "Transform": {
"posX": -30.35, "posX": -30.35,

View File

@ -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")

View File

@ -35,9 +35,6 @@
"Nickname": "SoundCube", "Nickname": "SoundCube",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"SoundCube"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 78, "posX": 78,

View File

@ -40,9 +40,6 @@
"Nickname": "Token Arranger", "Nickname": "Token Arranger",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"TokenArranger"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": -42.3, "posX": -42.3,

View File

@ -1,11 +1,12 @@
local blessCurseApi = require("chaosbag/BlessCurseManagerApi") local blessCurseApi = require("chaosbag/BlessCurseManagerApi")
local chaosBagApi = require("chaosbag/ChaosBagApi") local chaosBagApi = require("chaosbag/ChaosBagApi")
local deckImporterApi = require("arkhamdb/DeckImporterApi") local deckImporterApi = require("arkhamdb/DeckImporterApi")
local optionPanelApi = require("core/OptionPanelApi") local guidReferenceApi = require("core/GUIDReferenceApi")
local playAreaApi = require("core/PlayAreaApi") local optionPanelApi = require("core/OptionPanelApi")
local playAreaApi = require("core/PlayAreaApi")
local playmatApi = require("playermat/PlaymatApi")
local campaignTokenData = { local campaignTokenData = {
GUID = "51b1c9",
Name = "Custom_Model", Name = "Custom_Model",
Transform = { Transform = {
posX = -21.25, posX = -21.25,
@ -18,9 +19,7 @@ local campaignTokenData = {
scaleY = 2, scaleY = 2,
scaleZ = 2 scaleZ = 2
}, },
Nickname = "Arkham Coin",
Description = "SCED Importer Token", Description = "SCED Importer Token",
GMNotes = "",
Tags = { Tags = {
"ImporterToken" "ImporterToken"
}, },
@ -33,56 +32,48 @@ local campaignTokenData = {
MaterialIndex = 2, MaterialIndex = 2,
TypeIndex = 0, TypeIndex = 0,
CustomShader = { CustomShader = {
SpecularColor = { SpecularColor = {
r = 0.7222887, r = 0.7222887,
g = 0.507659256, g = 0.507659256,
b = 0.339915335 b = 0.339915335
}, },
SpecularIntensity = 0.4, SpecularIntensity = 0.4,
SpecularSharpness = 7.0, SpecularSharpness = 7.0,
FresnelStrength = 0.0 FresnelStrength = 0.0
}, },
CastShadows = true CastShadows = true
} }
} }
local COLORS = { "White", "Orange", "Green", "Red" }
-- counter GUIDS (4x damage and 4x horror) function onLoad()
local DAMAGE_HORROR_GUIDS = {
"eb08d6"; "e64eec"; "1f5a0a"; "591a45";
"468e88"; "0257d9"; "7b5729"; "beb964";
}
local TOUR_GUID = "0e5aa8"
local campaignBoxGUID
function onLoad(save_state)
campaignBoxGUID = ""
self.createButton({ self.createButton({
click_function = "findCampaignFromToken", click_function = "findCampaignFromToken",
function_owner = self, function_owner = self,
label = "Import", 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!)", 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}, position = { x = -1, y = 0.2, z = 0 },
font_size = 400, font_size = 400,
width = 1400, width = 1400,
height = 600, height = 600,
scale = {0.5, 1, 0.5}, scale = { 0.5, 1, 0.5 },
}) })
self.createButton({ self.createButton({
click_function = "createCampaignToken", click_function = "createCampaignToken",
function_owner = self, function_owner = self,
label = "Export", label = "Export",
tooltip = "Create a campaign save token!\n\n(Ensure all chaos tokens have been unsealed!)", 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, font_size = 400,
width = 1400, width = 1400,
height = 600, height = 600,
scale = {0.5, 1, 0.5}, scale = { 0.5, 1, 0.5 },
}) })
end 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) -- Identifies import token, determines campaign box and downloads it (if needed)
function findCampaignFromToken(_, _, _) function findCampaignFromToken(_, _, _)
@ -91,11 +82,13 @@ function findCampaignFromToken(_, _, _)
if #coinObjects == 0 then if #coinObjects == 0 then
broadcastToAll("Could not find importer token", Color.Red) broadcastToAll("Could not find importer token", Color.Red)
elseif #coinObjects > 1 then 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 else
coin = coinObjects[1] coin = coinObjects[1]
local importData = JSON.decode(coin.getGMNotes()) local importData = JSON.decode(coin.getGMNotes())
campaignBoxGUID = importData["box"] campaignBoxGUID = importData["box"]
local campaignBox = getObjectFromGUID(campaignBoxGUID) local campaignBox = getObjectFromGUID(campaignBoxGUID)
if campaignBox.type == "Generic" then if campaignBox.type == "Generic" then
campaignBox.call("buttonClick_download") campaignBox.call("buttonClick_download")
@ -110,24 +103,24 @@ function findCampaignFromToken(_, _, _)
end, end,
function() function()
local obj = getObjectFromGUID(campaignBoxGUID) local obj = getObjectFromGUID(campaignBoxGUID)
if obj == nil then if obj == nil then
return false return false
else else
return obj.type == "Bag" and obj.getLuaScript() ~= "" return obj.type == "Bag" and obj.getLuaScript() ~= ""
end end
end, end,
2, 2,
function() broadcastToAll("Error loading campaign box") end function() broadcastToAll("Error loading campaign box") end
) )
end end
end end
-- After box has been downloaded, places content on table -- After box has been downloaded, places content on table
function placeCampaignFromToken(importData) function placeCampaignFromToken(importData)
getObjectFromGUID(campaignBoxGUID).call("buttonClick_place") getObjectFromGUID(importData["box"]).call("buttonClick_place")
Wait.condition( Wait.condition(
function() createCampaignFromToken(importData) end, function() createCampaignFromToken(importData) end,
function() return findCampaignLog() ~= nil end, function() return findUniqueObjectWithTag("CampaignLog") ~= nil end,
2, 2,
function() broadcastToAll("Error placing campaign box") end function() broadcastToAll("Error placing campaign box") end
) )
@ -135,82 +128,86 @@ end
-- After content is placed on table, conducts all the other import operations -- After content is placed on table, conducts all the other import operations
function createCampaignFromToken(importData) function createCampaignFromToken(importData)
findCampaignLog().destruct() -- destroy existing campaign log and load saved campaign log
--create campaign log findUniqueObjectWithTag("CampaignLog").destruct()
spawnObjectData({data = importData["log"]}) spawnObjectData({ data = importData["log"] })
--create chaos bag
chaosBagApi.setChaosBagState(importData["bag"]) chaosBagApi.setChaosBagState(importData["bag"])
--populate trauma values
-- populate trauma values
if importData["trauma"] then if importData["trauma"] then
updateCounters(importData["trauma"]) setTrauma(importData["trauma"])
end end
--populate ArkhamDB deck IDs
-- populate ArkhamDB deck IDs
if importData["decks"] then if importData["decks"] then
deckImporterApi.setUiState(importData["decks"]) deckImporterApi.setUiState(importData["decks"])
end end
--set investigator count
playAreaApi.setInvestigatorCount(importData["clueCount"]) playAreaApi.setInvestigatorCount(importData["clueCount"])
--set campaign guide page
local guide = findCampaignGuide() -- set campaign guide page
local guide = findUniqueObjectWithTag("CampaignGuide")
if guide then if guide then
Wait.condition( Wait.condition(
-- Called after the condition function returns true -- Called after the condition function returns true
function() function() log("Campaign Guide import successful!") end,
log("Campaign Guide import successful!") -- Condition function that is called continiously until returs true or timeout is reached
end, function() return guide.Book.setPage(importData["guide"]) 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,
-- Amount of time in seconds until the Wait times out -- Amount of time in seconds until the Wait times out
1, 1,
-- Called if the Wait times out -- Called if the Wait times out
function() function() log("Campaign Guide import failed!") end
log("Campaign Guide import failed!")
end
) )
end end
Wait.time(
function() optionPanelApi.loadSettings(importData["options"]) end, Wait.time(function() optionPanelApi.loadSettings(importData["options"]) end, 0.5)
0.5
) -- destroy Tour Starter token
getObjectFromGUID(TOUR_GUID).destruct() local tourStarter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TourStarter")
tourStarter.destruct()
-- restore PlayArea image
playAreaApi.updateSurface(importData["playmat"]) playAreaApi.updateSurface(importData["playmat"])
broadcastToAll("Campaign successfully imported!", Color.Green) broadcastToAll("Campaign successfully imported!", Color.Green)
end end
-- Creates a campaign token with save data encoded into GM Notes based on the current state of the table -- Creates a campaign token with save data encoded into GM Notes based on the current state of the table
function createCampaignToken(_, playerColor, _) function createCampaignToken(_, playerColor, _)
-- clean up chaos tokens -- clean up chaos tokens
blessCurseApi.removeAll(playerColor) blessCurseApi.removeAll(playerColor)
chaosBagApi.releaseAllSealedTokens(playerColor) chaosBagApi.releaseAllSealedTokens(playerColor)
local campaignBoxGUID = ""
-- find active campaign -- find active campaign
local campaignBox
for _, obj in ipairs(getObjectsWithTag("CampaignBox")) do for _, obj in ipairs(getObjectsWithTag("CampaignBox")) do
if obj.type == "Bag" and #obj.getObjects() == 0 then 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) broadcastToAll("Multiple empty campaign box detected; delete all but one.", Color.Red)
return return
end end
campaignBoxGUID = obj.getGUID()
end end
end end
if campaignBoxGUID == "" then if not campaignBox then
broadcastToAll("Campaign box with all placed objects not found!", Color.Red) broadcastToAll("Campaign box with all placed objects not found!", Color.Red)
return return
end end
local campaignLog = findCampaignLog()
local campaignLog = findUniqueObjectWithTag("CampaignLog")
if campaignLog == nil then if campaignLog == nil then
broadcastToAll("Campaign log not found!", Color.Red) broadcastToAll("Campaign log not found!", Color.Red)
return return
end end
local traumaValues = nil
local traumaValues = {
0, 0, 0, 0,
0, 0, 0, 0
}
local counterData = campaignLog.getVar("ref_buttonData") local counterData = campaignLog.getVar("ref_buttonData")
if counterData ~= nil then if counterData ~= nil then
traumaValues = {}
printToAll("Trauma values found in campaign log!", "Green") printToAll("Trauma values found in campaign log!", "Green")
for i = 1, 10, 3 do for i = 1, 10, 3 do
traumaValues[1 + (i - 1) / 3] = counterData.counter[i].value 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("Trauma values could not be found in campaign log!", "Yellow")
printToAll("Default values for health and sanity loaded.", "Yellow") printToAll("Default values for health and sanity loaded.", "Yellow")
end end
local campaignGuide = findCampaignGuide()
local campaignGuide = findUniqueObjectWithTag("CampaignGuide")
if campaignGuide == nil then if campaignGuide == nil then
broadcastToAll("Campaign guide not found!", Color.Red) broadcastToAll("Campaign guide not found!", Color.Red)
return return
end end
local campaignGuidePage = campaignGuide.Book.getPage()
local campaignData = { local campaignData = {
box = campaignBoxGUID, box = campaignBox.getGUID(),
log = campaignLog.getData(), log = campaignLog.getData(),
bag = chaosBagApi.getChaosBagState(), bag = chaosBagApi.getChaosBagState(),
trauma = traumaValues, trauma = traumaValues,
decks = deckImporterApi.getUiState(), decks = deckImporterApi.getUiState(),
clueCount = playAreaApi.getInvestigatorCount(), clueCount = playAreaApi.getInvestigatorCount(),
guide = campaignGuidePage, guide = campaignGuide.Book.getPage(),
options = optionPanelApi.getOptions(), options = optionPanelApi.getOptions(),
playmat = playAreaApi.getSurface() playmat = playAreaApi.getSurface()
} }
campaignTokenData.GMNotes = JSON.encode(campaignData) campaignTokenData.GMNotes = JSON.encode(campaignData)
campaignTokenData.Nickname = os.date("%b %d ") .. getObjectFromGUID(campaignBoxGUID).getName() .. " Save" campaignTokenData.Nickname = os.date("%b %d ") .. campaignBox.getName() .. " Save"
spawnObjectData({ spawnObjectData({ data = campaignTokenData })
data = campaignTokenData,
position = {-21.25, 1.68, 55.59}
})
broadcastToAll("Campaign successfully exported! Save coin object to import on a fresh save", Color.Green) broadcastToAll("Campaign successfully exported! Save coin object to import on a fresh save", Color.Green)
end end
---------------------------------------------------------
-- helper functions -- helper functions
---------------------------------------------------------
function findCampaignLog() function findUniqueObjectWithTag(tag)
local campaignLog = getObjectsWithTag("CampaignLog") local objects = getObjectsWithTag(tag)
if campaignLog then if not objects then return end
if #campaignLog == 1 then
return campaignLog[1] if #objects == 1 then
else return objects[1]
broadcastToAll("More than 1 campaign log detected; delete all but one.", Color.Red)
return nil
end
else else
broadcastToAll("More than 1 " .. tag .. " detected; delete all but one.", Color.Red)
return nil return nil
end end
end end
function findCampaignGuide() function setTrauma(trauma)
local campaignGuide = getObjectsWithTag("CampaignGuide") for i = 1, 4 do
if campaignGuide then playmatApi.updateCounter(COLORS[i], "DamageCounter", trauma[i])
if #campaignGuide == 1 then playmatApi.updateCounter(COLORS[i], "HorrorCounter", trauma[i + 4])
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
end end
end end

View File

@ -3,79 +3,24 @@
- puts everything on playmats and hands into respective trashcans - puts everything on playmats and hands into respective trashcans
- use the IGNORE_TAG to exclude objects from tidying (default: "CleanUpHelper_Ignore")]] - use the IGNORE_TAG to exclude objects from tidying (default: "CleanUpHelper_Ignore")]]
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local chaosBagApi = require("chaosbag/ChaosBagApi") local chaosBagApi = require("chaosbag/ChaosBagApi")
local playAreaApi = require("core/PlayAreaApi") local guidReferenceApi = require("core/GUIDReferenceApi")
local playmatApi = require("playermat/PlaymatApi") local playAreaApi = require("core/PlayAreaApi")
local soundCubeApi = require("core/SoundCubeApi") local playmatApi = require("playermat/PlaymatApi")
local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") 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"
}
-- objects with this tag will be ignored -- objects with this tag will be ignored
local IGNORE_TAG = "CleanUpHelper_ignore" local IGNORE_TAG = "CleanUpHelper_ignore"
-- colors and order for following tables -- colors and order for following tables
local COLORS = { "White", "Orange", "Green", "Red", "Agenda" } local COLORS = { "White", "Orange", "Green", "Red", "Mythos" }
-- counter GUIDS (4x damage and 4x horror)
local DAMAGE_HORROR_GUIDS = {
"eb08d6", "e64eec", "1f5a0a", "591a45",
"468e88", "0257d9", "7b5729", "beb964",
}
local campaignLog local campaignLog
local RESET_VALUES = {} local RESET_VALUES = {}
local loadingFailedBefore = false
-- 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 optionsVisible = false local optionsVisible = false
local options = {} local options = {}
options["importTrauma"] = true options["importTrauma"] = true
options["tidyPlayermats"] = true options["tidyPlayermats"] = true
@ -84,7 +29,6 @@ options["removeDrawnLines"] = false
local buttonParameters = {} local buttonParameters = {}
buttonParameters.function_owner = self buttonParameters.function_owner = self
local loadingFailedBefore = false
--------------------------------------------------------- ---------------------------------------------------------
-- option loading and GUI setup -- option loading and GUI setup
@ -131,14 +75,6 @@ function onLoad(savedData)
buttonParameters.position.z = 1.1 buttonParameters.position.z = 1.1
buttonParameters.width = 1550 buttonParameters.width = 1550
self.createButton(buttonParameters) 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 end
--------------------------------------------------------- ---------------------------------------------------------
@ -178,13 +114,8 @@ function cleanUp(_, color)
getTrauma() getTrauma()
-- delay to account for potential state change -- delay to account for potential state change
Wait.time(function() Wait.time(updateCounters, 0.2)
updateCounters(RESOURCE_GUIDS, 5, "Resource")
updateCounters(CLUE_CLICKER_GUIDS, 0, "Clue clicker")
updateCounters(DAMAGE_HORROR_GUIDS, RESET_VALUES, "Damage / Horror")
end, 0.2)
resetSkillTrackers()
resetDoomCounter() resetDoomCounter()
blessCurseManagerApi.removeAll(color) blessCurseManagerApi.removeAll(color)
removeLines() removeLines()
@ -201,39 +132,20 @@ end
-- modular functions, called by other functions -- modular functions, called by other functions
--------------------------------------------------------- ---------------------------------------------------------
function updateCounters(tableOfGUIDs, newValues, info) function updateCounters()
-- instead of a table, this will be used if just a single value is provided playmatApi.updateCounter("All", "ResourceCounter" , 5)
local singleValue = tonumber(newValues) playmatApi.updateCounter("All", "ClickableClueCounter" , 0)
playmatApi.resetSkillTracker("All")
for i, guid in ipairs(tableOfGUIDs) do for i = 1, 4 do
local TOKEN = getObjectFromGUID(guid) playmatApi.updateCounter(COLORS[i], "DamageCounter", RESET_VALUES.Damage[i])
local newValue = singleValue or newValues[i] playmatApi.updateCounter(COLORS[i], "HorrorCounter", RESET_VALUES.Horror[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
end end
end end
-- reset doom on agenda -- reset doom on agenda
function resetDoomCounter() function resetDoomCounter()
local doomCounter = getObjectFromGUID("85c4c6") local doomCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomCounter")
if doomCounter ~= nil then if doomCounter ~= nil then
doomCounter.call("updateVal") doomCounter.call("updateVal")
else else
@ -241,19 +153,19 @@ function resetDoomCounter()
end end
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() function ignoreCustomDataHelper()
local customDataHelper = playAreaApi.getCustomDataHelper() local customDataHelper = playAreaApi.getCustomDataHelper()
if customDataHelper then if customDataHelper then
table.insert(IGNORE_GUIDS, customDataHelper.getGUID()) customDataHelper.addTag(IGNORE_TAG)
end end
end end
-- read values for trauma from campaign log if enabled -- read values for trauma from campaign log if enabled
function getTrauma() function getTrauma()
RESET_VALUES = { RESET_VALUES = {
0, 0, 0, 0, Damage = { 0, 0, 0, 0 },
0, 0, 0, 0 Horror = { 0, 0, 0, 0 }
} }
-- stop here if trauma import is disabled -- stop here if trauma import is disabled
@ -263,13 +175,12 @@ function getTrauma()
end end
-- get campaign log -- get campaign log
campaignLog = findObjects(6)[1] campaignLog = getObjectsWithTag("CampaignLog")[1]
if campaignLog == nil then if campaignLog == nil then
printToAll("Campaign log not found in standard position!", "Yellow") printToAll("Campaign log not found in standard position!", "Yellow")
printToAll("Default values for health and sanity loaded.", "Yellow") printToAll("Default values for health and sanity loaded.", "Yellow")
return return
end end
campaignLog = campaignLog.hit_object
loadTrauma() loadTrauma()
end end
@ -280,7 +191,14 @@ function loadTrauma()
if trauma ~= nil then if trauma ~= nil then
printToAll("Trauma values found in campaign log!", "Green") 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 loadingFailedBefore = false
elseif loadingFailedBefore then elseif loadingFailedBefore then
printToAll("Trauma values could not be found in campaign log!", "Yellow") printToAll("Trauma values could not be found in campaign log!", "Yellow")
@ -303,7 +221,7 @@ end
-- remove drawn lines -- remove drawn lines
function removeLines() function removeLines()
if options["removeDrawnLines"] then if options["removeDrawnLines"] then
printToAll("Removing vector lines...", "White") printToAll("Removing global vector lines...", "White")
Global.setVectorLines({}) Global.setVectorLines({})
end end
end end
@ -312,40 +230,40 @@ end
function discardHands() function discardHands()
if not options["tidyPlayermats"] then return end if not options["tidyPlayermats"] then return end
for i = 1, 4 do for i = 1, 4 do
local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[i]) local trash = guidReferenceApi.getObjectByOwnerAndType(COLORS[i], "Trash")
if trashcan == nil then return end if trash == nil then return end
local hand = Player[playmatApi.getPlayerColor(COLORS[i])].getHandObjects() local hand = Player[playmatApi.getPlayerColor(COLORS[i])].getHandObjects()
for j = #hand, 1, -1 do for j = #hand, 1, -1 do
trashcan.putObject(hand[j]) trash.putObject(hand[j])
end end
end end
end end
-- clean up for play area -- clean up for play area
function tidyPlayareaCoroutine() function tidyPlayareaCoroutine()
local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[5]) local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash")
local PLAYMATZONE = getObjectFromGUID("a2f932") 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") 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") printToAll("Trashcan for main play area could not be found!", "Red")
else else
for _, obj in ipairs(PLAYMATZONE.getObjects()) do for _, obj in ipairs(playAreaZone.getObjects()) do
-- ignore these elements -- 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) coroutine.yield(0)
trashcan.putObject(obj) trash.putObject(obj)
end end
end end
end end
printToAll("Tidying playermats and agenda mat...", "White") printToAll("Tidying playermats and mythos area...", "White")
startLuaCoroutine(self, "tidyPlayerMatCoroutine") startLuaCoroutine(self, "tidyPlayerMatCoroutine")
return 1 return 1
end end
-- clean up for the four playermats and the agenda/act playmat -- clean up for the four playermats and the mythos area
function tidyPlayerMatCoroutine() function tidyPlayerMatCoroutine()
for i = 1, 5 do for i = 1, 5 do
-- only continue for playermat (1-4) if option enabled -- only continue for playermat (1-4) if option enabled
@ -353,32 +271,38 @@ function tidyPlayerMatCoroutine()
-- delay for animation purpose -- delay for animation purpose
for k = 1, 30 do coroutine.yield(0) end for k = 1, 30 do coroutine.yield(0) end
-- get respective trashcan -- get respective trash
local trashcan = getObjectFromGUID(TRASHCAN_GUIDS[i]) local trash = guidReferenceApi.getObjectByOwnerAndType(COLORS[i], "Trash")
if trashcan == nil then if trash == nil then
printToAll("Trashcan for " .. COLORS[i] .. " playmat could not be found!", "Red") printToAll("Trashcan for " .. COLORS[i] .. " playmat could not be found!", "Red")
return 1 return 1
end end
for _, entry in ipairs(findObjects(i)) do local objList
local obj = entry.hit_object if i < 5 then
local desc_low = string.lower(obj.getDescription()) objList = playmatApi.searchAroundPlaymat(COLORS[i])
else
objList = searchMythosArea()
end
for _, obj in ipairs(objList) do
-- ignore these elements -- ignore these elements
if not tableContains(IGNORE_GUIDS, obj.getGUID()) and obj.hasTag(IGNORE_TAG) == false and if obj.hasTag(IGNORE_TAG) == false
desc_low ~= "chaos bag" and desc_low ~= "action token" then and obj.hasTag("ActionToken") == false
and obj.hasTag("chaosBag") == false
and checkMemo(obj) == false then
coroutine.yield(0) coroutine.yield(0)
trashcan.putObject(obj) trash.putObject(obj)
-- flip action tokens back to ready -- flip action tokens back to ready
elseif desc_low == "action token" and obj.is_face_down then elseif obj.hasTag("ActionToken") == false and obj.is_face_down then
obj.flip() obj.flip()
end end
end end
-- reset "activeInvestigatorId" -- reset "activeInvestigatorId"
if i < 5 then if i < 5 then
local playermat = getObjectFromGUID(PLAYERMAT_GUIDS[i]) local playermat = guidReferenceApi.getObjectByOwnerAndType(COLORS[i], "Playermat")
if playermat then if playermat then
playermat.setVar("activeInvestigatorId", "00000") playermat.setVar("activeInvestigatorId", "00000")
end end
@ -386,7 +310,7 @@ function tidyPlayerMatCoroutine()
end end
end end
local datahelper = getObjectFromGUID("708279") local datahelper = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper")
if datahelper then if datahelper then
datahelper.setTable("SPAWNED_PLAYER_CARD_GUIDS", {}) datahelper.setTable("SPAWNED_PLAYER_CARD_GUIDS", {})
end end
@ -399,23 +323,31 @@ end
-- helper functions -- helper functions
--------------------------------------------------------- ---------------------------------------------------------
-- find objects depending on index (1 to 4 for playermats, 5 for agenda/act playmat, 6 for campaign log) -- find objects in the mythos area
function findObjects(num) function searchMythosArea()
return Physics.cast({ local searchResult = Physics.cast({
direction = { 0, 1, 0 }, direction = { 0, 1, 0 },
max_distance = 1, max_distance = 1,
type = 3, type = 3,
size = PHYSICS_SCALE[num], size = { 55, 1, 13.5 },
origin = PHYSICS_POSITION[num], origin = { -2, 2, 10 },
orientation = { 0, PHYSICS_ROTATION[num], 0 }, orientation = { 0, 270, 0 },
debug = false debug = false
}) })
local objList = {}
for _, v in ipairs(searchResult) do
table.insert(objList, v.hit_object)
end
return objList
end end
-- search a table for a value, return true if found (else returns false) -- checks if the object is owned by a playermat or the mythos, returns boolean
function tableContains(table, value) function checkMemo(obj)
for _, v in ipairs(table) do local memo = obj.getMemo()
if v == value then if memo then
local decoded = JSON.decode(memo) or {}
if decoded.matColor then
return true return true
end end
end end

View File

@ -1,11 +1,12 @@
do do
local TokenArrangerApi = {} local TokenArrangerApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
-- local function to call the token arranger, if it is on the table -- local function to call the token arranger, if it is on the table
---@param functionName String Name of the function to cal ---@param functionName String Name of the function to cal
---@param argument Variant Parameter to pass ---@param argument Variant Parameter to pass
local function callIfExistent(functionName, argument) local function callIfExistent(functionName, argument)
local tokenArranger = getObjectsWithTag("TokenArranger")[1] local tokenArranger = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TokenArranger")
if tokenArranger ~= nil then if tokenArranger ~= nil then
tokenArranger.call(functionName, argument) tokenArranger.call(functionName, argument)
end end

View File

@ -1,7 +1,11 @@
do do
local DeckImporterApi = {} 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. -- 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 -- This can be used to persist via onSave(), or provide values for a load operation
-- Table values: -- Table values:
@ -14,7 +18,7 @@ do
-- investigators: True if investigator cards should be spawned -- investigators: True if investigator cards should be spawned
DeckImporterApi.getUiState = function() DeckImporterApi.getUiState = function()
local passthroughTable = {} 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 passthroughTable[k] = v
end end
return passthroughTable return passthroughTable
@ -31,7 +35,7 @@ do
-- loadNewest: True if the most upgraded version of the deck should be loaded -- loadNewest: True if the most upgraded version of the deck should be loaded
-- investigators: True if investigator cards should be spawned -- investigators: True if investigator cards should be spawned
DeckImporterApi.setUiState = function(uiStateTable) DeckImporterApi.setUiState = function(uiStateTable)
return getObjectFromGUID(DECK_IMPORTER_GUID).call("setUiState", uiStateTable) return getDeckImporter().call("setUiState", uiStateTable)
end end
return DeckImporterApi return DeckImporterApi

View File

@ -58,36 +58,9 @@ end
-- loadNewest: True if the most upgraded version of the deck should be loaded -- loadNewest: True if the most upgraded version of the deck should be loaded
-- investigators: True if investigator cards should be spawned -- investigators: True if investigator cards should be spawned
function setUiState(uiStateTable) function setUiState(uiStateTable)
-- Callback functions aren't triggered when editing buttons/inputs so values must be set manually self.clearButtons()
self.clearInputs()
if uiStateTable["greenDeck"] then initializeUi(uiStateTable)
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
end end
-- Sets up the UI for the deck loader, populating fields from the given save state table decoded from onLoad() -- Sets up the UI for the deck loader, populating fields from the given save state table decoded from onLoad()

View File

@ -1,10 +1,14 @@
do do
local BlessCurseManagerApi = {} 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 -- removes all taken tokens and resets the counts
BlessCurseManagerApi.removeTakenTokensAndReset = function() 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", "Bless") end, 0.05)
Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Curse") end, 0.10) Wait.time(function() BlessCurseManager.call("removeTakenTokens", "Curse") end, 0.10)
Wait.time(function() BlessCurseManager.call("doReset", "White") end, 0.15) 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) -- updates the internal count (called by cards that seal bless/curse tokens)
BlessCurseManagerApi.sealedToken = function(type, guid) BlessCurseManagerApi.sealedToken = function(type, guid)
getObjectFromGUID(MANAGER_GUID).call("sealedToken", { type = type, guid = guid }) getManager().call("sealedToken", { type = type, guid = guid })
end end
-- updates the internal count (called by cards that seal bless/curse tokens) -- updates the internal count (called by cards that seal bless/curse tokens)
BlessCurseManagerApi.releasedToken = function(type, guid) BlessCurseManagerApi.releasedToken = function(type, guid)
getObjectFromGUID(MANAGER_GUID).call("releasedToken", { type = type, guid = guid }) getManager().call("releasedToken", { type = type, guid = guid })
end end
-- broadcasts the current status for bless/curse tokens -- broadcasts the current status for bless/curse tokens
---@param playerColor String Color of the player to show the broadcast to ---@param playerColor String Color of the player to show the broadcast to
BlessCurseManagerApi.broadcastStatus = function(playerColor) BlessCurseManagerApi.broadcastStatus = function(playerColor)
getObjectFromGUID(MANAGER_GUID).call("broadcastStatus", playerColor) getManager().call("broadcastStatus", playerColor)
end end
-- removes all bless / curse tokens from the chaos bag and play -- removes all bless / curse tokens from the chaos bag and play
---@param playerColor String Color of the player to show the broadcast to ---@param playerColor String Color of the player to show the broadcast to
BlessCurseManagerApi.removeAll = function(playerColor) BlessCurseManagerApi.removeAll = function(playerColor)
getObjectFromGUID(MANAGER_GUID).call("doRemove", playerColor) getManager().call("doRemove", playerColor)
end end
-- adds Wendy's menu to the hovered card (allows sealing of tokens) -- adds Wendy's menu to the hovered card (allows sealing of tokens)
---@param color String Color of the player to show the broadcast to ---@param color String Color of the player to show the broadcast to
BlessCurseManagerApi.addWendysMenu = function(playerColor, hoveredObject) BlessCurseManagerApi.addWendysMenu = function(playerColor, hoveredObject)
getObjectFromGUID(MANAGER_GUID).call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject }) getManager().call("addMenuOptions", { playerColor = playerColor, hoveredObject = hoveredObject })
end end
return BlessCurseManagerApi return BlessCurseManagerApi

View File

@ -1,3 +1,5 @@
local guidReferenceApi = require("core/GUIDReferenceApi")
local optionsVisible = false local optionsVisible = false
local options = { local options = {
Agenda = true, Agenda = true,
@ -64,10 +66,9 @@ function startReset()
if options.Agenda then if options.Agenda then
updateVal(0) updateVal(0)
end end
-- call the "Doom-in-Play"-counter local doomInPlayCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomInPlayCounter")
local DoomInPlayCounter = getObjectFromGUID("652ff3") if doomInPlayCounter then
if DoomInPlayCounter then doomInPlayCounter.call("removeDoom", options)
DoomInPlayCounter.call("removeDoom", options)
end end
end end

View File

@ -1,25 +1,24 @@
-- common parameters local guidReferenceApi = require("core/GUIDReferenceApi")
local castParameters = {} local playmatApi = require("playermat/PlaymatApi")
castParameters.direction = { 0, 1, 0 }
castParameters.type = 3
castParameters.max_distance = 0
local zone local ZONE, TRASH, loopID
local doomURL = "https://i.imgur.com/EoL7yaZ.png" local doomURL = "https://i.imgur.com/EoL7yaZ.png"
local IGNORE_TAG = "DoomCounter_ignore" local IGNORE_TAG = "DoomCounter_ignore"
local TOTAL_PLAY_AREA = {
-- playermats 1 to 4 upperLeft = {
local originAndSize = { x = -10,
{ origin = { -55, 1.6, 16.5 }, size = { 12, 1, 25 } }, z = -35
{ origin = { -55, 1.6, -16.5 }, size = { 12, 1, 25 } }, },
{ origin = { -25, 1.6, 27 }, size = { 25, 1, 12 } }, lowerRight = {
{ origin = { -25, 1.6, -27 }, size = { 25, 1, 12 } } x = -60,
z = 35
}
} }
-- create button, context menu and start loop -- create button, context menu and start loop
function onLoad() function onLoad()
self.createButton({ self.createButton({
label = tostring(0), label = "0",
click_function = "none", click_function = "none",
function_owner = self, function_owner = self,
position = { 0, 0.06, 0 }, position = { 0, 0.06, 0 },
@ -31,76 +30,65 @@ function onLoad()
color = { 0, 0, 0, 0 } color = { 0, 0, 0, 0 }
}) })
zone = getObjectFromGUID("a2f932") TRASH = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash")
ZONE = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayAreaZone")
loopID = Wait.time(countDoom, 2, -1) loopID = Wait.time(countDoom, 2, -1)
end end
-- main function -- main function
function countDoom() function countDoom()
local doom = 0 local count = 0
for i = 1, 5 do doom = doom + search(i) end
self.editButton({ index = 0, label = tostring(doom) }) -- get doom in play
for _, obj in ipairs(getObjects()) do
count = count + getDoomAmount(obj)
end
self.editButton({ index = 0, label = tostring(count) })
end end
-- searches playermats (num = 1-4) or the scripting zone (num = 5) -- gets quantity (for stacks) of doom
function search(num) function getDoomAmount(obj)
local val = 0 if (obj.is_face_down and obj.getCustomObject().image_bottom == doomURL)
if num == 5 then and not obj.hasTag(IGNORE_TAG)
for _, obj in ipairs(zone.getObjects()) do and inArea(obj.getPosition(), TOTAL_PLAY_AREA) then
val = val + isDoom(obj) return math.abs(obj.getQuantity())
end
else else
castParameters.origin = originAndSize[num].origin return 0
castParameters.size = originAndSize[num].size
for _, obj in ipairs(Physics.cast(castParameters)) do
val = val + isDoom(obj.hit_object)
end
end 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 end
-- removes doom from playermats / playarea -- removes doom from playermats / playarea
function removeDoom(options) function removeDoom(options)
local trashCan = getObjectFromGUID("70b9f6")
local count = 0 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 if options.Playermats then
local obj = obj.hit_object count = removeDoomFromList(playmatApi.searchAroundPlaymat("All"))
local amount = isDoom(obj)
if amount > 0 then
trashCan.putObject(obj)
count = count + amount
end
end
end
broadcastToAll(count .. " doom removed from Playermats.", "White") broadcastToAll(count .. " doom removed from Playermats.", "White")
end end
local count = 0
if options.Playarea then if options.Playarea then
for _, obj in ipairs(zone.getObjects()) do count = removeDoomFromList(ZONE.getObjects())
local amount = isDoom(obj) broadcastToAll(count .. " doom removed from Playerarea.", "White")
if amount > 0 then
trashCan.putObject(obj)
count = count + amount
end
end
broadcastToAll(count .. " doom removed from Playarea.", "White")
end end
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

View File

@ -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

View File

@ -0,0 +1,100 @@
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",
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

View File

@ -1,4 +1,5 @@
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local guidReferenceApi = require("core/GUIDReferenceApi")
local optionPanelApi = require("core/OptionPanelApi") local optionPanelApi = require("core/OptionPanelApi")
local playmatApi = require("playermat/PlaymatApi") local playmatApi = require("playermat/PlaymatApi")
local victoryDisplayApi = require("core/VictoryDisplayApi") local victoryDisplayApi = require("core/VictoryDisplayApi")
@ -41,7 +42,8 @@ end
-- adds 1 doom to the agenda -- adds 1 doom to the agenda
function addDoomToAgenda() function addDoomToAgenda()
getObjectFromGUID("85c4c6").call("addVal", 1) local doomCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomCounter")
doomCounter.call("addVal", 1)
end end
-- moves the hovered card to the victory display -- moves the hovered card to the victory display
@ -105,7 +107,7 @@ function takeClueFromLocation(playerColor, hoveredObject)
local pos = nil local pos = nil
if clickableClues then if clickableClues then
pos = {x = 0.49, y = 2.66, z = 0.00} pos = {x = 0.49, y = 2.66, z = 0.00}
playmatApi.updateClueClicker(playerColor, playmatApi.getClueCount(clickableClues, playerColor) + 1) playmatApi.updateCounter(matColor, "ClickableClueCounter", _, 1)
else else
pos = playmatApi.transformLocalPosition({x = -1.12, y = 0.05, z = 0.7}, matColor) pos = playmatApi.transformLocalPosition({x = -1.12, y = 0.05, z = 0.7}, matColor)
end end

View File

@ -1,4 +1,5 @@
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local guidReferenceApi = require("core/GUIDReferenceApi")
local mythosAreaApi = require("core/MythosAreaApi") local mythosAreaApi = require("core/MythosAreaApi")
local navigationOverlayApi = require("core/NavigationOverlayApi") local navigationOverlayApi = require("core/NavigationOverlayApi")
local playAreaApi = require("core/PlayAreaApi") local playAreaApi = require("core/PlayAreaApi")
@ -12,11 +13,8 @@ local tokenManager = require("core/token/TokenManager")
-- general setup -- general setup
--------------------------------------------------------- ---------------------------------------------------------
ENCOUNTER_DECK_POS = {-3.93, 1, 5.76} ENCOUNTER_DECK_POS = { -3.93, 1, 5.76 }
ENCOUNTER_DECK_DISCARD_POSITION = {-3.85, 1, 10.38} ENCOUNTER_DECK_DISCARD_POSITION = { -3.85, 1, 10.38 }
-- GUID of data helper
DATA_HELPER_GUID = "708279"
-- GUIDs that will not be interactable (e.g. parts of the table) -- GUIDs that will not be interactable (e.g. parts of the table)
local NOT_INTERACTABLE = { local NOT_INTERACTABLE = {
@ -37,7 +35,7 @@ chaosTokens = {}
local chaosTokensLastMat = nil local chaosTokensLastMat = nil
local bagSearchers = {} local bagSearchers = {}
local MAT_COLORS = {"White", "Orange", "Green", "Red"} local MAT_COLORS = { "White", "Orange", "Green", "Red" }
local hideTitleSplashWaitFunctionId = nil local hideTitleSplashWaitFunctionId = nil
-- online functionality related variables -- online functionality related variables
@ -115,14 +113,6 @@ ID_URL_MAP = {
-- data for chaos token stat tracker -- data for chaos token stat tracker
--------------------------------------------------------- ---------------------------------------------------------
local MAT_GUID_TO_COLOR = {
["Overall"] = "Overall",
["8b081b"] = "White",
["bd0ff4"] = "Orange",
["383d8b"] = "Green",
["0840d5"] = "Red"
}
local tokenDrawingStats = { local tokenDrawingStats = {
["Overall"] = {}, ["Overall"] = {},
["8b081b"] = {}, ["8b081b"] = {},
@ -136,7 +126,12 @@ local tokenDrawingStats = {
--------------------------------------------------------- ---------------------------------------------------------
-- saving state of optionPanel to restore later -- 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) function onLoad(savedData)
if savedData then if savedData then
@ -308,8 +303,8 @@ function handleStatTrackerClick(_, _, isRightClick)
if isRightClick then if isRightClick then
resetChaosTokenStatTracker() resetChaosTokenStatTracker()
else else
local squidKing = "Nobody" local squidKing = "Nobody"
local maxSquid = 0 local maxSquid = 0
local foundAnyStats = false local foundAnyStats = false
for key, personalStats in pairs(tokenDrawingStats) do for key, personalStats in pairs(tokenDrawingStats) do
@ -319,7 +314,9 @@ function handleStatTrackerClick(_, _, isRightClick)
playerColor = "White" playerColor = "White"
playerName = "Overall" playerName = "Overall"
else 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 playerName = Player[playerColor].steam_name or playerColor
local playerSquidCount = personalStats["Auto-fail"] local playerSquidCount = personalStats["Auto-fail"]
@ -339,8 +336,8 @@ function handleStatTrackerClick(_, _, isRightClick)
if totalCount > 0 then if totalCount > 0 then
foundAnyStats = true foundAnyStats = true
printToAll("------------------------------") printToAll("------------------------------")
printToAll(playerName .. " Stats", playerColor) printToAll(playerName .. " Stats", playerColor)
for tokenName, value in pairs(personalStats) do for tokenName, value in pairs(personalStats) do
if value ~= 0 then if value ~= 0 then
printToAll(tokenName .. ': ' .. tostring(value)) printToAll(tokenName .. ': ' .. tostring(value))
@ -353,7 +350,7 @@ function handleStatTrackerClick(_, _, isRightClick)
-- detect if any player drew tokens -- detect if any player drew tokens
if foundAnyStats then if foundAnyStats then
printToAll("------------------------------") printToAll("------------------------------")
printToAll(squidKing .. " is an auto-fail magnet.", {255, 0, 0}) printToAll(squidKing .. " is an auto-fail magnet.", { 255, 0, 0 })
else else
printToAll("No tokens have been drawn yet.", "Yellow") printToAll("No tokens have been drawn yet.", "Yellow")
end end
@ -382,11 +379,11 @@ function createSetupButtons(args)
if data ~= nil then if data ~= nil then
local buttonParameters = {} local buttonParameters = {}
buttonParameters.function_owner = args.object buttonParameters.function_owner = args.object
buttonParameters.position = {0, 0.1, -0.15} buttonParameters.position = { 0, 0.1, -0.15 }
buttonParameters.scale = {0.47, 1, 0.47} buttonParameters.scale = { 0.47, 1, 0.47 }
buttonParameters.height = 200 buttonParameters.height = 200
buttonParameters.width = 1150 buttonParameters.width = 1150
buttonParameters.color = {0.87, 0.8, 0.7} buttonParameters.color = { 0.87, 0.8, 0.7 }
if data.easy ~= nil then if data.easy ~= nil then
buttonParameters.label = "Easy" buttonParameters.label = "Easy"
@ -469,7 +466,8 @@ function fillContainer(args)
end end
function getDataValue(storage, key) 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 if data ~= nil then
local value = data[key] local value = data[key]
if value ~= nil then if value ~= nil then
@ -514,7 +512,6 @@ function getChaosBagState()
end end
return tokens return tokens
end end
-- respawns the chaos bag with a new state of tokens -- respawns the chaos bag with a new state of tokens
@ -543,7 +540,7 @@ function setChaosBagState(tokenList)
-- overwrite chaos bag content and respawn it -- overwrite chaos bag content and respawn it
chaosbagData.ContainedObjects = containedObjects chaosbagData.ContainedObjects = containedObjects
chaosbag.destruct() chaosbag.destruct()
spawnObjectData({data = chaosbagData}) spawnObjectData({ data = chaosbagData })
-- remove tokens that are still in play -- remove tokens that are still in play
for _, token in pairs(chaosTokens) do for _, token in pairs(chaosTokens) do
@ -572,7 +569,7 @@ function spawnChaosToken(id)
type = 'Custom_Tile', type = 'Custom_Tile',
position = { 0.49, 3, 0 }, position = { 0.49, 3, 0 },
scale = { 0.81, 1.0, 0.81 }, scale = { 0.81, 1.0, 0.81 },
rotation = {0, 270, 0}, rotation = { 0, 270, 0 },
callback_function = function(obj) callback_function = function(obj)
obj.setName(ID_URL_MAP[id].name) obj.setName(ID_URL_MAP[id].name)
chaosbag.putObject(obj) chaosbag.putObject(obj)
@ -622,7 +619,7 @@ function emptyChaosBag()
local chaosbag = findChaosBag() local chaosbag = findChaosBag()
for _, object in ipairs(chaosbag.getObjects()) do 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
end end
@ -1006,7 +1003,8 @@ function applyOptionPanelChange(id, state)
optionPanel[id] = state optionPanel[id] = state
-- update master clue counter -- update master clue counter
getObjectFromGUID("4a3aa4").setVar("useClickableCounters", state) local counter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "MasterClueCounter")
counter.setVar("useClickableCounters", state)
-- option: Play area snap tags -- option: Play area snap tags
elseif id == "playAreaSnapTags" then elseif id == "playAreaSnapTags" then
@ -1075,8 +1073,9 @@ end
-- copies the specified tool (by name) from the option panel source bag -- copies the specified tool (by name) from the option panel source bag
---@param name String Name of the object that should be copied ---@param name String Name of the object that should be copied
---@param position Table Desired position of the object ---@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) function spawnHelperObject(name, position, rotation)
local sourceBag = getObjectFromGUID("830bd0") local sourceBag = guidReferenceApi.getObjectByOwnerAndType("Mythos","OptionPanelSource")
-- error handling for missing sourceBag -- error handling for missing sourceBag
if not sourceBag then if not sourceBag then
@ -1084,7 +1083,7 @@ function spawnHelperObject(name, position, rotation)
return return
end end
local spawnTable = {position = position} local spawnTable = { position = position }
-- only overrride rotation if there is one provided (object's rotation used instead) -- only overrride rotation if there is one provided (object's rotation used instead)
if rotation then if rotation then
@ -1177,7 +1176,6 @@ end
-- splash scenario title on setup -- splash scenario title on setup
function titleSplash(scenarioName) function titleSplash(scenarioName)
if optionPanel['showTitleSplash'] then if optionPanel['showTitleSplash'] then
-- if there's any ongoing title being displayed, hide it and cancel the waiting function -- if there's any ongoing title being displayed, hide it and cancel the waiting function
if hideTitleSplashWaitFunctionId then if hideTitleSplashWaitFunctionId then
Wait.stop(hideTitleSplashWaitFunctionId) Wait.stop(hideTitleSplashWaitFunctionId)
@ -1219,7 +1217,7 @@ function compareVersion(request)
-- stop here if on latest version -- stop here if on latest version
if MOD_VERSION == modMeta["latestVersion"] then return end if MOD_VERSION == modMeta["latestVersion"] then return end
-- stop here if "don't show again" was clicked for this version before -- stop here if "don't show again" was clicked for this version before
if acknowledgedUpgradeVersions[modMeta["latestVersion"]] then return end if acknowledgedUpgradeVersions[modMeta["latestVersion"]] then return end
@ -1241,9 +1239,9 @@ function updateNotificationLoading()
highlightText = highlightText .. "\n• " .. entry highlightText = highlightText .. "\n• " .. entry
end end
end end
-- update the XML UI -- 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.setValue("releaseHighlightText", highlightText)
UI.setAttribute("highlightRow", "preferredHeight", 20*#highlights) UI.setAttribute("highlightRow", "preferredHeight", 20*#highlights)
UI.setAttribute("updateNotification", "height", 20*#highlights + 125) UI.setAttribute("updateNotification", "height", 20*#highlights + 125)

View File

@ -23,8 +23,7 @@ function onLoad(savedData)
font_color = { 1, 1, 1, 100 }, font_color = { 1, 1, 1, 100 },
color = { 0, 0, 0, 0 } color = { 0, 0, 0, 0 }
}) })
Wait.time(sumClues, 2, -1)
loopID = Wait.time(sumClues, 2, -1)
end end
-- removes all player clues by calling the respective function from the counting bowls / clickers -- removes all player clues by calling the respective function from the counting bowls / clickers

View File

@ -1,3 +1,4 @@
local guidReferenceApi = require("core/GUIDReferenceApi")
local playAreaApi = require("core/PlayAreaApi") local playAreaApi = require("core/PlayAreaApi")
local tokenArrangerApi = require("accessories/TokenArrangerApi") local tokenArrangerApi = require("accessories/TokenArrangerApi")
local tokenChecker = require("core/token/TokenChecker") local tokenChecker = require("core/token/TokenChecker")
@ -20,11 +21,8 @@ local isReshuffling = false
-- scenario metadata -- scenario metadata
local currentScenario, useFrontData, tokenData local currentScenario, useFrontData, tokenData
-- GUID of data helper -- object references
local DATA_HELPER_GUID = "708279" local TRASH, DATA_HELPER
local TRASHCAN
local TRASHCAN_GUID = "70b9f6"
-- we use this to turn off collision handling until onLoad() is complete -- we use this to turn off collision handling until onLoad() is complete
local collisionEnabled = false local collisionEnabled = false
@ -36,7 +34,8 @@ function onLoad(saveState)
useFrontData = loadedState.useFrontData or true useFrontData = loadedState.useFrontData or true
tokenData = loadedState.tokenData or {} tokenData = loadedState.tokenData or {}
end end
TRASHCAN = getObjectFromGUID(TRASHCAN_GUID) TRASH = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash")
DATA_HELPER = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper")
collisionEnabled = true collisionEnabled = true
end end
@ -153,7 +152,7 @@ function actualEncounterCardDraw(card, params)
local faceUpRotation = 0 local faceUpRotation = 0
if not params.alwaysFaceUp then if not params.alwaysFaceUp then
local metadata = JSON.decode(card.getGMNotes()) or {} 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 faceUpRotation = 180
end end
end end
@ -209,7 +208,7 @@ function removeTokensFromObject(object)
obj.memo ~= nil and obj.memo ~= nil and
obj.getLock() == false and obj.getLock() == false and
not tokenChecker.isChaosToken(obj) then not tokenChecker.isChaosToken(obj) then
TRASHCAN.putObject(obj) TRASH.putObject(obj)
end end
end end
end end

View File

@ -1,15 +1,19 @@
do do
local MythosAreaApi = {} 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) -- returns the chaos token metadata (if provided through scenario reference card)
MythosAreaApi.returnTokenData = function() MythosAreaApi.returnTokenData = function()
return getObjectFromGUID(MYTHOS_AREA_GUID).call("returnTokenData") return getMythosArea().call("returnTokenData")
end end
-- draw an encounter card to the requested position/rotation -- draw an encounter card to the requested position/rotation
MythosAreaApi.drawEncounterCard = function(pos, rotY, alwaysFaceUp) MythosAreaApi.drawEncounterCard = function(pos, rotY, alwaysFaceUp)
getObjectFromGUID(MYTHOS_AREA_GUID).call("drawEncounterCard", { getMythosArea().call("drawEncounterCard", {
pos = pos, pos = pos,
rotY = rotY, rotY = rotY,
alwaysFaceUp = alwaysFaceUp alwaysFaceUp = alwaysFaceUp

View File

@ -1,21 +1,25 @@
do do
local NavigationOverlayApi = {} 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 -- Copies the visibility for the Navigation overlay
---@param startColor String Color of the player to copy from ---@param startColor String Color of the player to copy from
---@param targetColor String Color of the targeted player ---@param targetColor String Color of the targeted player
NavigationOverlayApi.copyVisibility = function(startColor, targetColor) NavigationOverlayApi.copyVisibility = function(startColor, targetColor)
getObjectFromGUID(HANDLER_GUID).call("copyVisibility", { getNOHandler().call("copyVisibility", {
startColor = startColor, startColor = startColor,
targetColor = targetColor targetColor = targetColor
}) })
end end
-- Changes the Navigation Overlay view ("Full View" --> "Play Areas" --> "Closed" etc.) -- Changes the Navigation Overlay view ("Full View" --> "Play Areas" --> "Closed" etc.)
---@param playerColor String Color of the player to update the visibility for ---@param playerColor String Color of the player to update the visibility for
NavigationOverlayApi.cycleVisibility = function(playerColor) NavigationOverlayApi.cycleVisibility = function(playerColor)
getObjectFromGUID(HANDLER_GUID).call("cycleVisibility", playerColor) getNOHandler().call("cycleVisibility", playerColor)
end end
return NavigationOverlayApi return NavigationOverlayApi

View File

@ -328,7 +328,7 @@ function loadCamera(player, index)
end end
-- search on the playmat for objects -- search on the playmat for objects
local bounds = getDynamicViewBounds(playmatApi.searchPlaymat(matColor)) local bounds = getDynamicViewBounds(playmatApi.searchAroundPlaymat(matColor))
lookHere = { lookHere = {
position = { bounds.middleX, 0, bounds.middleZ }, position = { bounds.middleX, 0, bounds.middleZ },

View File

@ -3,60 +3,60 @@
--------------------------------------------------------- ---------------------------------------------------------
-- Location connection directional options -- Location connection directional options
local BIDIRECTIONAL = 0 local BIDIRECTIONAL = 0
local ONE_WAY = 1 local ONE_WAY = 1
local INCOMING_ONE_WAY = 2 local INCOMING_ONE_WAY = 2
-- Connector draw parameters -- Connector draw parameters
local CONNECTION_THICKNESS = 0.015 local CONNECTION_THICKNESS = 0.015
local DRAGGING_CONNECTION_THICKNESS = 0.15 local DRAGGING_CONNECTION_THICKNESS = 0.15
local DRAGGING_CONNECTION_COLOR = { 0.8, 0.8, 0.8, 1 } local DRAGGING_CONNECTION_COLOR = { 0.8, 0.8, 0.8, 1 }
local CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 } local CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 }
local DIRECTIONAL_ARROW_DISTANCE = 3.5 local DIRECTIONAL_ARROW_DISTANCE = 3.5
local ARROW_ARM_LENGTH = 0.9 local ARROW_ARM_LENGTH = 0.9
local ARROW_ANGLE = 25 local ARROW_ANGLE = 25
-- Height to draw the connector lines, places them just above the table and always below cards -- 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 -- 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 -- 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 }, left = { x = 0.00, y = 0, z = 7.67 },
right = { 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 }, up = { x = 6.54, y = 0, z = 0.00 },
down = { 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, ["b7b45b"] = true,
["f182ee"] = true, ["f182ee"] = true,
["721ba2"] = true ["721ba2"] = true
} }
local LOC_LINK_EXCLUDE_SCENARIOS = { local LOC_LINK_EXCLUDE_SCENARIOS = {
["The Witching Hour"] = true, ["The Witching Hour"] = true,
["The Heart of Madness"] = true ["The Heart of Madness"] = true
} }
local tokenManager = require("core/token/TokenManager") local guidReferenceApi = require("core/GUIDReferenceApi")
local INVESTIGATOR_COUNTER_GUID = "f182ee" local tokenManager = require("core/token/TokenManager")
local PLAY_AREA_ZONE_GUID = "a2f932"
local clueData = {} local clueData = {}
local spawnedLocationGUIDs = {} local spawnedLocationGUIDs = {}
local locations = {} local locations = {}
local locationConnections = {} local locationConnections = {}
local draggingGuids = {} local draggingGuids = {}
local locationData local locationData
local currentScenario local currentScenario
local missingData = {} local missingData = {}
local countedVP = {} local countedVP = {}
--------------------------------------------------------- ---------------------------------------------------------
-- general code -- general code
@ -71,8 +71,8 @@ end
function onLoad(saveState) function onLoad(saveState)
-- records locations we have spawned clues for -- records locations we have spawned clues for
local save = JSON.decode(saveState) or { } local save = JSON.decode(saveState) or {}
locations = save.trackedLocations or { } locations = save.trackedLocations or {}
currentScenario = save.currentScenario currentScenario = save.currentScenario
self.interactable = false self.interactable = false
@ -93,13 +93,13 @@ function updateSurface(newURL)
local customInfo = self.getCustomObject() local customInfo = self.getCustomObject()
if newURL ~= "" and newURL ~= nil and newURL ~= DEFAULT_URL then if newURL ~= "" and newURL ~= nil and newURL ~= DEFAULT_URL then
customInfo.image = newURL customInfo.image = newURL
broadcastToAll("New Playmat Image Applied", { 0.2, 0.9, 0.2 }) broadcastToAll("New Playmat Image Applied", { 0.2, 0.9, 0.2 })
else else
customInfo.image = DEFAULT_URL customInfo.image = DEFAULT_URL
broadcastToAll("Default Playmat Image Applied", { 0.2, 0.9, 0.2 }) broadcastToAll("Default Playmat Image Applied", { 0.2, 0.9, 0.2 })
end end
self.setCustomObject(customInfo) self.setCustomObject(customInfo)
local guid = nil local guid = nil
@ -108,7 +108,7 @@ function updateSurface(newURL)
self.reload() self.reload()
if guid ~= nil then if guid ~= nil then
Wait.time(function() updateLocations({ guid }) end, 1) Wait.time(function() updateLocations({ guid }) end, 1)
end end
end end
@ -129,12 +129,14 @@ function onCollisionEnter(collisionInfo)
if shouldSpawnTokens(card) then if shouldSpawnTokens(card) then
tokenManager.spawnForCard(card) tokenManager.spawnForCard(card)
end end
-- If this card was being dragged, clear the dragging connections. A multi-drag/drop may send -- 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 -- the dropped card immediately into a deck, so this has to be done here
if draggingGuids[card.getGUID()] ~= nil then if draggingGuids[card.getGUID()] ~= nil then
card.setVectorLines(nil) card.setVectorLines(nil)
draggingGuids[card.getGUID()] = nil draggingGuids[card.getGUID()] = nil
end end
maybeTrackLocation(card) maybeTrackLocation(card)
end end
@ -167,20 +169,20 @@ function onObjectPickUp(player, object)
-- should be tracking -- should be tracking
if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then
local pickedUpGuid = object.getGUID() local pickedUpGuid = object.getGUID()
local metadata = JSON.decode(object.getGMNotes()) or { } local metadata = JSON.decode(object.getGMNotes()) or {}
if metadata.type == "Location" then if metadata.type == "Location" then
-- onCollisionExit sometimes comes 1 frame after onObjectPickUp (rather than before it or in -- 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 -- 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. -- 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 -- Waiting ensures we always do thing in the expected Exit->PickUp order
Wait.frames(function() Wait.frames(function()
if object.is_face_down then if object.is_face_down then
draggingGuids[pickedUpGuid] = metadata.locationBack draggingGuids[pickedUpGuid] = metadata.locationBack
else else
draggingGuids[pickedUpGuid] = metadata.locationFront draggingGuids[pickedUpGuid] = metadata.locationFront
end end
rebuildConnectionList() rebuildConnectionList()
end, 2) end, 2)
end end
end end
end end
@ -273,11 +275,11 @@ end
-- drawBaseConnections() -- drawBaseConnections()
function rebuildConnectionList() function rebuildConnectionList()
if not showLocationLinks() then if not showLocationLinks() then
locationConnections = { } locationConnections = {}
return return
end end
local iconCardList = { } local iconCardList = {}
-- Build a list of cards with each icon as their location ID -- Build a list of cards with each icon as their location ID
for cardId, metadata in pairs(draggingGuids) do for cardId, metadata in pairs(draggingGuids) do
@ -288,7 +290,7 @@ function rebuildConnectionList()
end end
-- Pair up all the icons -- Pair up all the icons
locationConnections = { } locationConnections = {}
for cardId, metadata in pairs(draggingGuids) do for cardId, metadata in pairs(draggingGuids) do
buildConnection(cardId, iconCardList, metadata) buildConnection(cardId, iconCardList, metadata)
end end
@ -307,7 +309,7 @@ function buildLocListByIcon(cardId, iconCardList, locData)
if locData ~= nil and locData.icons ~= nil then if locData ~= nil and locData.icons ~= nil then
for icon in string.gmatch(locData.icons, "%a+") do for icon in string.gmatch(locData.icons, "%a+") do
if iconCardList[icon] == nil then if iconCardList[icon] == nil then
iconCardList[icon] = { } iconCardList[icon] = {}
end end
table.insert(iconCardList[icon], cardId) table.insert(iconCardList[icon], cardId)
end end
@ -321,19 +323,19 @@ end
---@param locData Table A table containing the metadata for the card (for the correct side) ---@param locData Table A table containing the metadata for the card (for the correct side)
function buildConnection(cardId, iconCardList, locData) function buildConnection(cardId, iconCardList, locData)
if locData ~= nil and locData.connections ~= nil then if locData ~= nil and locData.connections ~= nil then
locationConnections[cardId] = { } locationConnections[cardId] = {}
for icon in string.gmatch(locData.connections, "%a+") do for icon in string.gmatch(locData.connections, "%a+") do
if iconCardList[icon] ~= nil then if iconCardList[icon] ~= nil then
for _, connectedGuid in ipairs(iconCardList[icon]) do for _, connectedGuid in ipairs(iconCardList[icon]) do
-- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way -- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way
if locationConnections[connectedGuid] ~= nil if locationConnections[connectedGuid] ~= nil
and (locationConnections[connectedGuid][cardId] == ONE_WAY and (locationConnections[connectedGuid][cardId] == ONE_WAY
or locationConnections[connectedGuid][cardId] == BIDIRECTIONAL) then or locationConnections[connectedGuid][cardId] == BIDIRECTIONAL) then
locationConnections[connectedGuid][cardId] = BIDIRECTIONAL locationConnections[connectedGuid][cardId] = BIDIRECTIONAL
locationConnections[cardId][connectedGuid] = nil locationConnections[cardId][connectedGuid] = nil
else else
if locationConnections[connectedGuid] == nil then if locationConnections[connectedGuid] == nil then
locationConnections[connectedGuid] = { } locationConnections[connectedGuid] = {}
end end
locationConnections[cardId][connectedGuid] = ONE_WAY locationConnections[cardId][connectedGuid] = ONE_WAY
locationConnections[connectedGuid][cardId] = INCOMING_ONE_WAY locationConnections[connectedGuid][cardId] = INCOMING_ONE_WAY
@ -348,10 +350,10 @@ end
-- Constructed vectors will be set to the playmat -- Constructed vectors will be set to the playmat
function drawBaseConnections() function drawBaseConnections()
if not showLocationLinks() then if not showLocationLinks() then
locationConnections = { } locationConnections = {}
return return
end end
local cardConnectionLines = { } local cardConnectionLines = {}
for originGuid, targetGuids in pairs(locationConnections) do for originGuid, targetGuids in pairs(locationConnections) do
-- Objects should reliably exist at this point, but since this can be called during onUpdate the -- 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 if not showLocationLinks() then
return return
end end
local cardConnectionLines = { } local cardConnectionLines = {}
local ownedVectors = { } local ownedVectors = {}
for originGuid, _ in pairs(draggingGuids) do for originGuid, _ in pairs(draggingGuids) do
targetGuids = locationConnections[originGuid] targetGuids = locationConnections[originGuid]
@ -389,7 +391,7 @@ function drawDraggingConnections()
-- object checks are conservative just to make sure. -- object checks are conservative just to make sure.
local origin = getObjectFromGUID(originGuid) local origin = getObjectFromGUID(originGuid)
if draggingGuids[originGuid] and origin ~= nil and targetGuids ~= nil then if draggingGuids[originGuid] and origin ~= nil and targetGuids ~= nil then
ownedVectors[originGuid] = { } ownedVectors[originGuid] = {}
for targetGuid, direction in pairs(targetGuids) do for targetGuid, direction in pairs(targetGuids) do
local target = getObjectFromGUID(targetGuid) local target = getObjectFromGUID(targetGuid)
if target != nil then if target != nil then
@ -427,9 +429,9 @@ function addBidirectionalVector(card1, card2, vectorOwner, lines)
local pos2 = vectorOwner.positionToLocal(cardPos2) local pos2 = vectorOwner.positionToLocal(cardPos2)
table.insert(lines, { table.insert(lines, {
points = { pos1, pos2 }, points = { pos1, pos2 },
color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR, color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR,
thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS, thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS,
}) })
end 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 -- Calculate card distance to be closer for horizontal positions than vertical, since cards are
-- taller than they are wide -- taller than they are wide
local heading = Vector(originPos):sub(targetPos):heading("y") 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 -- 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 -- 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 closeToOrigin = Vector(originPos):moveTowards(targetPos, distanceFromCard + ARROW_ARM_LENGTH / 2)
local closeToTarget = Vector(targetPos):moveTowards(originPos, 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 --- 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 ---@param lines Table List of vector line elements. Mutable, will be updated to add this arrow
function addArrowLines(arrowheadPos, originPos, vectorOwner, lines) 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 arrowArm1 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y",
local arrowArm2 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", ARROW_ANGLE):add(arrowheadPos) -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 head = vectorOwner.positionToLocal(arrowheadPos)
local arm1 = vectorOwner.positionToLocal(arrowArm1) local arm1 = vectorOwner.positionToLocal(arrowArm1)
local arm2 = vectorOwner.positionToLocal(arrowArm2) local arm2 = vectorOwner.positionToLocal(arrowArm2)
table.insert(lines, { table.insert(lines, {
points = { arm1, head, arm2}, points = { arm1, head, arm2 },
color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR, color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR,
thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS, thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS,
}) })
@ -508,7 +514,7 @@ function shiftContentsRight(playerColor)
end end
function shiftContents(playerColor, direction) function shiftContents(playerColor, direction)
local zone = getObjectFromGUID(PLAY_AREA_ZONE_GUID) local zone = guidReferenceApi.getObjectByOwnerAndType("Mythos", "PlayAreaZone")
if not zone then if not zone then
broadcastToColor("Scripting zone couldn't be found.", playerColor, "Red") broadcastToColor("Scripting zone couldn't be found.", playerColor, "Red")
return return
@ -530,8 +536,8 @@ function isInPlayArea(object)
local bounds = self.getBounds() local bounds = self.getBounds()
local position = object.getPosition() local position = object.getPosition()
-- Corners are arbitrary since it's all global - c1 goes down both axes, c2 goes up -- 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 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 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 return position.x > c1.x and position.x < c2.x and position.z > c1.z and position.z < c2.z
end end
@ -582,7 +588,7 @@ function countVP()
local cardVP = tonumber(metadata.victory) or 0 local cardVP = tonumber(metadata.victory) or 0
if cardVP ~= 0 and not cardHasClues(cardId) then if cardVP ~= 0 and not cardHasClues(cardId) then
totalVP = totalVP + cardVP totalVP = totalVP + cardVP
if cardVP >0 then if cardVP > 0 then
table.insert(countedVP, getObjectFromGUID(cardId)) table.insert(countedVP, getObjectFromGUID(cardId))
end end
end end
@ -651,8 +657,8 @@ end
-- rebuilds local snap points (could be useful in the future again) -- rebuilds local snap points (could be useful in the future again)
function buildSnaps() function buildSnaps()
local upperleft = { x = 1.53, z = -1.09} local upperleft = { x = 1.53, z = -1.09 }
local lowerright = {x = -1.53, z = 1.55} local lowerright = { x = -1.53, z = 1.55 }
local snaps = {} local snaps = {}
-- creates 81 snap points, for uneven rows + columns it makes a rotation snap point -- 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 -- enable rotation snaps for uneven rows / columns
if (i % 2 ~= 0) and (j % 2 ~= 0) then if (i % 2 ~= 0) and (j % 2 ~= 0) then
snap.rotation = {0, 0, 0} snap.rotation = { 0, 0, 0 }
snap.rotation_snap = true snap.rotation_snap = true
end end
@ -678,6 +684,6 @@ end
-- utility function -- utility function
function round(num, numDecimalPlaces) function round(num, numDecimalPlaces)
local mult = 10^(numDecimalPlaces or 0) local mult = 10 ^ (numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult return math.floor(num * mult + 0.5) / mult
end end

View File

@ -1,105 +1,109 @@
do do
local PlayAreaApi = { } local PlayAreaApi = {}
local PLAY_AREA_GUID = "721ba2" local guidReferenceApi = require("core/GUIDReferenceApi")
local INVESTIGATOR_COUNTER_GUID = "f182ee"
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 -- Returns the current value of the investigator counter from the playmat
---@return Integer. Number of investigators currently set on the counter ---@return Integer. Number of investigators currently set on the counter
PlayAreaApi.getInvestigatorCount = function() PlayAreaApi.getInvestigatorCount = function()
return getObjectFromGUID(INVESTIGATOR_COUNTER_GUID).getVar("val") return getInvestigatorCounter().getVar("val")
end end
-- Updates the current value of the investigator counter from the playmat -- Updates the current value of the investigator counter from the playmat
---@param count Number of investigators to set on the counter ---@param count Number of investigators to set on the counter
PlayAreaApi.setInvestigatorCount = function(count) PlayAreaApi.setInvestigatorCount = function(count)
return getObjectFromGUID(INVESTIGATOR_COUNTER_GUID).call("updateVal", count) getInvestigatorCounter().call("updateVal", count)
end end
-- Move all contents on the play area (cards, tokens, etc) one slot in the given direction. Certain -- 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 -- fixed objects will be ignored, as will anything the player has tagged with 'displacement_excluded'
-- 'displacement_excluded' ---@param playerColor Color Color of the player requesting the shift for messages
---@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
PlayAreaApi.shiftContentsUp = function(playerColor) PlayAreaApi.shiftContentsUp = function(playerColor)
return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsUp", playerColor) return getPlayArea().call("shiftContentsUp", playerColor)
end end
PlayAreaApi.shiftContentsDown = function(playerColor) PlayAreaApi.shiftContentsDown = function(playerColor)
return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsDown", playerColor) return getPlayArea().call("shiftContentsDown", playerColor)
end end
PlayAreaApi.shiftContentsLeft = function(playerColor) PlayAreaApi.shiftContentsLeft = function(playerColor)
return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsLeft", playerColor) return getPlayArea().call("shiftContentsLeft", playerColor)
end end
PlayAreaApi.shiftContentsRight = function(playerColor) PlayAreaApi.shiftContentsRight = function(playerColor)
return getObjectFromGUID(PLAY_AREA_GUID).call("shiftContentsRight", playerColor) return getPlayArea().call("shiftContentsRight", playerColor)
end end
-- Reset the play area's tracking of which cards have had tokens spawned. -- Reset the play area's tracking of which cards have had tokens spawned.
PlayAreaApi.resetSpawnedCards = function() PlayAreaApi.resetSpawnedCards = function()
return getObjectFromGUID(PLAY_AREA_GUID).call("resetSpawnedCards") return getPlayArea().call("resetSpawnedCards")
end end
-- Event to be called when the current scenario has changed. -- Event to be called when the current scenario has changed.
---@param scenarioName Name of the new scenario ---@param scenarioName Name of the new scenario
PlayAreaApi.onScenarioChanged = function(scenarioName) PlayAreaApi.onScenarioChanged = function(scenarioName)
getObjectFromGUID(PLAY_AREA_GUID).call("onScenarioChanged", scenarioName) getPlayArea().call("onScenarioChanged", scenarioName)
end end
-- Sets this playmat's snap points to limit snapping to locations or not. -- 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. -- 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. ---@param matchTypes Boolean Whether snap points should only snap for the matching card types.
PlayAreaApi.setLimitSnapsByType = function(matchCardTypes) PlayAreaApi.setLimitSnapsByType = function(matchCardTypes)
getObjectFromGUID(PLAY_AREA_GUID).call("setLimitSnapsByType", matchCardTypes) getPlayArea().call("setLimitSnapsByType", matchCardTypes)
end end
-- Receiver for the Global tryObjectEnterContainer event. Used to clear vector lines from dragged -- Receiver for the Global tryObjectEnterContainer event. Used to clear vector lines from dragged
-- cards before they're destroyed by entering the container -- cards before they're destroyed by entering the container
PlayAreaApi.tryObjectEnterContainer = function(container, object) PlayAreaApi.tryObjectEnterContainer = function(container, object)
getObjectFromGUID(PLAY_AREA_GUID).call("tryObjectEnterContainer", getPlayArea().call("tryObjectEnterContainer", { container = container, object = object })
{ container = container, object = object })
end end
-- counts the VP on locations in the play area -- counts the VP on locations in the play area
PlayAreaApi.countVP = function() PlayAreaApi.countVP = function()
return getObjectFromGUID(PLAY_AREA_GUID).call("countVP") return getPlayArea().call("countVP")
end end
-- highlights all locations in the play area without metadata -- highlights all locations in the play area without metadata
---@param state Boolean True if highlighting should be enabled ---@param state Boolean True if highlighting should be enabled
PlayAreaApi.highlightMissingData = function(state) PlayAreaApi.highlightMissingData = function(state)
return getObjectFromGUID(PLAY_AREA_GUID).call("highlightMissingData", state) return getPlayArea().call("highlightMissingData", state)
end end
-- highlights all locations in the play area with VP -- highlights all locations in the play area with VP
---@param state Boolean True if highlighting should be enabled ---@param state Boolean True if highlighting should be enabled
PlayAreaApi.highlightCountedVP = function(state) PlayAreaApi.highlightCountedVP = function(state)
return getObjectFromGUID(PLAY_AREA_GUID).call("highlightCountedVP", state) return getPlayArea().call("highlightCountedVP", state)
end end
-- Checks if an object is in the play area (returns true or false) -- Checks if an object is in the play area (returns true or false)
PlayAreaApi.isInPlayArea = function(object) PlayAreaApi.isInPlayArea = function(object)
return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object) return getPlayArea().call("isInPlayArea", object)
end end
PlayAreaApi.getSurface = function() PlayAreaApi.getSurface = function()
return getObjectFromGUID(PLAY_AREA_GUID).getCustomObject().image return getPlayArea().getCustomObject().image
end end
PlayAreaApi.updateSurface = function(url) PlayAreaApi.updateSurface = function(url)
return getObjectFromGUID(PLAY_AREA_GUID).call("updateSurface", url) return getPlayArea().call("updateSurface", url)
end end
-- Called by Custom Data Helpers to push their location data into the Data Helper. This adds the -- Called by Custom Data Helpers to push their location data into the Data Helper. This adds the
-- data to the local token manager instance. -- data to the local token manager instance.
---@param args Table Single-value array holding the GUID of the Custom Data Helper making the call ---@param args Table Single-value array holding the GUID of the Custom Data Helper making the call
PlayAreaApi.updateLocations = function(args) PlayAreaApi.updateLocations = function(args)
getObjectFromGUID(PLAY_AREA_GUID).call("updateLocations", args) getPlayArea().call("updateLocations", args)
end end
PlayAreaApi.getCustomDataHelper = function() PlayAreaApi.getCustomDataHelper = function()
return getObjectFromGUID(PLAY_AREA_GUID).getVar("customDataHelper") return getPlayArea().getVar("customDataHelper")
end end
return PlayAreaApi return PlayAreaApi

View File

@ -1,5 +1,6 @@
do do
local SoundCubeApi = {} local SoundCubeApi = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
-- this table links the name of a trigger effect to its index -- this table links the name of a trigger effect to its index
local soundIndices = { local soundIndices = {
@ -9,7 +10,8 @@ do
} }
local function playTriggerEffect(index) local function playTriggerEffect(index)
getObjectsWithTag("SoundCube")[1].AssetBundle.playTriggerEffect(index) local SoundCube = guidReferenceApi.getObjectByOwnerAndType("Mythos", "SoundCube")
SoundCube.AssetBundle.playTriggerEffect(index)
end end
-- plays the by name requested sound -- plays the by name requested sound

View File

@ -1,4 +1,5 @@
local chaosBagApi = require("chaosbag/ChaosBagApi") local chaosBagApi = require("chaosbag/ChaosBagApi")
local guidReferenceApi = require("core/GUIDReferenceApi")
local playAreaApi = require("core/PlayAreaApi") local playAreaApi = require("core/PlayAreaApi")
local tokenChecker = require("core/token/TokenChecker") local tokenChecker = require("core/token/TokenChecker")
@ -10,13 +11,8 @@ local countedVP = {}
local highlightMissing = false local highlightMissing = false
local highlightCounted = false local highlightCounted = false
local TRASHCAN
local TRASHCAN_GUID = "70b9f6"
-- button creation when loading the game -- button creation when loading the game
function onLoad() function onLoad()
TRASHCAN = getObjectFromGUID(TRASHCAN_GUID)
-- index 0: VP - "Display" -- index 0: VP - "Display"
local buttonParameters = {} local buttonParameters = {}
buttonParameters.label = "0" buttonParameters.label = "0"
@ -236,8 +232,7 @@ end
function highlightCountedVP() function highlightCountedVP()
self.editButton({ self.editButton({
index = 4, index = 4,
tooltip = (highlightCounted and "Enable" or "Disable") .. tooltip = (highlightCounted and "Enable" or "Disable") .. " highlighting of cards with VP."
" highlighting of cards with VP."
}) })
for _, obj in pairs(countedVP) do for _, obj in pairs(countedVP) do
if obj ~= nil then if obj ~= nil then
@ -254,6 +249,8 @@ end
-- places the provided card in the first empty spot -- places the provided card in the first empty spot
function placeCard(card) function placeCard(card)
local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash")
-- check snap point states -- check snap point states
local snaps = self.getSnapPoints() local snaps = self.getSnapPoints()
table.sort(snaps, function(a, b) return a.position.x > b.position.x end) 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() local chaosBag = chaosBagApi.findChaosBag()
chaosBag.putObject(obj) chaosBag.putObject(obj)
elseif obj.memo ~= nil and obj.getLock() == false then elseif obj.memo ~= nil and obj.getLock() == false then
TRASHCAN.putObject(obj) trash.putObject(obj)
end end
end end
@ -327,13 +324,3 @@ function checkSnapPointState(pos)
origin = pos origin = pos
}) })
end 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

View File

@ -1,18 +1,22 @@
do do
local VictoryDisplayApi = {} 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 -- triggers an update of the Victory count
---@param delay Number Delay in seconds after which the update call is executed ---@param delay Number Delay in seconds after which the update call is executed
VictoryDisplayApi.update = function(delay) VictoryDisplayApi.update = function(delay)
getObjectFromGUID(VD_GUID).call("startUpdate", delay) getVictoryDisplay().call("startUpdate", delay)
end end
-- moves a card to the victory display (in the first empty spot) -- moves a card to the victory display (in the first empty spot)
---@param object Object Object that should be checked and potentially moved ---@param object Object Object that should be checked and potentially moved
VictoryDisplayApi.placeCard = function(object) VictoryDisplayApi.placeCard = function(object)
if object ~= nil and object.tag == "Card" then if object ~= nil and object.tag == "Card" then
getObjectFromGUID(VD_GUID).call("placeCard", object) getVictoryDisplay().call("placeCard", object)
end end
end end

View File

@ -1,4 +1,5 @@
do do
local guidReferenceApi = require("core/GUIDReferenceApi")
local optionPanelApi = require("core/OptionPanelApi") local optionPanelApi = require("core/OptionPanelApi")
local playAreaApi = require("core/PlayAreaApi") local playAreaApi = require("core/PlayAreaApi")
local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi")
@ -119,15 +120,10 @@ do
["supply"] = 7 ["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 -- 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) -- should match the token type keys ("resource", "clue", etc)
local tokenTemplates local tokenTemplates
local DATA_HELPER_GUID = "708279"
local playerCardData local playerCardData
local locationData local locationData
@ -340,8 +336,8 @@ do
if tokenTemplates ~= nil then if tokenTemplates ~= nil then
return return
end end
tokenTemplates = { } tokenTemplates = {}
local tokenSource = getObjectFromGUID(TOKEN_SOURCE_GUID) local tokenSource = guidReferenceApi.getObjectByOwnerAndType("Mythos", "TokenSource")
for _, tokenTemplate in ipairs(tokenSource.getData().ContainedObjects) do for _, tokenTemplate in ipairs(tokenSource.getData().ContainedObjects) do
local tokenName = tokenTemplate.Memo local tokenName = tokenTemplate.Memo
tokenTemplates[tokenName] = tokenTemplate tokenTemplates[tokenName] = tokenTemplate
@ -353,7 +349,7 @@ do
if playerCardData ~= nil then if playerCardData ~= nil then
return return
end end
local dataHelper = getObjectFromGUID(DATA_HELPER_GUID) local dataHelper = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DataHelper")
playerCardData = dataHelper.getTable('PLAYER_CARD_DATA') playerCardData = dataHelper.getTable('PLAYER_CARD_DATA')
locationData = dataHelper.getTable('LOCATIONS_DATA') locationData = dataHelper.getTable('LOCATIONS_DATA')
end end
@ -368,18 +364,16 @@ do
if uses == nil then return end if uses == nil then return end
-- go through tokens to spawn -- go through tokens to spawn
local type, token, tokenCount local tokenCount
for i, useInfo in ipairs(uses) do for i, useInfo in ipairs(uses) do
type = useInfo.type tokenCount = (useInfo.count or 0) + (useInfo.countPerInvestigator or 0) * playAreaApi.getInvestigatorCount()
token = useInfo.token if extraUses ~= nil and extraUses[useInfo.type] ~= nil then
tokenCount = (useInfo.count or 0) tokenCount = tokenCount + extraUses[useInfo.type]
+ (useInfo.countPerInvestigator or 0) * playAreaApi.getInvestigatorCount()
if extraUses ~= nil and extraUses[type] ~= nil then
tokenCount = tokenCount + extraUses[type]
end end
-- Shift each spawned group after the first down so they don't pile on each other -- 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 end
tokenSpawnTrackerApi.markTokensSpawned(card.getGUID()) tokenSpawnTrackerApi.markTokensSpawned(card.getGUID())
end end
@ -403,9 +397,8 @@ do
---@param playerData Table Player card data structure retrieved from the DataHelper. Should be ---@param playerData Table Player card data structure retrieved from the DataHelper. Should be
-- the right data for this card. -- the right data for this card.
internal.spawnPlayerCardTokensFromDataHelper = function(card, playerData) internal.spawnPlayerCardTokensFromDataHelper = function(card, playerData)
token = playerData.tokenType local token = playerData.tokenType
tokenCount = playerData.tokenCount local tokenCount = playerData.tokenCount
--log("Spawning data helper tokens for "..card.getName()..'['..card.getDescription()..']: '..tokenCount.."x "..token)
TokenManager.spawnTokenGroup(card, token, tokenCount) TokenManager.spawnTokenGroup(card, token, tokenCount)
tokenSpawnTrackerApi.markTokensSpawned(card.getGUID()) tokenSpawnTrackerApi.markTokensSpawned(card.getGUID())
end end
@ -438,7 +431,6 @@ do
return 0 return 0
end end
--log(card.getName() .. ' : ' .. locationData.type .. ' : ' .. locationData.value .. ' : ' .. locationData.clueSide)
if ((card.is_face_down and locationData.clueSide == 'back') if ((card.is_face_down and locationData.clueSide == 'back')
or (not card.is_face_down and locationData.clueSide == 'front')) then or (not card.is_face_down and locationData.clueSide == 'front')) then
if locationData.type == 'fixed' then if locationData.type == 'fixed' then

View File

@ -1,26 +1,15 @@
local spawnedCardGuids = { } local spawnedCardGuids = {}
local HAND_ZONES = { } function onSave() return JSON.encode({ cards = spawnedCardGuids }) end
HAND_ZONES["a70eee"] = true -- White
HAND_ZONES["0285cc"] = true -- Green
HAND_ZONES["5fe087"] = true -- Orange
HAND_ZONES["be2f17"] = true -- Red
function onLoad(saveState) function onLoad(saveState)
if saveState ~= nil then if saveState ~= nil then
local saveTable = JSON.decode(saveState) or { } local saveTable = JSON.decode(saveState) or {}
spawnedCardGuids = saveTable.cards or { } spawnedCardGuids = saveTable.cards or {}
end end
createResetMenuItems() createResetMenuItems()
end end
function onSave()
return JSON.encode({
cards = spawnedCardGuids
})
end
function createResetMenuItems() function createResetMenuItems()
self.addContextMenuItem("Reset All", resetAll) self.addContextMenuItem("Reset All", resetAll)
self.addContextMenuItem("Reset Locations", resetAllLocations) self.addContextMenuItem("Reset Locations", resetAllLocations)
@ -39,14 +28,20 @@ function resetTokensSpawned(cardGuid)
spawnedCardGuids[cardGuid] = nil spawnedCardGuids[cardGuid] = nil
end end
function resetAllAssetAndEvents() function resetAll() spawnedCardGuids = {} end
local resetList = { }
function resetAllLocations() resetSpecificTypes("Location") end
function resetAllAssetAndEvents() resetSpecificTypes("Asset", "Event") end
function resetSpecificTypes(type1, type2)
local resetList = {}
for cardGuid, _ in pairs(spawnedCardGuids) do for cardGuid, _ in pairs(spawnedCardGuids) do
local card = getObjectFromGUID(cardGuid) local card = getObjectFromGUID(cardGuid)
if card ~= nil then 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 -- 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 resetList[cardGuid] = true
end end
end end
@ -56,30 +51,9 @@ function resetAllAssetAndEvents()
end end
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. -- Listener to reset card token spawns when they enter a hand.
function onObjectEnterZone(zone, enterObject) function onObjectEnterZone(zone, enterObject)
if HAND_ZONES[zone.getGUID()] then if zone.type == "Hand" and enterObject.type == "Card" then
resetTokensSpawned(enterObject.getGUID()) resetTokensSpawned(enterObject.getGUID())
end end
end end

View File

@ -1,29 +1,33 @@
do do
local TokenSpawnTracker = { } local TokenSpawnTracker = {}
local SPAWN_TRACKER_GUID = "e3ffc9" local guidReferenceApi = require("core/GUIDReferenceApi")
local function getSpawnTracker()
return guidReferenceApi.getObjectByOwnerAndType("Mythos", "TokenSpawnTracker")
end
TokenSpawnTracker.hasSpawnedTokens = function(cardGuid) TokenSpawnTracker.hasSpawnedTokens = function(cardGuid)
return getObjectFromGUID(SPAWN_TRACKER_GUID).call("hasSpawnedTokens", cardGuid) return getSpawnTracker().call("hasSpawnedTokens", cardGuid)
end end
TokenSpawnTracker.markTokensSpawned = function(cardGuid) TokenSpawnTracker.markTokensSpawned = function(cardGuid)
return getObjectFromGUID(SPAWN_TRACKER_GUID).call("markTokensSpawned", cardGuid) return getSpawnTracker().call("markTokensSpawned", cardGuid)
end end
TokenSpawnTracker.resetTokensSpawned = function(cardGuid) TokenSpawnTracker.resetTokensSpawned = function(cardGuid)
return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetTokensSpawned", cardGuid) return getSpawnTracker().call("resetTokensSpawned", cardGuid)
end end
TokenSpawnTracker.resetAllAssetAndEvents = function() TokenSpawnTracker.resetAllAssetAndEvents = function()
return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetAllAssetAndEvents") return getSpawnTracker().call("resetAllAssetAndEvents")
end end
TokenSpawnTracker.resetAllLocations = function() TokenSpawnTracker.resetAllLocations = function()
return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetAllLocations") return getSpawnTracker().call("resetAllLocations")
end end
TokenSpawnTracker.resetAll = function() TokenSpawnTracker.resetAll = function()
return getObjectFromGUID(SPAWN_TRACKER_GUID).call("resetAll") return getSpawnTracker().call("resetAll")
end end
return TokenSpawnTracker return TokenSpawnTracker

View File

@ -1,8 +1,9 @@
do do
require("core/tour/TourScript") require("core/tour/TourScript")
require("core/tour/TourCard") require("core/tour/TourCard")
local TourManager = { } local TourManager = {}
local internal = { } local internal = {}
local guidReferenceApi = require("core/GUIDReferenceApi")
-- Base IDs for various tour card UI elements. Actual IDs will have _[playerColor] appended -- Base IDs for various tour card UI elements. Actual IDs will have _[playerColor] appended
local CARD_ID = "tourCard" local CARD_ID = "tourCard"
@ -123,8 +124,9 @@ do
delay = delay + 0.5 delay = delay + 0.5
end end
local lookPos local lookPos
if TOUR_SCRIPT[cardIndex].showObj ~= nil then local objReferenceData = TOUR_SCRIPT[cardIndex].objReferenceData
local lookAtObj = getObjectFromGUID(TOUR_SCRIPT[cardIndex].showObj) if objReferenceData ~= nil then
local lookAtObj = guidReferenceApi.getObjectByOwnerAndType(objReferenceData.owner, objReferenceData.type)
lookPos = lookAtObj.getPosition() lookPos = lookAtObj.getPosition()
lookPos.y = TOUR_SCRIPT[cardIndex].distanceFromObj or 0 lookPos.y = TOUR_SCRIPT[cardIndex].distanceFromObj or 0
-- Since camera isn't directly above the hook, changing the Y affects the visual position of -- Since camera isn't directly above the hook, changing the Y affects the visual position of

View File

@ -15,7 +15,7 @@ TOUR_SCRIPT = {
{ {
narrator = "Daisy", 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.", 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, distanceFromObj = 20,
position = "west", position = "west",
speakerSide = "right" speakerSide = "right"
@ -23,7 +23,7 @@ TOUR_SCRIPT = {
{ {
narrator = "Mandy", 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.", 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, distanceFromObj = -5,
position = "northwest", position = "northwest",
skipCentering = true, skipCentering = true,
@ -31,7 +31,7 @@ TOUR_SCRIPT = {
{ {
narrator = "Daniela", 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.", 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, distanceFromObj = -7,
position = "south", position = "south",
speakerSide = "right" speakerSide = "right"
@ -39,7 +39,7 @@ TOUR_SCRIPT = {
{ {
narrator = "Finn", 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.", 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, distanceFromObj = 20,
position = "northwest", position = "northwest",
}, },
@ -77,7 +77,7 @@ TOUR_SCRIPT = {
{ {
narrator = "Preston", 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.", 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", position = "north",
skipCentering = true, skipCentering = true,
speakerSide = "right" speakerSide = "right"

View File

@ -1,29 +1,33 @@
do do
local AllCardsBagApi = {} 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 -- Returns a specific card from the bag, based on ArkhamDB ID
-- @param table: ---@param id table String ID of the card to retrieve
-- id: String ID of the card to retrieve ---@return table table
-- @return: If the indexes are still being constructed, an empty table is -- If the indexes are still being constructed, an empty table is
-- returned. Otherwise, a single table with the following fields -- returned. Otherwise, a single table with the following fields
-- cardData: TTS object data, suitable for spawning the card -- cardData: TTS object data, suitable for spawning the card
-- cardMetadata: Table of parsed metadata -- cardMetadata: Table of parsed metadata
AllCardsBagApi.getCardById = function(id) AllCardsBagApi.getCardById = function(id)
return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getCardById", {id = id}) return getAllCardsBag().call("getCardById", {id = id})
end end
-- Gets a random basic weakness from the bag. Once a given ID has been returned -- Gets a random basic weakness from the bag. Once a given ID has been returned
-- it will be removed from the list and cannot be selected again until a reload -- 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 -- occurs or the indexes are rebuilt, which will refresh the list to include all
-- weaknesses. -- weaknesses.
-- @return: String ID of the selected weakness. ---@return id String ID of the selected weakness.
AllCardsBagApi.getRandomWeaknessId = function() AllCardsBagApi.getRandomWeaknessId = function()
return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getRandomWeaknessId") return getAllCardsBag().call("getRandomWeaknessId")
end end
AllCardsBagApi.isIndexReady = function() AllCardsBagApi.isIndexReady = function()
return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("isIndexReady") return getAllCardsBag().call("isIndexReady")
end end
-- Called by Hotfix bags when they load. If we are still loading indexes, then -- Called by Hotfix bags when they load. If we are still loading indexes, then
@ -32,40 +36,38 @@ do
-- called once indexing is complete it means the hotfix bag has been added -- called once indexing is complete it means the hotfix bag has been added
-- later, and we should rebuild the index to integrate the hotfix bag. -- later, and we should rebuild the index to integrate the hotfix bag.
AllCardsBagApi.rebuildIndexForHotfix = function() AllCardsBagApi.rebuildIndexForHotfix = function()
return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("rebuildIndexForHotfix") return getAllCardsBag().call("rebuildIndexForHotfix")
end end
-- Searches the bag for cards which match the given name and returns a list. Note that this is -- Searches the bag for cards which match the given name and returns a list. Note that this is
-- an O(n) search without index support. It may be slow. -- an O(n) search without index support. It may be slow.
-- @param ---@param name String or string fragment to search for names
-- name String or string fragment to search for names ---@param exact Boolean Whether the name match should be exact
-- exact Whether the name match should be exact
AllCardsBagApi.getCardsByName = function(name, 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 end
AllCardsBagApi.isBagPresent = function() AllCardsBagApi.isBagPresent = function()
return getObjectFromGUID(ALL_CARDS_BAG_GUID) and true return getAllCardsBag() and true
end end
-- Returns a list of cards from the bag matching a class and level (0 or upgraded) -- Returns a list of cards from the bag matching a class and level (0 or upgraded)
-- @param ---@param class String class to retrieve ("Guardian", "Seeker", etc)
-- class: String class to retrieve ("Guardian", "Seeker", etc) ---@param upgraded Boolean true for upgraded cards (Level 1-5), false for Level 0
-- upgraded: true for upgraded cards (Level 1-5), false for Level 0 ---@return: If the indexes are still being constructed, returns an empty table.
-- @return: If the indexes are still being constructed, returns an empty table.
-- Otherwise, a list of tables, each with the following fields -- Otherwise, a list of tables, each with the following fields
-- cardData: TTS object data, suitable for spawning the card -- cardData: TTS object data, suitable for spawning the card
-- cardMetadata: Table of parsed metadata -- cardMetadata: Table of parsed metadata
AllCardsBagApi.getCardsByClassAndLevel = function(class, upgraded) 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 end
AllCardsBagApi.getCardsByCycle = function(cycle) AllCardsBagApi.getCardsByCycle = function(cycle)
return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getCardsByCycle", cycle) return getAllCardsBag().call("getCardsByCycle", cycle)
end end
AllCardsBagApi.getUniqueWeaknesses = function() AllCardsBagApi.getUniqueWeaknesses = function()
return getObjectFromGUID(ALL_CARDS_BAG_GUID).call("getUniqueWeaknesses") return getAllCardsBag().call("getUniqueWeaknesses")
end end
return AllCardsBagApi return AllCardsBagApi

View File

@ -16,8 +16,8 @@ function searchSelf()
for _, obj in ipairs(searchArea(self.getPosition(), { 2.5, 0.5, 3.5 })) do for _, obj in ipairs(searchArea(self.getPosition(), { 2.5, 0.5, 3.5 })) do
local obj = obj.hit_object local obj = obj.hit_object
if obj.getCustomObject().image == local image = obj.getCustomObject().image
"http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then if image == "http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then
foundTokens = foundTokens + math.abs(obj.getQuantity()) foundTokens = foundTokens + math.abs(obj.getQuantity())
obj.destruct() obj.destruct()
elseif obj.getMemo() == "resourceCounter" then elseif obj.getMemo() == "resourceCounter" then
@ -47,7 +47,7 @@ end
function takeAll(playerColor) function takeAll(playerColor)
searchSelf() searchSelf()
local matColor = playmatApi.getMatColorByPosition(self.getPosition()) local matColor = playmatApi.getMatColorByPosition(self.getPosition())
playmatApi.gainResources(foundTokens, matColor) playmatApi.updateCounter(matColor, "ResourceCounter", foundTokens)
if clickableResourceCounter then if clickableResourceCounter then
clickableResourceCounter.call("updateVal", 0) clickableResourceCounter.call("updateVal", 0)

View File

@ -1,4 +1,4 @@
local playmatAPI = require("playermat/PlaymatApi") local playmatApi = require("playermat/PlaymatApi")
function onLoad() function onLoad()
self.addContextMenuItem("Discard 10 cards", shortSupply) self.addContextMenuItem("Discard 10 cards", shortSupply)
@ -6,11 +6,11 @@ end
-- called by context menu entry -- called by context menu entry
function shortSupply(color) function shortSupply(color)
local matColor = playmatAPI.getMatColorByPosition(self.getPosition()) local matColor = playmatApi.getMatColorByPosition(self.getPosition())
-- get draw deck and discard position -- get draw deck and discard position
local drawDeck = playmatAPI.getDrawDeck(matColor) local drawDeck = playmatApi.getDrawDeck(matColor)
local discardPos = playmatAPI.getDiscardPosition(matColor) local discardPos = playmatApi.getDiscardPosition(matColor)
-- error handling -- error handling
if discardPos == nil then if discardPos == nil then
@ -21,7 +21,7 @@ function shortSupply(color)
if drawDeck == nil then if drawDeck == nil then
broadcastToColor("Deck not found!", color, "Yellow") broadcastToColor("Deck not found!", color, "Yellow")
return return
elseif drawDeck.tag ~= "Deck" then elseif drawDeck.type ~= "Deck" then
broadcastToColor("Deck only contains a single card!", color, "Yellow") broadcastToColor("Deck only contains a single card!", color, "Yellow")
return return
end end

View File

@ -1,72 +1,53 @@
-- this script is shared between both the level 0 and the upgraded level 3 version of the card -- this script is shared between both the level 0 and the upgraded level 3 version of the card
local playmatApi = require("playermat/PlaymatApi") local playmatApi = require("playermat/PlaymatApi")
local display = false local modValue, loopId
local count = 0 local buttonParameters = {
local modValue = 5 -- level 0 Well Connected click_function = "toggleCounter",
local loopId = nil tooltip = "disable counter",
function_owner = self,
local b_display = { position = { 0.88, 0.5, -1.33 },
click_function = "toggleCounter", font_size = 150,
function_owner = self, width = 175,
position = {0.88,0.5,-1.33}, height = 175
font_size = 150,
width = 175,
height = 175
} }
function onLoad(saved_data) function onSave() return JSON.encode({ loopId = loopId }) end
local notes = JSON.decode(self.getGMNotes())
if notes.id == "54006" then -- hardcoded card id for upgraded Well Connected (3) function onLoad(savedData)
modValue = 4 -- Well Connected (3) -- 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
end
if saved_data != '' then self.addContextMenuItem("Toggle Counter", toggleCounter)
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})
end end
function toggleCounter() function toggleCounter()
display = not display if loopId ~= nil then
Wait.stop(loopId)
if display then loopId = nil
createUpdateDisplay() self.clearButtons()
loopId = Wait.time(|| createUpdateDisplay(), 2, -1) else
else self.createButton(buttonParameters)
if loopId ~= nil then updateDisplay()
Wait.stop(loopId) loopId = Wait.time(updateDisplay, 2, -1)
end end
self.clearButtons()
loopId = nil
end
end end
function createUpdateDisplay() function updateDisplay()
count = math.max(math.floor(getPlayerResources() / modValue), 0) local matColor = playmatApi.getMatColorByPosition(self.getPosition())
local resources = playmatApi.getCounterValue(matColor, "ResourceCounter")
b_display.label = tostring(count) local count = tostring(math.floor(resources / modValue))
self.editButton({ index = 0, label = count })
if loopId == nil then
self.createButton(b_display)
else
self.editButton(b_display)
end
end end
function getPlayerResources()
local matColor = playmatApi.getMatColorByPosition(self.getPosition())
return playmatApi.getResourceCount(matColor)
end

View File

@ -7,13 +7,12 @@ local validCountItemList = {
["Clue"] = 1, ["Clue"] = 1,
[""] = 1 [""] = 1
} }
local trashGUID = "70b9f6"
exposedValue = 0 exposedValue = 0
function onLoad() function onLoad()
self.createButton({ self.createButton({
label = "", label = "",
click_function = "removeAllClues", click_function = "countItems",
function_owner = self, function_owner = self,
position = { 0, 0.1, 0 }, position = { 0, 0.1, 0 },
height = 0, height = 0,
@ -27,16 +26,15 @@ end
-- Activated once per second, counts items in bowls -- Activated once per second, counts items in bowls
function countItems() function countItems()
local totalValue = 0 local totalValue = 0
local countableItems = findValidItemsInSphere() for _, item in ipairs(findValidItemsInSphere()) do
for _, entry in ipairs(countableItems) do local descValue = tonumber(item.getDescription())
local descValue = tonumber(entry.hit_object.getDescription()) local stackMult = math.abs(item.getQuantity())
local stackMult = math.abs(entry.hit_object.getQuantity())
-- Use value in description if available -- Use value in description if available
if descValue ~= nil then if descValue ~= nil then
totalValue = totalValue + descValue * stackMult totalValue = totalValue + descValue * stackMult
else else
-- Otherwise use the value in validCountItemList -- Otherwise use the value in validCountItemList
totalValue = totalValue + validCountItemList[entry.hit_object.getName()] * stackMult totalValue = totalValue + validCountItemList[item.getName()] * stackMult
end end
end end
exposedValue = totalValue exposedValue = totalValue
@ -49,38 +47,22 @@ function findValidItemsInSphere()
direction = { 0, 1, 0 }, direction = { 0, 1, 0 },
type = 2, type = 2,
max_distance = 0, max_distance = 0,
size = { 2, 2, 2 }, size = { 2, 2, 2 }
--debug=true
}) })
retval = {} local validItemList = {}
for _, entry in ipairs(items) do for _, entry in ipairs(items) do
--Ignore the bowl
if entry.hit_object ~= self then if entry.hit_object ~= self then
--Ignore if not in validCountItemList if validCountItemList[entry.hit_object.getName()] ~= nil then
local tableEntry = validCountItemList[entry.hit_object.getName()] table.insert(validItemList, entry.hit_object)
if tableEntry ~= nil then
table.insert(retval, entry)
end end
end end
end end
return retval return validItemList
end end
function removeAllClues() function removeAllClues(trash)
startLuaCoroutine(self, "clueRemovalCoroutine") for _, obj in ipairs(findValidItemsInSphere()) do
end trash.putObject(obj)
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
end end
return 1 end
end

View File

@ -1,4 +1,5 @@
local chaosBagApi = require("chaosbag/ChaosBagApi") local chaosBagApi = require("chaosbag/ChaosBagApi")
local guidReferenceApi = require("core/GUIDReferenceApi")
local mythosAreaApi = require("core/MythosAreaApi") local mythosAreaApi = require("core/MythosAreaApi")
local navigationOverlayApi = require("core/NavigationOverlayApi") local navigationOverlayApi = require("core/NavigationOverlayApi")
local tokenChecker = require("core/token/TokenChecker") local tokenChecker = require("core/token/TokenChecker")
@ -66,7 +67,7 @@ local DECK_DISCARD_AREA = {
}, },
size = { size = {
x = 0.4, x = 0.4,
y = 0.1, y = 1,
z = 1.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 -- global variable so it can be reset by the Clean Up Helper
activeInvestigatorId = "00000" 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 local isDrawButtonVisible = false
-- global variable to report "Dream-Enhancing Serum" status -- global variable to report "Dream-Enhancing Serum" status
@ -98,9 +103,8 @@ end
function onLoad(saveState) function onLoad(saveState)
self.interactable = DEBUG self.interactable = DEBUG
TRASHCAN = getObjectFromGUID(TRASHCAN_GUID) -- get object references to owned objects
STAT_TRACKER = getObjectFromGUID(STAT_TRACKER_GUID) ownedObjects = guidReferenceApi.getObjectsByOwner(matColor)
RESOURCE_COUNTER = getObjectFromGUID(RESOURCE_COUNTER_GUID)
-- button creation -- button creation
for i = 1, 6 do for i = 1, 6 do
@ -145,9 +149,7 @@ function onLoad(saveState)
end end
showDrawButton(isDrawButtonVisible) showDrawButton(isDrawButtonVisible)
collisionEnabled = true collisionEnabled = true
math.randomseed(os.time()) math.randomseed(os.time())
end end
@ -163,7 +165,7 @@ function searchArea(origin, size, filter)
orientation = self.getRotation(), orientation = self.getRotation(),
type = 3, type = 3,
size = size, size = size,
max_distance = 1 max_distance = 0
}) })
local objList = {} local objList = {}
@ -180,11 +182,12 @@ function isCard(x) return x.type == 'Card' end
function isDeck(x) return x.type == 'Deck' end function isDeck(x) return x.type == 'Deck' end
function isCardOrDeck(x) return x.type == 'Card' or 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) function searchAroundSelf(filter)
local bounds = self.getBoundsNormalized() local bounds = self.getBoundsNormalized()
-- Increase the width to cover the set aside zone -- Increase the width to cover the set aside zone
bounds.size.x = bounds.size.x + SEARCH_AROUND_SELF_X_BUFFER 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 -- 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 -- 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 -- 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 return card.getVar("do_not_ready") or false
end 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 -- Discard buttons
--------------------------------------------------------- ---------------------------------------------------------
@ -233,7 +244,7 @@ function makeDiscardHandlerFor(searchPosition)
chaosBag.putObject(obj) chaosBag.putObject(obj)
-- don't touch the table or this playmat itself -- don't touch the table or this playmat itself
elseif obj.guid ~= "4ee1f2" and obj ~= self then elseif obj.guid ~= "4ee1f2" and obj ~= self then
TRASHCAN.putObject(obj) ownedObjects.Trash.putObject(obj)
end end
end end
end end
@ -370,10 +381,10 @@ function doUpkeep(_, clickedByColor, isRightClick)
-- gain a resource (or two if playing Jenny Barnes) -- gain a resource (or two if playing Jenny Barnes)
if string.match(activeInvestigatorId, "%d%d%d%d%d") == "02003" then if string.match(activeInvestigatorId, "%d%d%d%d%d") == "02003" then
gainResources(2) updateCounter({type = "ResourceCounter", modifier = 2})
printToColor("Gaining 2 resources (Jenny)", messageColor) printToColor("Gaining 2 resources (Jenny)", messageColor)
else else
gainResources(1) updateCounter({type = "ResourceCounter", modifier = 1})
end end
-- draw a card (with handling for Patrice and Forced Learning) -- draw a card (with handling for Patrice and Forced Learning)
@ -399,18 +410,6 @@ function doUpkeep(_, clickedByColor, isRightClick)
end end
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 for "draw 1 button" (that can be added via option panel)
function doDrawOne(_, color) function doDrawOne(_, color)
-- send messages to player who clicked button if no seated player found -- send messages to player who clicked button if no seated player found
@ -425,10 +424,13 @@ function drawCardsWithReshuffle(numCards)
-- Norman Withers handling -- Norman Withers handling
if string.match(activeInvestigatorId, "%d%d%d%d%d") == "08004" then if string.match(activeInvestigatorId, "%d%d%d%d%d") == "08004" then
local harbinger = false 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 elseif drawDeck ~= nil and not drawDeck.is_face_down then
local cards = drawDeck.getObjects() 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 end
if harbinger then if harbinger then
@ -554,32 +556,8 @@ function changeColor(clickedByColor)
-- show the option dialog for color selection to the player that triggered this -- show the option dialog for color selection to the player that triggered this
Player[clickedByColor].showOptionsDialog("Select a new color:", colorList, _, function(color) 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 -- update the color of the hand zone
local handZone = getObjectFromGUID(HAND_ZONE_GUIDS[index]) local handZone = ownedObjects.HandZone
handZone.setValue(color) handZone.setValue(color)
-- if the seated player clicked this, reseat him to the new color -- if the seated player clicked this, reseat him to the new color
@ -643,17 +621,17 @@ function spawnTokensFor(object)
tokenManager.spawnForCard(object, extraUses) tokenManager.spawnForCard(object, extraUses)
end end
function onCollisionEnter(collision_info) function onCollisionEnter(collisionInfo)
local object = collision_info.collision_object local object = collisionInfo.collision_object
-- detect if "Dream-Enhancing Serum" is placed
if object.getName() == "Dream-Enhancing Serum" then isDES = true end
-- only continue if loading is completed -- only continue if loading is completed
if not collisionEnabled then return end if not collisionEnabled then return end
-- only continue for cards -- 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) maybeUpdateActiveInvestigator(object)
syncCustomizableMetadata(object) syncCustomizableMetadata(object)
@ -668,8 +646,8 @@ function onCollisionEnter(collision_info)
end end
-- detect if "Dream-Enhancing Serum" is removed -- detect if "Dream-Enhancing Serum" is removed
function onCollisionExit(collision_info) function onCollisionExit(collisionInfo)
if collision_info.collision_object.getName() == "Dream-Enhancing Serum" then isDES = false end if collisionInfo.collision_object.getName() == "Dream-Enhancing Serum" then isDES = false end
end end
-- checks if tokens should be spawned for the provided card -- checks if tokens should be spawned for the provided card
@ -705,14 +683,12 @@ function shouldSpawnTokens(card)
end end
function onObjectEnterContainer(container, object) function onObjectEnterContainer(container, object)
Wait.frames(function() resetTokensIfInDeckZone(container, object) end, 1) if not isCard(object) then return end
end
function resetTokensIfInDeckZone(container, object) local localCardPos = self.positionToLocal(object.getPosition())
local pos = self.positionToLocal(container.getPosition()) if inArea(localCardPos, DECK_DISCARD_AREA) then
if inArea(pos, DECK_DISCARD_AREA) then
tokenManager.resetTokensSpawned(object) tokenManager.resetTokensSpawned(object)
removeTokensFromObject(container) removeTokensFromObject(object)
end end
end end
@ -727,7 +703,7 @@ function removeTokensFromObject(object)
obj.getLock() == false and obj.getLock() == false and
obj.getDescription() ~= "Action Token" and obj.getDescription() ~= "Action Token" and
not tokenChecker.isChaosToken(obj) then not tokenChecker.isChaosToken(obj) then
TRASHCAN.putObject(obj) ownedObjects.Trash.putObject(obj)
end end
end end
end end
@ -746,11 +722,16 @@ function maybeUpdateActiveInvestigator(card)
if notes.id == activeInvestigatorId then return end if notes.id == activeInvestigatorId then return end
class = notes.class class = notes.class
activeInvestigatorId = notes.id 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 elseif activeInvestigatorId ~= "00000" then
class = "Neutral" class = "Neutral"
activeInvestigatorId = "00000" activeInvestigatorId = "00000"
STAT_TRACKER.call("updateStats", {1, 1, 1, 1}) ownedObjects.InvestigatorSkillTracker.call("updateStats", {1, 1, 1, 1})
else else
return return
end end
@ -797,6 +778,40 @@ function setObjectState(obj, stateId)
if obj.getStateId() ~= stateId then obj.setState(stateId) end if obj.getStateId() ~= stateId then obj.setState(stateId) end
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 -- 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 -- 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 ---@param showCounter Boolean Whether the clickable clue counter should be present
function clickableClues(showCounter) function clickableClues(showCounter)
local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID) local clickerPos = ownedObjects.ClickableClueCounter.getPosition()
local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID)
local clickerPos = CLUE_CLICKER.getPosition()
local clueCount = 0 local clueCount = 0
if showCounter then if showCounter then
-- current clue count -- current clue count
clueCount = CLUE_COUNTER.getVar("exposedValue") clueCount = ownedObjects.ClueCounter.getVar("exposedValue")
-- remove clues -- remove clues
CLUE_COUNTER.call("removeAllClues") ownedObjects.ClueCounter.call("removeAllClues", ownedObjects.Trash)
-- set value for clue clickers -- set value for clue clickers
CLUE_CLICKER.call("updateVal", clueCount) ownedObjects.ClickableClueCounter.call("updateVal", clueCount)
-- move clue counters up -- move clue counters up
clickerPos.y = 1.52 clickerPos.y = 1.52
CLUE_CLICKER.setPosition(clickerPos) ownedObjects.ClickableClueCounter.setPosition(clickerPos)
else else
-- current clue count -- current clue count
clueCount = CLUE_CLICKER.getVar("val") clueCount = ownedObjects.ClickableClueCounter.getVar("val")
-- move clue counters down -- move clue counters down
clickerPos.y = 1.3 clickerPos.y = 1.3
CLUE_CLICKER.setPosition(clickerPos) ownedObjects.ClickableClueCounter.setPosition(clickerPos)
-- spawn clues -- spawn clues
local pos = self.positionToWorld({x = -1.12, y = 0.05, z = 0.7}) 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) -- removes all clues (moving tokens to the trash and setting counters to 0)
function removeClues() function removeClues()
local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID) ownedObjects.ClueCounter.call("removeAllClues", ownedObjects.Trash)
local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID) ownedObjects.ClickableClueCounter.call("updateVal", 0)
CLUE_COUNTER.call("removeAllClues")
CLUE_CLICKER.call("updateVal", 0)
end end
-- reports the clue count -- reports the clue count
---@param useClickableCounters Boolean Controls which type of counter is getting checked ---@param useClickableCounters Boolean Controls which type of counter is getting checked
function getClueCount(useClickableCounters) function getClueCount(useClickableCounters)
local count = 0
if useClickableCounters then if useClickableCounters then
local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID) return ownedObjects.ClickableClueCounter.getVar("val")
count = tonumber(CLUE_CLICKER.getVar("val"))
else else
local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID) return ownedObjects.ClueCounter.getVar("exposedValue")
count = tonumber(CLUE_COUNTER.getVar("exposedValue"))
end end
return count
end end
-- Sets this playermat's snap points to limit snapping to matching card types or not. If matchTypes -- 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") local playerCardData = customDataHelper.getTable("PLAYER_CARD_DATA")
tokenManager.addPlayerCardData(playerCardData) tokenManager.addPlayerCardData(playerCardData)
end 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

View File

@ -1,225 +1,213 @@
do do
local PlaymatApi = { } local PlaymatApi = {}
local internal = { } local guidReferenceApi = require("core/GUIDReferenceApi")
local MAT_IDS = { -- Convenience function to look up a mat's object by color, or get all mats.
White = "8b081b", ---@param matColor String Color of the playmat - White, Orange, Green, Red or All
Orange = "bd0ff4", ---@return array Table Single-element if only single playmat is requested
Green = "383d8b", local function getMatForColor(matColor)
Red = "0840d5" if matColor == "All" then
} return guidReferenceApi.getObjectsByType("Playermat")
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
else else
if startPos.z > 0 then return { matColor = guidReferenceApi.getObjectByOwnerAndType(matColor, "Playermat") }
return "Green" end
else end
return "Red"
-- 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 end
end end
-- Returns the color of the player's hand that is seated next to the playermat -- Returns if there is the card "Dream-Enhancing Serum" on 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.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
PlaymatApi.isDES = function(matColor) PlaymatApi.isDES = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("isDES") return mat.getVar("isDES")
end
end end
-- Returns the draw deck of the requested playmat -- 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) PlaymatApi.getDrawDeck = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
mat.call("getDrawDiscardDecks") mat.call("getDrawDiscardDecks")
return mat.getVar("drawDeck") return mat.getVar("drawDeck")
end
end end
-- Returns the position of the discard pile of the requested playmat -- 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) PlaymatApi.getDiscardPosition = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("returnGlobalDiscardPosition") return mat.call("returnGlobalDiscardPosition")
end
end end
-- Transforms a local position into a global position -- Transforms a local position into a global position
---@param localPos Table Local position to be transformed ---@param localPos Table Local position to be transformed
---@param matColor String Color of the playermat ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All")
PlaymatApi.transformLocalPosition = function(localPos, matColor) PlaymatApi.transformLocalPosition = function(localPos, matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
return mat.positionToWorld(localPos) return mat.positionToWorld(localPos)
end
end end
-- Returns the rotation of the requested playmat -- 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) PlaymatApi.returnRotation = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
return mat.getRotation() return mat.getRotation()
end
end end
-- Triggers the Upkeep for the requested playmat -- 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) ---@param playerColor String Color of the calling player (for messages)
PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor) PlaymatApi.doUpkeepFromHotkey = function(matColor, playerColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
return mat.call("doUpkeepFromHotkey", playerColor) mat.call("doUpkeepFromHotkey", playerColor)
end
end end
-- Returns the active investigator id -- 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) PlaymatApi.returnInvestigatorId = function(matColor)
local mat = getObjectFromGUID(MAT_IDS[matColor]) for _, mat in pairs(getMatForColor(matColor)) do
return mat.getVar("activeInvestigatorId") return mat.getVar("activeInvestigatorId")
end
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 -- matchTypes is true, the main card slot snap points will only snap assets, while the
-- investigator area point will only snap Investigators. If matchTypes is false, snap points will -- investigator area point will only snap Investigators. If matchTypes is false, snap points will
-- be reset to snap all cards. -- be reset to snap all cards.
---@param matchCardTypes Boolean. Whether snap points should only snap for the matching card ---@param matchCardTypes Boolean Whether snap points should only snap for the matching card types
-- types. ---@param matColor String Color of the playmat - White, Orange, Green, Red or All
---@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.
PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor) PlaymatApi.setLimitSnapsByType = function(matchCardTypes, matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("setLimitSnapsByType", matchCardTypes) mat.call("setLimitSnapsByType", matchCardTypes)
end end
end end
-- Sets the requested playermat's draw 1 button to visible -- Sets the requested playmat's draw 1 button to visible
---@param isDrawButtonVisible Boolean. Whether the draw 1 button should be visible or not ---@param isDrawButtonVisible Boolean Whether the draw 1 button should be visible or not
---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also ---@param matColor String Color of the playmat - White, Orange, Green, Red or All
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.showDrawButton = function(isDrawButtonVisible, matColor) PlaymatApi.showDrawButton = function(isDrawButtonVisible, matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("showDrawButton", isDrawButtonVisible) mat.call("showDrawButton", isDrawButtonVisible)
end end
end end
-- Shows or hides the clickable clue counter for the requested playermat -- Shows or hides the clickable clue counter for the requested playmat
---@param showCounter Boolean. Whether the clickable counter should be present or not ---@param showCounter Boolean Whether the clickable counter should be present or not
---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also ---@param matColor String Color of the playmat - White, Orange, Green, Red or All
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.clickableClues = function(showCounter, matColor) PlaymatApi.clickableClues = function(showCounter, matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("clickableClues", showCounter) mat.call("clickableClues", showCounter)
end end
end end
-- Removes all clues (to the trash for tokens and counters set to 0) for the requested playermat -- Removes all clues (to the trash for tokens and counters set to 0) for the requested playmat
---@param matColor String for one of the active player colors - White, Orange, Green, Red. Also ---@param matColor String Color of the playmat - White, Orange, Green, Red or All
-- accepts "All" as a special value which will apply the setting to all four mats.
PlaymatApi.removeClues = function(matColor) PlaymatApi.removeClues = function(matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("removeClues") mat.call("removeClues")
end end
end end
-- Reports the clue count for the requested playermat -- Reports the clue count for the requested playmat
---@param useClickableCounters Boolean Controls which type of counter is getting checked ---@param useClickableCounters Boolean Controls which type of counter is getting checked
PlaymatApi.getClueCount = function(useClickableCounters, matColor) PlaymatApi.getClueCount = function(useClickableCounters, matColor)
local count = 0 local count = 0
for _, mat in ipairs(internal.getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
count = count + tonumber(mat.call("getClueCount", useClickableCounters)) count = count + mat.call("getClueCount", useClickableCounters)
end end
return count return count
end end
-- Adds the specified amount of resources to the requested playermat's resource counter -- updates the specified owned counter
PlaymatApi.gainResources = function(amount, matColor) ---@param matColor String Color of the playmat - White, Orange, Green, Red or All
for _, mat in ipairs(internal.getMatForColor(matColor)) do ---@param type String Counter to target
mat.call("gainResources", amount) ---@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
end end
-- Returns the resource counter amount for the requested playermat -- returns the resource counter amount
PlaymatApi.getResourceCount = function(matColor) ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All")
local mat = getObjectFromGUID(MAT_IDS[matColor]) ---@param type String Counter to target
return mat.call("getResourceCount") 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 end
-- Discard a non-hidden card from the corresponding player's hand -- Discard a non-hidden card from the corresponding player's hand
---@param matColor String Color of the playmat - White, Orange, Green, Red or All
PlaymatApi.doDiscardOne = function(matColor) PlaymatApi.doDiscardOne = function(matColor)
for _, mat in ipairs(internal.getMatForColor(matColor)) do for _, mat in pairs(getMatForColor(matColor)) do
mat.call("doDiscardOne") mat.call("doDiscardOne")
end end
end end
-- Triggers the metadata sync for all playmats
PlaymatApi.syncAllCustomizableCards = function() PlaymatApi.syncAllCustomizableCards = function()
for _, mat in ipairs(internal.getMatForColor("All")) do for _, mat in pairs(getMatForColor("All")) do
mat.call("syncAllCustomizableCards") mat.call("syncAllCustomizableCards")
end end
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 return PlaymatApi
end end

View File

@ -20,14 +20,9 @@
-- SetAside5: Hunch Deck for Joe Diamond -- SetAside5: Hunch Deck for Joe Diamond
-- SetAside6: currently unused -- SetAside6: currently unused
do do
local playmatApi = require("playermat/PlaymatApi")
local Zones = { } local Zones = { }
local playerMatGuids = {}
playerMatGuids["Red"] = "0840d5"
playerMatGuids["Orange"] = "bd0ff4"
playerMatGuids["White"] = "8b081b"
playerMatGuids["Green"] = "383d8b"
local commonZones = {} local commonZones = {}
commonZones["Investigator"] = { -1.17702, 0, 0.00209 } commonZones["Investigator"] = { -1.17702, 0, 0.00209 }
commonZones["Deck"] = { -1.822724, 0, -0.02940192 } commonZones["Deck"] = { -1.822724, 0, -0.02940192 }
@ -119,7 +114,7 @@ do
and playerColor ~= "Green") then and playerColor ~= "Green") then
return nil return nil
end end
return getObjectFromGUID(playerMatGuids[playerColor]).positionToWorld(zoneData[playerColor][zoneName]) return playmatApi.transformLocalPosition(zoneData[playerColor][zoneName], playerColor)
end end
-- Return the global rotation for a card on the given player mat, based on its metadata. -- 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 -- 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. -- Z rotation to place the card face up or face down.
Zones.getDefaultCardRotation = function(playerColor, zone) Zones.getDefaultCardRotation = function(playerColor, zone)
local deckRotation = getObjectFromGUID(playerMatGuids[playerColor]).getRotation() local cardRotation = playmatApi.returnRotation(playerColor)
if zone == "Deck" then if zone == "Deck" then
deckRotation = deckRotation + Vector(0, 0, 180) cardRotation = cardRotation + Vector(0, 0, 180)
end end
return cardRotation
return deckRotation
end end
return Zones return Zones