Merge pull request #302 from Entrox-Licher/entrox-main

Added a Campaign Importer/Exporter!
This commit is contained in:
Chr1Z 2023-08-10 22:53:49 +02:00 committed by GitHub
commit 1cff0cb5be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 599 additions and 3 deletions

View File

@ -199,6 +199,7 @@
"GameKeyHandler.fce69c", "GameKeyHandler.fce69c",
"3DText.d628cc", "3DText.d628cc",
"NavigationOverlayHandler.797ede", "NavigationOverlayHandler.797ede",
"CampaignImporterExporter.334ee3",
"Bloodborne-CityoftheUnseen.1e7a0b", "Bloodborne-CityoftheUnseen.1e7a0b",
"TheColorOutofOz.806d9a" "TheColorOutofOz.806d9a"
], ],

View File

@ -76,6 +76,10 @@
"displayed": "SoundCube", "displayed": "SoundCube",
"normalized": "soundcube" "normalized": "soundcube"
}, },
{
"displayed": "CampaignBox",
"normalized": "campaignbox"
},
{ {
"displayed": "CameraZoom_ignore", "displayed": "CameraZoom_ignore",
"normalized": "camerazoom_ignore" "normalized": "camerazoom_ignore"

View File

@ -0,0 +1,57 @@
{
"AltLookAngle": {
"x": 0,
"y": 0,
"z": 0
},
"Autoraise": true,
"ColorDiffuse": {
"b": 1,
"g": 1,
"r": 1
},
"CustomImage": {
"CustomTile": {
"Stackable": false,
"Stretch": true,
"Thickness": 0.2,
"Type": 0
},
"ImageScalar": 1,
"ImageSecondaryURL": "",
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/254843371583173230/BECDC34EB4D2C8C5F9F9933C97085F82A2F21AE3/",
"WidthScale": 0
},
"Description": "Saves the state of the table to enable loading the campaign into a new save and keep all current progress.\n\nThis tool will track which campaign you're playing, the entire contents of your campaign log, the contents of your chaos bag, update your health/sanity according to trauma, your ArkhamDB deck IDs, the number of investigators, the page of your campaign guide, and any options you have selected in the options panel.\n\nFor saving trauma values to correct seats, ensure investigators in the campaign log are in the following order: White, Orange, Green, Red\n\n(For custom campaigns, ensure: 1) Campaign Box, Campaign Log, and Campaign Guide each have the corresponding tag, 2)The Campaign Box is on the table when you import or export.",
"DragSelectable": true,
"GMNotes": "",
"GUID": "334ee3",
"Grid": true,
"GridProjection": false,
"Hands": false,
"HideWhenFaceDown": false,
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": true,
"LuaScript": "require(\"accessories/CampaignImporterExporter\")",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Custom_Tile",
"Nickname": "Campaign Importer/Exporter",
"Snap": true,
"Sticky": true,
"Tooltip": true,
"Transform": {
"posX": -11.74,
"posY": 1.481,
"posZ": 55.65,
"rotX": 0,
"rotY": 270,
"rotZ": 0,
"scaleX": 3.38,
"scaleY": 1,
"scaleZ": 3.38
},
"Value": 0,
"XmlUI": ""
}

View File

@ -49,6 +49,9 @@
"Nickname": "Core/Night of the Zealot", "Nickname": "Core/Night of the Zealot",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 65, "posX": 65,

View File

@ -49,6 +49,9 @@
"Nickname": "Edge of the Earth", "Nickname": "Edge of the Earth",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 39, "posX": 39,

View File

@ -49,6 +49,9 @@
"Nickname": "Return to The Circle Undone", "Nickname": "Return to The Circle Undone",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 13, "posX": 13,

View File

@ -49,6 +49,9 @@
"Nickname": "Return to The Dunwich Legacy", "Nickname": "Return to The Dunwich Legacy",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 52, "posX": 52,

View File

@ -49,6 +49,9 @@
"Nickname": "Return to The Forgotten Age", "Nickname": "Return to The Forgotten Age",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 26, "posX": 26,

View File

@ -49,6 +49,9 @@
"Nickname": "Return to The Path to Carcosa", "Nickname": "Return to The Path to Carcosa",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 39, "posX": 39,

View File

@ -49,6 +49,9 @@
"Nickname": "Return to the Night of the Zealot", "Nickname": "Return to the Night of the Zealot",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 65, "posX": 65,

View File

@ -49,6 +49,9 @@
"Nickname": "The Circle Undone", "Nickname": "The Circle Undone",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 13, "posX": 13,

View File

@ -49,6 +49,9 @@
"Nickname": "The Dream-Eaters", "Nickname": "The Dream-Eaters",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 65, "posX": 65,

View File

@ -49,6 +49,9 @@
"Nickname": "The Dunwich Legacy", "Nickname": "The Dunwich Legacy",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 52, "posX": 52,

View File

@ -49,6 +49,9 @@
"Nickname": "The Forgotten Age", "Nickname": "The Forgotten Age",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 26, "posX": 26,

View File

@ -49,6 +49,9 @@
"Nickname": "The Innsmouth Conspiracy", "Nickname": "The Innsmouth Conspiracy",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 52, "posX": 52,

View File

@ -49,6 +49,9 @@
"Nickname": "The Path to Carcosa", "Nickname": "The Path to Carcosa",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 39, "posX": 39,

View File

@ -49,6 +49,9 @@
"Nickname": "The Scarlet Keys", "Nickname": "The Scarlet Keys",
"Snap": true, "Snap": true,
"Sticky": true, "Sticky": true,
"Tags": [
"CampaignBox"
],
"Tooltip": true, "Tooltip": true,
"Transform": { "Transform": {
"posX": 26, "posX": 26,

View File

@ -0,0 +1,298 @@
local campaignTokenData = {
GUID = "51b1c9",
Name = "Custom_Model",
Transform = {
posX = -21.25,
posY = 1.68,
posZ = 55.59,
rotX = 0,
rotY = 270,
rotZ = 0,
scaleX = 2,
scaleY = 2,
scaleZ = 2
},
Nickname = "Arkham Coin",
Description = "SCED Importer Token",
GMNotes = "",
Tags = {
"ImporterToken"
},
CustomMesh = {
MeshURL = "http://cloud-3.steamusercontent.com/ugc/943949966265929204/A38BB5D72419E6298385556D931877C0A1A55C17/",
DiffuseURL = "http://cloud-3.steamusercontent.com/ugc/254843371583188147/920981125E37B5CEB6C400E3FD353A2C428DA969/",
NormalURL = "",
ColliderURL = "http://cloud-3.steamusercontent.com/ugc/943949966265929204/A38BB5D72419E6298385556D931877C0A1A55C17/",
Convex = true,
MaterialIndex = 2,
TypeIndex = 0,
CustomShader = {
SpecularColor = {
r = 0.7222887,
g = 0.507659256,
b = 0.339915335
},
SpecularIntensity = 0.4,
SpecularSharpness = 7.0,
FresnelStrength = 0.0
},
CastShadows = true
}
}
-- counter GUIDS (4x damage and 4x horror)
local DAMAGE_HORROR_GUIDS = {
"eb08d6"; "e64eec"; "1f5a0a"; "591a45";
"468e88"; "0257d9"; "7b5729"; "beb964";
}
local chaosBagApi = require("chaosbag/ChaosBagApi")
local playAreaApi = require("core/PlayAreaApi")
local deckImporterApi = require("arkhamdb/DeckImporterApi")
local optionPanelApi = require("core/OptionPanelApi")
local blessCurseApi = require("chaosbag/BlessCurseManagerApi")
local TOUR_GUID = "0e5aa8"
local campaignBoxGUID
function onLoad(save_state)
campaignBoxGUID = ""
self.createButton({
click_function = "findCampaignFromToken",
function_owner = self,
label = "Import",
tooltip = "Load in a campaign save from a token!\n\n(Token can be anywhere on the table, but ensure there is only 1!)",
position = {x=-1, y=0.2, z=0},
font_size = 400,
width = 1400,
height = 600,
scale = {0.5, 1, 0.5},
})
self.createButton({
click_function = "createCampaignToken",
function_owner = self,
label = "Export",
tooltip = "Create a campaign save token!\n\n(Ensure all chaos tokens have been unsealed!)",
position = {x=1, y=0.2, z=0},
font_size = 400,
width = 1400,
height = 600,
scale = {0.5, 1, 0.5},
})
end
-- The main import functions. Due to timing concerns, has to be split up into several separate methods to allow for Wait conditions
-- Identifies import token, determines campaign box and downloads it (if needed)
function findCampaignFromToken(_, _, _)
local coin = nil
local coinObjects = getObjectsWithTag("ImporterToken")
if #coinObjects == 0 then
broadcastToAll("Could not find importer token", Color.Red)
elseif #coinObjects > 1 then
broadcastToAll("More than 1 importer token found. Please delete all but 1 importer token", Color.Yellow)
else
coin = coinObjects[1]
local importData = JSON.decode(coin.getGMNotes())
campaignBoxGUID = importData["box"]
local campaignBox = getObjectFromGUID(campaignBoxGUID)
if campaignBox.type == "Generic" then
campaignBox.call("buttonClick_download")
end
Wait.condition(
function()
if #campaignBox.getObjects() > 0 then
placeCampaignFromToken(importData)
else
createCampaignFromToken(importData)
end
end,
function()
local obj = getObjectFromGUID(campaignBoxGUID)
if obj == nil then
return false
else
return obj.type == "Bag" and obj.getLuaScript() ~= ""
end
end,
2,
function() broadcastToAll("Error loading campaign box") end
)
end
end
-- After box has been downloaded, places content on table
function placeCampaignFromToken(importData)
getObjectFromGUID(campaignBoxGUID).call("buttonClick_place")
Wait.condition(
function() createCampaignFromToken(importData) end,
function() return findCampaignLog() ~= nil end,
2,
function() broadcastToAll("Error placing campaign box") end
)
end
-- After content is placed on table, conducts all the other import operations
function createCampaignFromToken(importData)
findCampaignLog().destruct()
--create campaign log
spawnObjectData({data = importData["log"]})
--create chaos bag
chaosBagApi.setChaosBagState(importData["bag"])
--populate trauma values
if importData["trauma"] then
updateCounters(importData["trauma"])
end
--populate ArkhamDB deck IDs
if importData["decks"] then
deckImporterApi.setUiState(importData["decks"])
end
--set investigator count
playAreaApi.setInvestigatorCount(importData["clueCount"])
--set campaign guide page
local guide = findCampaignGuide()
if guide then
Wait.condition(
-- Called after the condition function returns true
function()
log("Campaign Guide import successful!")
end,
-- Condition function that is called continiously until returs true or timeout is reached
function()
guide.Book.setPage(importData["guide"])
return guide.Book.getPage() == importData["guide"]
end,
-- Amount of time in seconds until the Wait times out
1,
-- Called if the Wait times out
function()
log("Campaign Guide import failed!")
end
)
end
Wait.time(
function() optionPanelApi.loadSettings(importData["options"]) end,
0.5
)
getObjectFromGUID(TOUR_GUID).destruct()
playAreaApi.updateSurface(importData["playmat"])
broadcastToAll("Campaign successfully imported!", Color.Green)
end
-- Creates a campaign token with save data encoded into GM Notes based on the current state of the table
function createCampaignToken(_, playerColor, _)
-- clean up chaos tokens
blessCurseApi.removeAll(playerColor)
chaosBagApi.releaseAllSealedTokens(playerColor)
local campaignBoxGUID = ""
-- find active campaign
for _, obj in ipairs(getObjectsWithTag("CampaignBox")) do
if obj.type == "Bag" and #obj.getObjects() == 0 then
if campaignBoxGUID ~= "" then
broadcastToAll("Multiple empty campaign box detected; delete all but one.", Color.Red)
return
end
campaignBoxGUID = obj.getGUID()
end
end
if campaignBoxGUID == "" then
broadcastToAll("Campaign box with all placed objects not found!", Color.Red)
return
end
local campaignLog = findCampaignLog()
if campaignLog == nil then
broadcastToAll("Campaign log not found!", Color.Red)
return
end
local traumaValues = nil
local counterData = campaignLog.getVar("ref_buttonData")
if counterData ~= nil then
traumaValues = {}
printToAll("Trauma values found in campaign log!", "Green")
for i = 1, 10, 3 do
traumaValues[1 + (i - 1) / 3] = counterData.counter[i].value
traumaValues[5 + (i - 1) / 3] = counterData.counter[i + 1].value
end
else
printToAll("Trauma values could not be found in campaign log!", "Yellow")
printToAll("Default values for health and sanity loaded.", "Yellow")
end
local campaignGuide = findCampaignGuide()
if campaignGuide == nil then
broadcastToAll("Campaign guide not found!", Color.Red)
return
end
local campaignGuidePage = campaignGuide.Book.getPage()
local campaignData = {
box = campaignBoxGUID,
log = campaignLog.getData(),
bag = chaosBagApi.getChaosBagState(),
trauma = traumaValues,
decks = deckImporterApi.getUiState(),
clueCount = playAreaApi.getInvestigatorCount(),
guide = campaignGuidePage,
options = optionPanelApi.getOptions(),
playmat = playAreaApi.getSurface()
}
campaignTokenData.GMNotes = JSON.encode(campaignData)
campaignTokenData.Nickname = os.date("%b %d ") .. getObjectFromGUID(campaignBoxGUID).getName() .. " Save"
spawnObjectData({
data = campaignTokenData,
position = {-21.25, 1.68, 55.59}
})
broadcastToAll("Campaign successfully exported! Save coin object to import on a fresh save", Color.Green)
end
-- helper functions
function findCampaignLog()
local campaignLog = getObjectsWithTag("CampaignLog")
if campaignLog then
if #campaignLog == 1 then
return campaignLog[1]
else
broadcastToAll("More than 1 campaign log detected; delete all but one.", Color.Red)
return nil
end
else
return nil
end
end
function findCampaignGuide()
local campaignGuide = getObjectsWithTag("CampaignGuide")
if campaignGuide then
if #campaignGuide == 1 then
return campaignGuide[1]
else
broadcastToAll("More than 1 campaign guide detected; delete all but one.", Color.Red)
return nil
end
else
return nil
end
end
function updateCounters(tableOfNewValues)
if tonumber(tableOfNewValues) then
local value = tableOfNewValues
tableOfNewValues = {}
for i = 1, #DAMAGE_HORROR_GUIDS do
table.insert(tableOfNewValues, value)
end
end
for i, guid in ipairs(DAMAGE_HORROR_GUIDS) do
local TOKEN = getObjectFromGUID(guid)
if TOKEN ~= nil then
TOKEN.call("updateVal", tableOfNewValues[i])
else
printToAll(": No. " .. i .. " could not be found.", "Yellow")
end
end
end

View File

@ -7,6 +7,7 @@ local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi")
local soundCubeApi = require("core/SoundCubeApi") local soundCubeApi = require("core/SoundCubeApi")
local playmatApi = require("playermat/PlaymatApi") local playmatApi = require("playermat/PlaymatApi")
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
local chaosBagApi = require("chaosbag/ChaosBagApi")
-- these objects will be ignored -- these objects will be ignored
local IGNORE_GUIDS = { local IGNORE_GUIDS = {
@ -173,7 +174,7 @@ function cleanUp(_, color)
removeLines() removeLines()
discardHands() discardHands()
tokenSpawnTrackerApi.resetAll() tokenSpawnTrackerApi.resetAll()
Global.call("releaseAllSealedTokens", color) chaosBagApi.releaseAllSealedTokens(color)
printToAll("Tidying main play area...", "White") printToAll("Tidying main play area...", "White")
startLuaCoroutine(self, "tidyPlayareaCoroutine") startLuaCoroutine(self, "tidyPlayareaCoroutine")

View File

@ -0,0 +1,39 @@
do
local DeckImporterApi = {}
local DECK_IMPORTER_GUID = "a28140"
-- Returns a table with the full state of the UI, including options and deck IDs.
-- This can be used to persist via onSave(), or provide values for a load operation
-- Table values:
-- redDeck: Deck ID to load for the red player
-- orangeDeck: Deck ID to load for the orange player
-- whiteDeck: Deck ID to load for the white player
-- greenDeck: Deck ID to load for the green player
-- private: True to load a private deck, false to load a public deck
-- loadNewest: True if the most upgraded version of the deck should be loaded
-- investigators: True if investigator cards should be spawned
DeckImporterApi.getUiState = function()
local passthroughTable = {}
for k,v in pairs(getObjectFromGUID(DECK_IMPORTER_GUID).call("getUiState")) do
passthroughTable[k] = v
end
return passthroughTable
end
-- Updates the state of the UI based on the provided table. Any values not provided will be left the same.
-- @param uiStateTable Table of values to update on importer
-- Table values:
-- redDeck: Deck ID to load for the red player
-- orangeDeck: Deck ID to load for the orange player
-- whiteDeck: Deck ID to load for the white player
-- greenDeck: Deck ID to load for the green player
-- private: True to load a private deck, false to load a public deck
-- loadNewest: True if the most upgraded version of the deck should be loaded
-- investigators: True if investigator cards should be spawned
DeckImporterApi.setUiState = function(uiStateTable)
return getObjectFromGUID(DECK_IMPORTER_GUID).call("setUiState", uiStateTable)
end
return DeckImporterApi
end

View File

@ -45,6 +45,49 @@ function getUiState()
} }
end end
-- Updates the state of the UI based on the provided table. Any values not provided will be left the same.
-- @param uiStateTable Table of values to update on importer
-- Table values:
-- redDeck: Deck ID to load for the red player
-- orangeDeck: Deck ID to load for the orange player
-- whiteDeck: Deck ID to load for the white player
-- greenDeck: Deck ID to load for the green player
-- private: True to load a private deck, false to load a public deck
-- loadNewest: True if the most upgraded version of the deck should be loaded
-- investigators: True if investigator cards should be spawned
function setUiState(uiStateTable)
-- Callback functions aren't triggered when editing buttons/inputs so values must be set manually
if uiStateTable["greenDeck"] then
greenDeckId = uiStateTable["greenDeck"]
self.editInput({index=0, value=greenDeckId})
end
if uiStateTable["redDeck"] then
redDeckId = uiStateTable["redDeck"]
self.editInput({index=1, value=redDeckId})
end
if uiStateTable["whiteDeck"] then
whiteDeckId = uiStateTable["whiteDeck"]
self.editInput({index=2, value=whiteDeckId})
end
if uiStateTable["orangeDeck"]then
orangeDeckId = uiStateTable["orangeDeck"]
self.editInput({index=3, value=orangeDeckId})
end
if uiStateTable["private"] then
privateDeck = uiStateTable["private"]
self.editButton { index = 0, label = PRIVATE_TOGGLE_LABELS[privateDeck] }
end
if uiStateTable["loadNewest"] then
loadNewestDeck = uiStateTable["loadNewest"]
self.editButton { index = 1, label = UPGRADED_TOGGLE_LABELS[loadNewestDeck] }
end
if uiStateTable["investigators"] then
loadInvestigators = uiStateTable["investigators"]
self.editButton { index = 2, label = LOAD_INVESTIGATOR_TOGGLE_LABELS[loadInvestigators] }
end
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()
function initializeUi(savedUiState) function initializeUi(savedUiState)
if savedUiState ~= nil then if savedUiState ~= nil then

View File

@ -0,0 +1,32 @@
do
local ChaosBagApi = {}
-- respawns the chaos bag with a new state of tokens
---@param tokenList Table List of chaos token ids
ChaosBagApi.setChaosBagState = function(tokenList)
return Global.call("setChaosBagState", tokenList)
end
-- returns a Table List of chaos token ids in the current chaos bag
-- requires copying the data into a new table because TTS is weird about handling table return values in Global
ChaosBagApi.getChaosBagState = function()
local chaosBagContentsCatcher = Global.call("getChaosBagState")
local chaosBagContents = {}
for _, v in ipairs(chaosBagContentsCatcher) do
table.insert(chaosBagContents, v)
end
return chaosBagContents
end
-- checks scripting zone for chaos bag (also called by a lot of objects!)
ChaosBagApi.findChaosBag = function()
return Global.call("findChaosBag")
end
-- returns all sealed tokens on cards to the chaos bag
ChaosBagApi.releaseAllSealedTokens = function(playerColor)
return Global.call("releaseAllSealedTokens", playerColor)
end
return ChaosBagApi
end

View File

@ -548,8 +548,37 @@ function getDataValue(storage, key)
end end
end end
function createChaosTokenNameLookupTable()
local namesToIds = {}
for k, v in pairs(ID_URL_MAP) do
namesToIds[v.name] = k
end
return namesToIds
end
-- returns a Table List of chaos token ids in the current chaos bag
---@api chaosbag/ChaosBagApi
function getChaosBagState()
local tokens = {}
local invertedTable = createChaosTokenNameLookupTable()
local chaosbag = findChaosBag()
for _, v in ipairs(chaosbag.getObjects()) do
local id = invertedTable[v.name]
if id then
table.insert(tokens, id)
else
printToAll(v.name .. " token not recognized. Will not be recorded.", "Yellow")
end
end
return tokens
end
-- respawns the chaos bag with a new state of tokens -- respawns the chaos bag with a new state of tokens
---@param tokenList Table List of chaos token ids ---@param tokenList Table List of chaos token ids
---@api chaosbag/ChaosBagApi
function setChaosBagState(tokenList) function setChaosBagState(tokenList)
if not canTouchChaosTokens() then return end if not canTouchChaosTokens() then return end
@ -975,7 +1004,9 @@ end
---@param rotation Vector Rotation of the object for spawning (default: {0, 270, 0}) ---@param rotation Vector Rotation of the object for spawning (default: {0, 270, 0})
---@return. GUID of the spawnedObj (or nil if object was removed) ---@return. GUID of the spawnedObj (or nil if object was removed)
function spawnOrRemoveHelper(state, name, position, rotation) function spawnOrRemoveHelper(state, name, position, rotation)
if state then if (type(state) == "table" and #state == 0) then
return removeHelperObject(name)
elseif state then
Player.getPlayers()[1].pingTable(position) Player.getPlayers()[1].pingTable(position)
return spawnHelperObject(name, position, rotation).getGUID() return spawnHelperObject(name, position, rotation).getGUID()
else else
@ -1045,6 +1076,15 @@ function removeHelperObject(name)
end end
end end
-- loads saved options
function loadSettings(newOptions)
optionPanel = newOptions
updateOptionPanelState()
for id, state in pairs(optionPanel) do
applyOptionPanelChange(id, state)
end
end
-- loads the default options -- loads the default options
function onClick_defaultSettings() function onClick_defaultSettings()
for id, _ in pairs(optionPanel) do for id, _ in pairs(optionPanel) do

View File

@ -4,7 +4,7 @@ do
-- 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("9f334f").call("returnTokenData") return getObjectFromGUID(MYTHOS_AREA_GUID).call("returnTokenData")
end end
return MythosAreaApi return MythosAreaApi

View File

@ -0,0 +1,16 @@
do
local OptionPanelApi = {}
-- loads saved options
---@param options Table New options table
OptionPanelApi.loadSettings = function(options)
return Global.call("loadSettings", options)
end
-- returns option panel table
OptionPanelApi.getOptions = function()
return Global.getTable("optionPanel")
end
return OptionPanelApi
end

View File

@ -511,6 +511,13 @@ function getInvestigatorCount()
return investigatorCounter.getVar("val") return investigatorCounter.getVar("val")
end end
-- Updates the current value of the investigator counter from the playmat
---@param count Number of investigators to set on the counter
function setInvestigatorCount(count)
local investigatorCounter = getObjectFromGUID("f182ee")
return investigatorCounter.call("updateVal", count)
end
-- Check to see if the given object is within the bounds of the play area, based solely on the X and -- Check to see if the given object is within the bounds of the play area, based solely on the X and
-- Z coordinates, ignoring height -- Z coordinates, ignoring height
---@param object Object Object to check ---@param object Object Object to check

View File

@ -3,12 +3,20 @@ do
local PLAY_AREA_GUID = "721ba2" local PLAY_AREA_GUID = "721ba2"
local IMAGE_SWAPPER = "b7b45b"
-- 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(PLAY_AREA_GUID).call("getInvestigatorCount") return getObjectFromGUID(PLAY_AREA_GUID).call("getInvestigatorCount")
end end
-- Updates the current value of the investigator counter from the playmat
---@param count Number of investigators to set on the counter
PlayAreaApi.setInvestigatorCount = function(count)
return getObjectFromGUID(PLAY_AREA_GUID).call("setInvestigatorCount", count)
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'
@ -77,5 +85,13 @@ do
return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object) return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object)
end end
PlayAreaApi.getSurface = function()
return getObjectFromGUID(PLAY_AREA_GUID).getCustomObject().image
end
PlayAreaApi.updateSurface = function(url)
return getObjectFromGUID(IMAGE_SWAPPER).call("updateSurface", url)
end
return PlayAreaApi return PlayAreaApi
end end