Merge pull request #302 from Entrox-Licher/entrox-main
Added a Campaign Importer/Exporter!
This commit is contained in:
commit
1cff0cb5be
@ -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"
|
||||||
],
|
],
|
||||||
|
@ -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"
|
||||||
|
57
objects/CampaignImporterExporter.334ee3.json
Normal file
57
objects/CampaignImporterExporter.334ee3.json
Normal 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": ""
|
||||||
|
}
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
298
src/accessories/CampaignImporterExporter.ttslua
Normal file
298
src/accessories/CampaignImporterExporter.ttslua
Normal 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
|
@ -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")
|
||||||
|
39
src/arkhamdb/DeckImporterApi.ttslua
Normal file
39
src/arkhamdb/DeckImporterApi.ttslua
Normal 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
|
@ -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
|
||||||
|
32
src/chaosbag/ChaosBagApi.ttslua
Normal file
32
src/chaosbag/ChaosBagApi.ttslua
Normal 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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
16
src/core/OptionPanelApi.ttslua
Normal file
16
src/core/OptionPanelApi.ttslua
Normal 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
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user