Merge branch 'main' into image-swapper-fix
This commit is contained in:
commit
14aa296b7f
@ -154,7 +154,7 @@
|
||||
"ChaosBagStatTracker.766620",
|
||||
"Blesstokens.afa06b",
|
||||
"Cursetokens.bd0253",
|
||||
"WhimsicalsTokenRemover.0a5a29",
|
||||
"TokenRemover.0a5a29",
|
||||
"TokenSpawner.36b4ee",
|
||||
"Fan-MadeScenariosCampaignsMiscellany.66e97c",
|
||||
"OfficialStandaloneChallengeScenarios.0ef5c8",
|
||||
@ -250,7 +250,8 @@
|
||||
"TokenSpawnTracker.e3ffc9",
|
||||
"TokenSource.124381",
|
||||
"GameData.3dbe47",
|
||||
"SCEDTour.0e5aa8"
|
||||
"SCEDTour.0e5aa8",
|
||||
"PlayerCards.2d30ee"
|
||||
],
|
||||
"PlayArea": 1,
|
||||
"PlayerCounts": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"10": {
|
||||
"body": "Created by Whimsical\n\nAnything that passes over the remover that isn't a card or a deck will be deleted.\r\nTo use the remover, right click on it, choose the \"Enable\" option, and take your card with resources/horror/damage and swipe it over the remover. You may wish to unlock and/or copy the remover to your play area first.",
|
||||
"body": "Created by Whimsical\n\nAnything that passes over the remover that isn't a card, deck or chaos token will be deleted.\r\nTo use the remover, right click on it, choose the \"Enable\" option, and take your card with resources/horror/damage and swipe it over the remover. You may wish to unlock and/or copy the remover to your play area first.",
|
||||
"color": "Grey",
|
||||
"id": 10,
|
||||
"title": "Token Remover",
|
||||
|
@ -33,7 +33,7 @@
|
||||
"IgnoreFoW": false,
|
||||
"LayoutGroupSortIndex": 0,
|
||||
"Locked": false,
|
||||
"LuaScriptState": "{\"Bless\":8,\"Curse\":0}",
|
||||
"LuaScriptState": "",
|
||||
"LuaScript_path": "Fan-MadeAccessories.aa8b38/ChaosBagManager.023240.ttslua",
|
||||
"MeasureMovement": false,
|
||||
"Name": "Custom_Token",
|
||||
@ -48,9 +48,9 @@
|
||||
"posX": 22.215,
|
||||
"posY": 5.651,
|
||||
"posZ": -34.811,
|
||||
"rotX": 4,
|
||||
"rotX": 0,
|
||||
"rotY": 270,
|
||||
"rotZ": 357,
|
||||
"rotZ": 0,
|
||||
"scaleX": 2.5,
|
||||
"scaleY": 1,
|
||||
"scaleZ": 2.5
|
||||
|
@ -62,7 +62,7 @@ function onLoad()
|
||||
-- create buttons for tokens
|
||||
for i = 1, #BUTTON_POSITION do
|
||||
local funcName = "buttonClick" .. i
|
||||
self.setVar(funcName, function(_, _, isRightClick) buttonClick(_, _, isRightClick, i) end)
|
||||
self.setVar(funcName, function(_, _, isRightClick) buttonClick(i, isRightClick) end)
|
||||
|
||||
buttonParameters.click_function = funcName
|
||||
buttonParameters.tooltip = BUTTON_TOOLTIP[i]
|
||||
@ -70,8 +70,8 @@ function onLoad()
|
||||
|
||||
if i < 7 then
|
||||
buttonParameters.position.z = -0.778
|
||||
elseif i > 12 then
|
||||
buttonParameters.position.z = 0.75
|
||||
elseif i > 11 then
|
||||
buttonParameters.position.z = 0.755
|
||||
end
|
||||
|
||||
self.createButton(buttonParameters)
|
||||
@ -111,7 +111,7 @@ function getChaosBag()
|
||||
end
|
||||
|
||||
-- click function for buttons
|
||||
function buttonClick(_, _, isRightClick, index)
|
||||
function buttonClick(index, isRightClick)
|
||||
chaosbag = getChaosBag()
|
||||
|
||||
-- error handling: chaos bag not found
|
||||
|
@ -153,6 +153,7 @@ end
|
||||
---------------------------------------------------------
|
||||
-- discards a random card from hand
|
||||
---------------------------------------------------------
|
||||
|
||||
function discardRandom()
|
||||
if not playerExists(playerColor) then return end
|
||||
|
||||
@ -162,10 +163,8 @@ function discardRandom()
|
||||
broadcastToAll("Cannot discard from empty hand!", "Red")
|
||||
else
|
||||
local searchPos = Player[playerColor].getHandTransform().position
|
||||
local mat = playmatAPI.getMatbyPosition(searchPos)
|
||||
if mat == nil then return end
|
||||
|
||||
local discardPos = mat.getTable("DISCARD_PILE_POSITION")
|
||||
local discardPos = playmatAPI.getDiscardPosition(playmatAPI.getMatColorByPosition(searchPos))
|
||||
if discardPos == nil then
|
||||
broadcastToAll("Couldn't retrieve discard position from playermat!", "Red")
|
||||
return
|
||||
|
@ -1 +1 @@
|
||||
{"optionPanel":{"showChaosBagManager":false,"showCleanUpHelper":false,"showDrawButton":false,"showHandHelper":[],"showNavigationOverlay":false,"showTokenArranger":false,"useClueClickers":false,"useSnapTags":true}}
|
||||
{"optionPanel":{"showAttachmentHelper":false,"showChaosBagManager":false,"showCleanUpHelper":false,"useClueClickers":false,"showCustomPlaymatImages":false,"showCYOA":false,"showDisplacementTool":false,"showDrawButton":false,"showHandHelper":[],"showNavigationOverlay":false,"useSnapTags":true,"showTitleSplash":true,"showTokenArranger":false}}
|
||||
|
57
objects/PlayerCards.2d30ee.json
Normal file
57
objects/PlayerCards.2d30ee.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.1,
|
||||
"Type": 3
|
||||
},
|
||||
"ImageScalar": 1,
|
||||
"ImageSecondaryURL": "https://i.imgur.com/dISlnEk.jpg",
|
||||
"ImageURL": "https://i.imgur.com/dISlnEk.jpg",
|
||||
"WidthScale": 0
|
||||
},
|
||||
"Description": "",
|
||||
"DragSelectable": true,
|
||||
"GMNotes": "",
|
||||
"GUID": "2d30ee",
|
||||
"Grid": true,
|
||||
"GridProjection": false,
|
||||
"Hands": false,
|
||||
"HideWhenFaceDown": false,
|
||||
"IgnoreFoW": false,
|
||||
"LayoutGroupSortIndex": 0,
|
||||
"Locked": false,
|
||||
"LuaScript": "require(\"playercards/PlayerCardPanel\")",
|
||||
"LuaScriptState": "{\"spawnBagState\":{\"placed\":[],\"placedObjects\":[]}}",
|
||||
"MeasureMovement": false,
|
||||
"Name": "Custom_Tile",
|
||||
"Nickname": "Player Cards",
|
||||
"Snap": true,
|
||||
"Sticky": true,
|
||||
"Tooltip": false,
|
||||
"Transform": {
|
||||
"posX": -1.083,
|
||||
"posY": 1.255,
|
||||
"posZ": 69.985,
|
||||
"rotX": 0,
|
||||
"rotY": 270,
|
||||
"rotZ": 0,
|
||||
"scaleX": 9.39,
|
||||
"scaleY": 1,
|
||||
"scaleZ": 9.39
|
||||
},
|
||||
"Value": 0,
|
||||
"XmlUI": ""
|
||||
}
|
@ -14,7 +14,7 @@
|
||||
"CustomTile": {
|
||||
"Stackable": false,
|
||||
"Stretch": true,
|
||||
"Thickness": 0.2,
|
||||
"Thickness": 0.1,
|
||||
"Type": 0
|
||||
},
|
||||
"ImageScalar": 1,
|
||||
@ -34,10 +34,10 @@
|
||||
"LayoutGroupSortIndex": 0,
|
||||
"Locked": false,
|
||||
"LuaScript": "require(\"util/TokenRemover\")",
|
||||
"LuaScriptState": "[]",
|
||||
"LuaScriptState": "",
|
||||
"MeasureMovement": false,
|
||||
"Name": "Custom_Tile",
|
||||
"Nickname": "Whimsical's Token Remover",
|
||||
"Nickname": "Token Remover",
|
||||
"Snap": true,
|
||||
"Sticky": true,
|
||||
"Tags": [
|
||||
@ -45,16 +45,16 @@
|
||||
],
|
||||
"Tooltip": true,
|
||||
"Transform": {
|
||||
"posX": -6.868,
|
||||
"posX": -7,
|
||||
"posY": 1.583,
|
||||
"posZ": -16.394,
|
||||
"posZ": -16.4,
|
||||
"rotX": 0,
|
||||
"rotY": 90,
|
||||
"rotZ": 0,
|
||||
"scaleX": 0.75,
|
||||
"scaleX": 0.8,
|
||||
"scaleY": 1,
|
||||
"scaleZ": 0.75
|
||||
"scaleZ": 0.8
|
||||
},
|
||||
"Value": 0,
|
||||
"XmlUI": ""
|
||||
}
|
||||
}
|
@ -18,7 +18,8 @@
|
||||
"Damage.cd2a02",
|
||||
"Horror.36be72",
|
||||
"ClueDoom.a3fb6c",
|
||||
"Resource.00d19a"
|
||||
"Resource.00d19a",
|
||||
"ResourceCounter.498ec0"
|
||||
],
|
||||
"ContainedObjects_path": "TokenSource.124381",
|
||||
"Description": "",
|
||||
|
@ -40,7 +40,7 @@
|
||||
"Nickname": "ClueDoom",
|
||||
"Snap": false,
|
||||
"Sticky": true,
|
||||
"Tooltip": false,
|
||||
"Tooltip": true,
|
||||
"Transform": {
|
||||
"posX": 78.661,
|
||||
"posY": 2.398,
|
||||
|
@ -40,7 +40,7 @@
|
||||
"Nickname": "ClueDoom",
|
||||
"Snap": false,
|
||||
"Sticky": true,
|
||||
"Tooltip": false,
|
||||
"Tooltip": true,
|
||||
"Transform": {
|
||||
"posX": 78.738,
|
||||
"posY": 2.287,
|
||||
|
@ -40,7 +40,7 @@
|
||||
"Nickname": "Resource",
|
||||
"Snap": false,
|
||||
"Sticky": true,
|
||||
"Tooltip": false,
|
||||
"Tooltip": true,
|
||||
"Transform": {
|
||||
"posX": 78.848,
|
||||
"posY": 2.273,
|
||||
|
57
objects/TokenSource.124381/ResourceCounter.498ec0.json
Normal file
57
objects/TokenSource.124381/ResourceCounter.498ec0.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"AltLookAngle": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"Autoraise": true,
|
||||
"ColorDiffuse": {
|
||||
"b": 1,
|
||||
"g": 1,
|
||||
"r": 1
|
||||
},
|
||||
"CustomImage": {
|
||||
"CustomToken": {
|
||||
"MergeDistancePixels": 5,
|
||||
"Stackable": false,
|
||||
"StandUp": false,
|
||||
"Thickness": 0.1
|
||||
},
|
||||
"ImageScalar": 1,
|
||||
"ImageSecondaryURL": "",
|
||||
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/949599153663401115/EAA6D40FC6E15204BBE551BCDED35CC8C75111BF/",
|
||||
"WidthScale": 0
|
||||
},
|
||||
"Description": "0",
|
||||
"DragSelectable": true,
|
||||
"GMNotes": "resourceCounter",
|
||||
"GUID": "498ec0",
|
||||
"Grid": true,
|
||||
"GridProjection": false,
|
||||
"Hands": false,
|
||||
"HideWhenFaceDown": false,
|
||||
"IgnoreFoW": false,
|
||||
"LayoutGroupSortIndex": 0,
|
||||
"Locked": false,
|
||||
"LuaScript": "require(\"core/GenericCounter\")",
|
||||
"LuaScriptState": "0",
|
||||
"MeasureMovement": false,
|
||||
"Name": "Custom_Token",
|
||||
"Nickname": "Resource Counter",
|
||||
"Snap": false,
|
||||
"Sticky": true,
|
||||
"Tooltip": false,
|
||||
"Transform": {
|
||||
"posX": 0,
|
||||
"posY": 3,
|
||||
"posZ": 0,
|
||||
"rotX": 0,
|
||||
"rotY": 270,
|
||||
"rotZ": 0,
|
||||
"scaleX": 0.26,
|
||||
"scaleY": 1,
|
||||
"scaleZ": 0.26
|
||||
},
|
||||
"Value": 0,
|
||||
"XmlUI": ""
|
||||
}
|
@ -134,15 +134,6 @@ do
|
||||
|
||||
internal.maybePrint(table.concat({ "Found decklist: ", deck.name }), playerColor)
|
||||
|
||||
log(table.concat({ "-", deck.name, "-" }))
|
||||
for k, v in pairs(deck) do
|
||||
if type(v) == "table" then
|
||||
log(table.concat { k, ": <table>" })
|
||||
else
|
||||
log(table.concat { k, ": ", tostring(v) })
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize deck slot table and perform common transformations. The order of these should not
|
||||
-- be changed, as later steps may act on cards added in each. For example, a random weakness or
|
||||
-- investigator may have bonded cards or taboo entries, and should be present
|
||||
|
@ -1,50 +1,52 @@
|
||||
MIN_VALUE = -99
|
||||
MAX_VALUE = 999
|
||||
MIN_VALUE = 0
|
||||
MAX_VALUE = 99
|
||||
val = 0
|
||||
|
||||
function onSave() return JSON.encode(val) end
|
||||
|
||||
function onLoad(saved_data)
|
||||
if saved_data ~= nil then
|
||||
val = JSON.decode(saved_data)
|
||||
end
|
||||
function onLoad(savedData)
|
||||
if savedData ~= nil then
|
||||
val = JSON.decode(savedData)
|
||||
end
|
||||
|
||||
local name = self.getName()
|
||||
local position = {}
|
||||
local name = self.getName()
|
||||
local position = {}
|
||||
|
||||
if name == "Damage" or name == "Resources" then
|
||||
position = { 0, 0.06, 0.1 }
|
||||
elseif name == "Horror" then
|
||||
position = { -0.025, 0.06, -0.025 }
|
||||
else
|
||||
position = { 0, 0.06, 0 }
|
||||
end
|
||||
if name == "Damage" or name == "Resources" or name == "Resource Counter" then
|
||||
position = { 0, 0.06, 0.1 }
|
||||
elseif name == "Horror" then
|
||||
position = { -0.025, 0.06, -0.025 }
|
||||
else
|
||||
position = { 0, 0.06, 0 }
|
||||
end
|
||||
|
||||
self.createButton({
|
||||
label = tostring(val),
|
||||
click_function = "addOrSubtract",
|
||||
function_owner = self,
|
||||
position = position,
|
||||
height = 600,
|
||||
width = 1000,
|
||||
scale = { 1.5, 1.5, 1.5 },
|
||||
font_size = 600,
|
||||
font_color = { 1, 1, 1, 100 },
|
||||
color = { 0, 0, 0, 0 }
|
||||
})
|
||||
self.createButton({
|
||||
label = tostring(val),
|
||||
click_function = "addOrSubtract",
|
||||
function_owner = self,
|
||||
position = position,
|
||||
height = 600,
|
||||
width = 1000,
|
||||
scale = { 1.5, 1.5, 1.5 },
|
||||
font_size = 600,
|
||||
font_color = { 1, 1, 1, 100 },
|
||||
color = { 0, 0, 0, 0 }
|
||||
})
|
||||
|
||||
self.addContextMenuItem("Add 5", function() updateVal(val + 5) end)
|
||||
self.addContextMenuItem("Subtract 5", function() updateVal(val - 5) end)
|
||||
self.addContextMenuItem("Add 10", function() updateVal(val + 10) end)
|
||||
self.addContextMenuItem("Subtract 10", function() updateVal(val - 10) end)
|
||||
end
|
||||
|
||||
function updateVal(newVal)
|
||||
if tonumber(newVal) then
|
||||
val = newVal
|
||||
self.editButton({
|
||||
index = 0,
|
||||
label = tostring(val)
|
||||
})
|
||||
end
|
||||
if tonumber(newVal) then
|
||||
val = math.min(math.max(newVal, MIN_VALUE), MAX_VALUE)
|
||||
self.editButton({ index = 0, label = tostring(val) })
|
||||
end
|
||||
end
|
||||
|
||||
function addOrSubtract(_, _, alt_click)
|
||||
val = math.min(math.max(val + (alt_click and -1 or 1), MIN_VALUE), MAX_VALUE)
|
||||
self.editButton({ index = 0, label = tostring(val) })
|
||||
function addOrSubtract(_, _, isRightClick)
|
||||
val = math.min(math.max(val + (isRightClick and -1 or 1), MIN_VALUE), MAX_VALUE)
|
||||
self.editButton({ index = 0, label = tostring(val) })
|
||||
end
|
||||
|
@ -1,5 +1,3 @@
|
||||
local tokenManager = require("core/token/TokenManager")
|
||||
|
||||
---------------------------------------------------------
|
||||
-- general setup
|
||||
---------------------------------------------------------
|
||||
@ -34,7 +32,9 @@ local chaosTokens = {}
|
||||
local chaosTokensLastMat = nil
|
||||
local IS_RESHUFFLING = false
|
||||
local bagSearchers = {}
|
||||
local hideTitleSplashWaitFunctionId = nil
|
||||
local playmatAPI = require("playermat/PlaymatApi")
|
||||
local tokenManager = require("core/token/TokenManager")
|
||||
|
||||
---------------------------------------------------------
|
||||
-- data for tokens
|
||||
@ -818,12 +818,20 @@ function applyOptionPanelChange(id, state)
|
||||
-- update master clue counter
|
||||
getObjectFromGUID("4a3aa4").setVar("useClickableCounters", state)
|
||||
|
||||
-- option: Clickable resource counters
|
||||
elseif id == "useResourceCounters" then
|
||||
optionPanel[id] = state
|
||||
|
||||
-- option: Show Title on placing scenarios
|
||||
elseif id == "showTitleSplash" then
|
||||
optionPanel[id] = state
|
||||
|
||||
-- option: Show token arranger
|
||||
elseif id == "showTokenArranger" then
|
||||
-- delete previously pulled out tokens
|
||||
for _, token in ipairs(getObjectsWithTag("to_be_deleted")) do token.destruct() end
|
||||
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Token Arranger", {-42.3, 1.4, -46.5})
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Token Arranger", {-42.3, 1.6, -46.5})
|
||||
|
||||
-- option: Show clean up helper
|
||||
elseif id == "showCleanUpHelper" then
|
||||
@ -838,11 +846,27 @@ function applyOptionPanelChange(id, state)
|
||||
|
||||
-- option: Show chaos bag manager
|
||||
elseif id == "showChaosBagManager" then
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Chaos Bag Manager", {-67.8, 1.4, -49.5})
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Chaos Bag Manager", {-67.8, 1.6, -49.5})
|
||||
|
||||
-- option: Show attachment helper
|
||||
elseif id == "showAttachmentHelper" then
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Attachment Helper", {-64, 1.4, 0})
|
||||
|
||||
-- option: Show navigation overlay
|
||||
elseif id == "showNavigationOverlay" then
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "jaqenZann's Navigation Overlay", {-11.7, 1.4, -15})
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "jaqenZann's Navigation Overlay", {-11.7, 1.6, -15})
|
||||
|
||||
-- option: Show CYOA campaign guides
|
||||
elseif id == "showCYOA" then
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "CYOA Campaign Guides", {21.2, 1.4, -65.7})
|
||||
|
||||
-- option: Show custom playmat images
|
||||
elseif id == "showCustomPlaymatImages" then
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Custom Playmat Images", {12, 1.4, -46.5})
|
||||
|
||||
-- option: Show displacement tool
|
||||
elseif id == "showDisplacementTool" then
|
||||
optionPanel[id] = spawnOrRemoveHelper(state, "Displacement Tool", {-60.7, 1.4, -49.5})
|
||||
end
|
||||
end
|
||||
|
||||
@ -851,8 +875,8 @@ end
|
||||
---@param name String Name of the helper object
|
||||
---@param position Vector Position of the object (where it will spawn)
|
||||
---@param rotation Vector Rotation of the object for spawning (default: {0, 270, 0})
|
||||
---@param color Color This is only needed for correctly setting the color of the "Hand Helper"
|
||||
-- returns the GUID of the spawnedObj (or nil if object was removed)
|
||||
---@param color String This is only needed for correctly setting the color of the "Hand Helper"
|
||||
---@return. GUID of the spawnedObj (or nil if object was removed)
|
||||
function spawnOrRemoveHelper(state, name, position, rotation, color)
|
||||
if state then
|
||||
Player.getPlayers()[1].pingTable(position)
|
||||
@ -864,7 +888,7 @@ end
|
||||
|
||||
-- copies the specified tool (by name) from the barrel
|
||||
---@param name String Name of the object that should be copied
|
||||
---@param position Position Desired position of the object
|
||||
---@param position Table Desired position of the object
|
||||
function spawnHelperObject(name, position, rotation, color)
|
||||
local barrel = getObjectFromGUID(BARREL_GUID)
|
||||
|
||||
@ -907,7 +931,11 @@ function removeHelperObject(name)
|
||||
["Clean Up Helper"] = "showCleanUpHelper",
|
||||
["Hand Helper"] = "showHandHelper",
|
||||
["Chaos Bag Manager"] = "showChaosBagManager",
|
||||
["jaqenZann's Navigation Overlay"] = "showNavigationOverlay"
|
||||
["jaqenZann's Navigation Overlay"] = "showNavigationOverlay",
|
||||
["Displacement Tool"] = "showDisplacementTool",
|
||||
["Custom Playmat Images"] = "showCustomPlaymatImages",
|
||||
["Attachment Helper"] = "showAttachmentHelper",
|
||||
["CYOA Campaign Guides"] = "showCYOA"
|
||||
}
|
||||
|
||||
local data = optionPanel[referenceTable[name]]
|
||||
@ -931,7 +959,7 @@ function onClick_defaultSettings()
|
||||
for id, _ in pairs(optionPanel) do
|
||||
local state = false
|
||||
-- override for settings that are enabled by default
|
||||
if id == "useSnapTags" then
|
||||
if id == "useSnapTags" or id == "showTitleSplash" then
|
||||
state = true
|
||||
end
|
||||
applyOptionPanelChange(id, state)
|
||||
@ -939,16 +967,43 @@ function onClick_defaultSettings()
|
||||
|
||||
-- clean reset of variable
|
||||
optionPanel = {
|
||||
useSnapTags = true,
|
||||
showDrawButton = false,
|
||||
useClueClickers = false,
|
||||
showTokenArranger = false,
|
||||
showAttachmentHelper = false,
|
||||
showCleanUpHelper = false,
|
||||
showHandHelper = {},
|
||||
showChaosBagManager = false,
|
||||
showNavigationOverlay = false
|
||||
showCustomPlaymatImages = false,
|
||||
showCYOA = false,
|
||||
showDisplacementTool = false,
|
||||
showDrawButton = false,
|
||||
showHandHelper = {},
|
||||
showNavigationOverlay = false,
|
||||
showTitleSplash = true,
|
||||
showTokenArranger = false,
|
||||
useClueClickers = false,
|
||||
useSnapTags = true
|
||||
}
|
||||
|
||||
-- update UI
|
||||
updateOptionPanelState()
|
||||
end
|
||||
|
||||
-- splash scenario title on setup
|
||||
function titleSplash(scenarioName)
|
||||
if optionPanel['showTitleSplash'] then
|
||||
|
||||
-- if there's any ongoing title being displayed, hide it and cancel the waiting function
|
||||
if hideTitleSplashWaitFunctionId then
|
||||
Wait.stop(hideTitleSplashWaitFunctionId)
|
||||
hideTitleSplashWaitFunctionId = nil
|
||||
UI.setAttribute('title_splash', 'active', false)
|
||||
end
|
||||
|
||||
-- display scenario name and set a 4 seconds (2 seconds animation and 2 seconds on screen)
|
||||
-- wait timer to hide the scenario name
|
||||
UI.setValue('title_splash', scenarioName)
|
||||
UI.show('title_splash')
|
||||
hideTitleSplashWaitFunctionId = Wait.time(function()
|
||||
UI.hide('title_splash')
|
||||
hideTitleSplashWaitFunctionId = nil
|
||||
end, 4)
|
||||
end
|
||||
end
|
||||
|
@ -55,6 +55,7 @@ function resetTokensIfInDeckZone(container, object)
|
||||
end
|
||||
|
||||
function fireScenarioChangedEvent()
|
||||
Global.call('titleSplash', currentScenario)
|
||||
playArea.onScenarioChanged(currentScenario)
|
||||
end
|
||||
|
||||
|
@ -3,10 +3,24 @@
|
||||
---------------------------------------------------------
|
||||
|
||||
-- set true to enable debug logging
|
||||
DEBUG = false
|
||||
local DEBUG = false
|
||||
|
||||
-- Location connection directional options
|
||||
local BIDIRECTIONAL = 0
|
||||
local ONE_WAY = 1
|
||||
|
||||
-- Connector draw parameters
|
||||
local CONNECTION_THICKNESS = 0.015
|
||||
local CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 }
|
||||
local DIRECTIONAL_ARROW_DISTANCE = 3.5
|
||||
local ARROW_ARM_LENGTH = 0.9
|
||||
local ARROW_ANGLE = 25
|
||||
|
||||
-- Height to draw the connector lines, places them just above the table and always below cards
|
||||
local CONNECTION_LINE_Y = 1.529
|
||||
|
||||
-- we use this to turn off collision handling until onLoad() is complete
|
||||
COLLISION_ENABLED = false
|
||||
local collisionEnabled = false
|
||||
|
||||
-- used for recreating the link to a custom data helper after image change
|
||||
customDataHelper = nil
|
||||
@ -22,10 +36,24 @@ local SHIFT_EXCLUSION = {
|
||||
["f182ee"] = true,
|
||||
["721ba2"] = true
|
||||
}
|
||||
local LOC_LINK_EXCLUDE_SCENARIOS = {
|
||||
["Devil Reef"] = true,
|
||||
["The Witching Hour"] = true,
|
||||
}
|
||||
|
||||
local tokenManager = require("core/token/TokenManager")
|
||||
local INVESTIGATOR_COUNTER_GUID = "f182ee"
|
||||
local PLAY_AREA_ZONE_GUID = "a2f932"
|
||||
|
||||
local clueData = {}
|
||||
local spawnedLocationGUIDs = {}
|
||||
|
||||
local locations = { }
|
||||
local locationConnections = { }
|
||||
local draggingGuids = { }
|
||||
|
||||
local locationData
|
||||
|
||||
local currentScenario
|
||||
|
||||
---------------------------------------------------------
|
||||
@ -34,17 +62,19 @@ local currentScenario
|
||||
|
||||
function onSave()
|
||||
return JSON.encode({
|
||||
currentScenario = currentScenario
|
||||
trackedLocations = locations,
|
||||
currentScenario = currentScenario,
|
||||
})
|
||||
end
|
||||
|
||||
function onLoad(saveState)
|
||||
-- records locations we have spawned clues for
|
||||
local saveData = JSON.decode(saveState) or {}
|
||||
currentScenario = saveData.currentScenario
|
||||
local save = JSON.decode(saveState) or { }
|
||||
locations = save.trackedLocations or { }
|
||||
currentScenario = save.currentScenario
|
||||
|
||||
self.interactable = DEBUG
|
||||
Wait.time(function() COLLISION_ENABLED = true end, 1)
|
||||
Wait.time(function() collisionEnabled = true end, 1)
|
||||
end
|
||||
|
||||
function log(message)
|
||||
@ -61,17 +91,20 @@ function updateLocations(args)
|
||||
end
|
||||
end
|
||||
|
||||
function onCollisionEnter(collision_info)
|
||||
local obj = collision_info.collision_object
|
||||
function onCollisionEnter(collisionInfo)
|
||||
local obj = collisionInfo.collision_object
|
||||
local objType = obj.name
|
||||
|
||||
-- only continue for cards
|
||||
if not COLLISION_ENABLED or (objType ~= "Card" and objType ~= "CardCustom") then return end
|
||||
if not collisionEnabled or (objType ~= "Card" and objType ~= "CardCustom") then return end
|
||||
|
||||
-- check if we should spawn clues here and do so according to playercount
|
||||
if shouldSpawnTokens(obj) then
|
||||
tokenManager.spawnForCard(obj)
|
||||
local card = collisionInfo.collision_object
|
||||
if shouldSpawnTokens(card) then
|
||||
tokenManager.spawnForCard(card)
|
||||
end
|
||||
draggingGuids[card.getGUID()] = nil
|
||||
maybeTrackLocation(card)
|
||||
end
|
||||
|
||||
function shouldSpawnTokens(card)
|
||||
@ -85,6 +118,267 @@ function shouldSpawnTokens(card)
|
||||
or metadata.weakness
|
||||
end
|
||||
|
||||
function onCollisionExit(collisionInfo)
|
||||
maybeUntrackLocation(collisionInfo.collision_object)
|
||||
end
|
||||
|
||||
-- Destroyed objects don't trigger onCollisionExit(), so check on destruction to untrack as well
|
||||
function onObjectDestroy(object)
|
||||
maybeUntrackLocation(object)
|
||||
end
|
||||
|
||||
function onObjectPickUp(player, object)
|
||||
-- onCollisionExit fires first, so we have to check the card to see if it's a location we should
|
||||
-- be tracking
|
||||
if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then
|
||||
local pickedUpGuid = object.getGUID()
|
||||
local metadata = JSON.decode(object.getGMNotes())
|
||||
if (metadata.type == "Location") then
|
||||
draggingGuids[pickedUpGuid] = metadata
|
||||
rebuildConnectionList()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function onUpdate()
|
||||
-- Due to the frequence of onUpdate calls, ensure that we only process any changes to the
|
||||
-- connection list once, and only redraw once
|
||||
local needsConnectionRebuild = false
|
||||
local needsConnectionDraw = false
|
||||
for guid, _ in pairs(draggingGuids) do
|
||||
local obj = getObjectFromGUID(guid)
|
||||
if obj == nil or not isInPlayArea(obj) then
|
||||
draggingGuids[guid] = nil
|
||||
needsConnectionRebuild = true
|
||||
end
|
||||
-- Even if the last location left the play area, need one last draw to clear the lines
|
||||
needsConnectionDraw = true
|
||||
end
|
||||
if (needsConnectionRebuild) then
|
||||
rebuildConnectionList()
|
||||
end
|
||||
if needsConnectionDraw then
|
||||
drawConnections()
|
||||
end
|
||||
end
|
||||
|
||||
-- Checks the given card and adds it to the list of locations tracked for connection purposes.
|
||||
-- A card will be added to the tracking if it is a location in the play area (based on centerpoint).
|
||||
-- @param A card object, possibly a location.
|
||||
function maybeTrackLocation(card)
|
||||
-- Collision checks for any part of the card overlap, but our other tracking is centerpoint
|
||||
-- Ignore any collision where the centerpoint isn't in the area
|
||||
if showLocationLinks() and isInPlayArea(card) then
|
||||
local metadata = JSON.decode(card.getGMNotes()) or { }
|
||||
if metadata.type == "Location" then
|
||||
locations[card.getGUID()] = metadata
|
||||
rebuildConnectionList()
|
||||
drawConnections()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Stop tracking a location for connection drawing. This should be called for both collision exit
|
||||
-- and destruction, as a destroyed object does not trigger collision exit. An object can also be
|
||||
-- deleted mid-drag, but the ordering for drag events means we can't clear those here and those will
|
||||
-- be cleared in the next onUpdate() cycle.
|
||||
-- @param card Card to (maybe) stop tracking
|
||||
function maybeUntrackLocation(card)
|
||||
-- Locked objects no longer collide (hence triggering an exit event) but are still in the play
|
||||
-- area. If the object is now locked, don't remove it.
|
||||
if locations[card.getGUID()] ~= nil and not card.locked then
|
||||
locations[card.getGUID()] = nil
|
||||
rebuildConnectionList()
|
||||
drawConnections()
|
||||
end
|
||||
end
|
||||
|
||||
-- Builds a list of GUID to GUID connection information based on the currently tracked locations.
|
||||
-- This will update the connection information and store it in the locationConnections data member,
|
||||
-- but does not draw those connections. This should often be followed by a call to
|
||||
-- drawConnections()
|
||||
function rebuildConnectionList()
|
||||
if not showLocationLinks() then
|
||||
locationConnections = { }
|
||||
return
|
||||
end
|
||||
|
||||
local iconCardList = { }
|
||||
|
||||
-- Build a list of cards with each icon as their location ID
|
||||
for cardId, metadata in pairs(draggingGuids) do
|
||||
buildLocListByIcon(cardId, iconCardList)
|
||||
end
|
||||
for cardId, metadata in pairs(locations) do
|
||||
buildLocListByIcon(cardId, iconCardList)
|
||||
end
|
||||
|
||||
-- Pair up all the icons
|
||||
locationConnections = { }
|
||||
for cardId, metadata in pairs(draggingGuids) do
|
||||
buildConnection(cardId, iconCardList)
|
||||
end
|
||||
for cardId, metadata in pairs(locations) do
|
||||
if draggingGuids[cardId] == nil then
|
||||
buildConnection(cardId, iconCardList)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Extracts the card's icon string into a list of individual location icons
|
||||
-- @param cardID GUID of the card to pull the icon data from
|
||||
-- @param iconCardList A table of icon->GUID list. Mutable, will be updated by this method
|
||||
function buildLocListByIcon(cardId, iconCardList)
|
||||
local card = getObjectFromGUID(cardId)
|
||||
local locData = getLocationData(card)
|
||||
if locData ~= nil and locData.icons ~= nil then
|
||||
for icon in string.gmatch(locData.icons, "%a+") do
|
||||
if iconCardList[icon] == nil then
|
||||
iconCardList[icon] = { }
|
||||
end
|
||||
table.insert(iconCardList[icon], card.getGUID())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Builds the connections for the given cardID by finding matching icons and adding them to the
|
||||
-- Playarea's locationConnections table.
|
||||
-- @param cardId GUID of the card to build the connections for
|
||||
-- @param iconCardList A table of icon->GUID List. Used to find matching icons for connections.
|
||||
function buildConnection(cardId, iconCardList)
|
||||
local card = getObjectFromGUID(cardId)
|
||||
local locData = getLocationData(card)
|
||||
if locData ~= nil and locData.connections ~= nil then
|
||||
locationConnections[card.getGUID()] = { }
|
||||
for icon in string.gmatch(locData.connections, "%a+") do
|
||||
if iconCardList[icon] ~= nil then
|
||||
for _, connectedGuid in ipairs(iconCardList[icon]) do
|
||||
-- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way
|
||||
if locationConnections[connectedGuid] ~= nil
|
||||
and locationConnections[connectedGuid][card.getGUID()] ~= nil then
|
||||
locationConnections[connectedGuid][card.getGUID()] = BIDIRECTIONAL
|
||||
else
|
||||
locationConnections[card.getGUID()][connectedGuid] = ONE_WAY
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper method to extract the location metadata from a card based on whether it's front or back
|
||||
-- is showing.
|
||||
-- @param card Card object to extract data from
|
||||
-- @return Table with either the locationFront or locationBack metadata structure, or nil if the
|
||||
-- metadata doesn't exist
|
||||
function getLocationData(card)
|
||||
if card == nil then
|
||||
return nil
|
||||
end
|
||||
if card.is_face_down then
|
||||
return JSON.decode(card.getGMNotes()).locationBack
|
||||
else
|
||||
return JSON.decode(card.getGMNotes()).locationFront
|
||||
end
|
||||
end
|
||||
|
||||
-- Draws the lines for connections currently in locationConnections.
|
||||
function drawConnections()
|
||||
if not showLocationLinks() then
|
||||
locationConnections = { }
|
||||
return
|
||||
end
|
||||
local cardConnectionLines = { }
|
||||
|
||||
for originGuid, targetGuids in pairs(locationConnections) do
|
||||
-- Objects should reliably exist at this point, but since this can be called during onUpdate the
|
||||
-- object checks are conservative just to make sure.
|
||||
local origin = getObjectFromGUID(originGuid)
|
||||
if origin != nil then
|
||||
for targetGuid, direction in pairs(targetGuids) do
|
||||
local target = getObjectFromGUID(targetGuid)
|
||||
if target != nil then
|
||||
if direction == BIDIRECTIONAL then
|
||||
addBidirectionalVector(origin, target, cardConnectionLines)
|
||||
elseif direction == ONE_WAY then
|
||||
addOneWayVector(origin, target, cardConnectionLines)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.setVectorLines(cardConnectionLines)
|
||||
end
|
||||
|
||||
-- Draws a bidirectional location connection between the two cards, adding the lines to do so to the
|
||||
-- given lines list.
|
||||
-- @param card1 One of the card objects to connect
|
||||
-- @param card2 The other card object to connect
|
||||
-- @param lines List of vector line elements. Mutable, will be updated to add this connector
|
||||
function addBidirectionalVector(card1, card2, lines)
|
||||
local cardPos1 = card1.getPosition()
|
||||
local cardPos2 = card2.getPosition()
|
||||
cardPos1.y = CONNECTION_LINE_Y
|
||||
cardPos2.y = CONNECTION_LINE_Y
|
||||
local pos1 = self.positionToLocal(cardPos1)
|
||||
local pos2 = self.positionToLocal(cardPos2)
|
||||
table.insert(lines, {
|
||||
points = { pos1, pos2 },
|
||||
color = CONNECTION_COLOR,
|
||||
thickness = CONNECTION_THICKNESS,
|
||||
})
|
||||
end
|
||||
|
||||
-- Draws a one-way location connection between the two cards, adding the lines to do so to the
|
||||
-- given lines list. Arrows will point towards the target card.
|
||||
-- @param origin Origin card in the connection
|
||||
-- @param target Target card object to connect
|
||||
-- @param lines List of vector line elements. Mutable, will be updated to add this connector
|
||||
function addOneWayVector(origin, target, lines)
|
||||
-- Start with the BiDi then add the arrow lines to it
|
||||
addBidirectionalVector(origin, target, lines)
|
||||
local originPos = origin.getPosition()
|
||||
local targetPos = target.getPosition()
|
||||
originPos.y = CONNECTION_LINE_Y
|
||||
targetPos.y = CONNECTION_LINE_Y
|
||||
|
||||
-- Calculate card distance to be closer for horizontal positions than vertical, since cards are
|
||||
-- taller than they are wide
|
||||
local heading = Vector(originPos):sub(targetPos):heading("y")
|
||||
local distanceFromCard = DIRECTIONAL_ARROW_DISTANCE * 0.7 + DIRECTIONAL_ARROW_DISTANCE * 0.3 * math.abs(math.sin(math.rad(heading)))
|
||||
|
||||
-- Calculate the three possible arrow positions. These are offset by half the arrow length to
|
||||
-- make them visually balanced by keeping the arrows centered, not tracking the point
|
||||
local midpoint = Vector(originPos):add(targetPos):scale(Vector(0.5, 0.5, 0.5)):moveTowards(targetPos, ARROW_ARM_LENGTH / 2)
|
||||
local closeToOrigin = Vector(originPos):moveTowards(targetPos, distanceFromCard + ARROW_ARM_LENGTH / 2)
|
||||
local closeToTarget = Vector(targetPos):moveTowards(originPos, distanceFromCard - ARROW_ARM_LENGTH / 2)
|
||||
|
||||
if (originPos:distance(closeToOrigin) > originPos:distance(closeToTarget)) then
|
||||
addArrowLines(midpoint, originPos, lines)
|
||||
else
|
||||
addArrowLines(closeToOrigin, originPos, lines)
|
||||
addArrowLines(closeToTarget, originPos, lines)
|
||||
end
|
||||
end
|
||||
|
||||
-- Draws an arrowhead at the given position.
|
||||
-- @param arrowheadPosition Centerpoint of the arrowhead to draw (NOT the tip of the arrow)
|
||||
-- @param originPos Origin point of the connection, used to position the arrow arms
|
||||
-- @param lines List of vector line elements. Mutable, will be updated to add this arrow
|
||||
function addArrowLines(arrowheadPos, originPos, lines)
|
||||
local arrowArm1 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", -1 * ARROW_ANGLE):add(arrowheadPos)
|
||||
local arrowArm2 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", ARROW_ANGLE):add(arrowheadPos)
|
||||
|
||||
local head = self.positionToLocal(arrowheadPos)
|
||||
local arm1 = self.positionToLocal(arrowArm1)
|
||||
local arm2 = self.positionToLocal(arrowArm2)
|
||||
table.insert(lines, {
|
||||
points = { arm1, head, arm2},
|
||||
color = CONNECTION_COLOR,
|
||||
thickness = CONNECTION_THICKNESS
|
||||
})
|
||||
end
|
||||
|
||||
-- Move all contents on the play area (cards, tokens, etc) one slot in the given direction. Certain
|
||||
-- fixed objects will be ignored, as will anything the player has tagged with 'displacement_excluded'
|
||||
---@param playerColor String Color of the player requesting the shift. Used solely to send an error
|
||||
@ -126,6 +420,20 @@ function getInvestigatorCount()
|
||||
return investigatorCounter.getVar("val")
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- @param object Object to check
|
||||
-- @return True if the object is inside the play area
|
||||
function isInPlayArea(object)
|
||||
local bounds = self.getBounds()
|
||||
local position = object.getPosition()
|
||||
-- Corners are arbitrary since it's all global - c1 goes down both axes, c2 goes up
|
||||
local c1 = { x = bounds.center.x - bounds.size.x / 2, z = bounds.center.z - bounds.size.z / 2}
|
||||
local c2 = { x = bounds.center.x + bounds.size.x / 2, z = bounds.center.z + bounds.size.z / 2}
|
||||
|
||||
return position.x > c1.x and position.x < c2.x and position.z > c1.z and position.z < c2.z
|
||||
end
|
||||
|
||||
-- Reset the play area's tracking of which cards have had tokens spawned.
|
||||
function resetSpawnedCards()
|
||||
spawnedLocationGUIDs = {}
|
||||
@ -133,4 +441,11 @@ end
|
||||
|
||||
function onScenarioChanged(scenarioName)
|
||||
currentScenario = scenarioName
|
||||
if not showLocationLinks() then
|
||||
broadcastToAll("Automatic location connections not available for this scenario")
|
||||
end
|
||||
end
|
||||
|
||||
function showLocationLinks()
|
||||
return not LOC_LINK_EXCLUDE_SCENARIOS[currentScenario]
|
||||
end
|
||||
|
@ -149,8 +149,12 @@ do
|
||||
-- spawned state object rather than spawning multiple tokens
|
||||
---@param shiftDown An offset for the z-value of this group of tokens
|
||||
TokenManager.spawnTokenGroup = function(card, tokenType, tokenCount, shiftDown)
|
||||
local optionPanel = Global.getTable("optionPanel")
|
||||
|
||||
if tokenType == "damage" or tokenType == "horror" then
|
||||
TokenManager.spawnCounterToken(card, tokenType, tokenCount, shiftDown)
|
||||
elseif tokenType == "resource" and optionPanel["useResourceCounters"] then
|
||||
TokenManager.spawnResourceCounterToken(card, tokenCount)
|
||||
else
|
||||
TokenManager.spawnMultipleTokens(card, tokenType, tokenCount, shiftDown)
|
||||
end
|
||||
@ -184,6 +188,14 @@ do
|
||||
end)
|
||||
end
|
||||
|
||||
TokenManager.spawnResourceCounterToken = function(card, tokenCount)
|
||||
local pos = card.positionToWorld(card.positionToLocal(card.getPosition()) + Vector(0, 0.2, -0.5))
|
||||
local rot = card.getRotation()
|
||||
TokenManager.spawnToken(pos, "resourceCounter", rot, function(spawned)
|
||||
spawned.call("updateVal", tokenCount)
|
||||
end)
|
||||
end
|
||||
|
||||
-- Spawns a number of tokens.
|
||||
---@param tokenType String type of token to spawn, valid values are resource", "doom", or "clue".
|
||||
-- Other types should use spawnCounterToken()
|
||||
@ -248,6 +260,8 @@ do
|
||||
else
|
||||
rot.y = 270
|
||||
end
|
||||
|
||||
tokenTemplate.Nickname = ""
|
||||
return spawnObjectData({
|
||||
data = tokenTemplate,
|
||||
position = position,
|
||||
|
@ -7,6 +7,8 @@ local WEAKNESS_CHECK_Z = 37
|
||||
local cardIdIndex = { }
|
||||
local classAndLevelIndex = { }
|
||||
local basicWeaknessList = { }
|
||||
local uniqueWeaknessList = { }
|
||||
local cycleIndex = { }
|
||||
|
||||
local indexingDone = false
|
||||
local allowRemoval = false
|
||||
@ -45,7 +47,9 @@ function clearIndexes()
|
||||
classAndLevelIndex["Survivor-level0"] = { }
|
||||
classAndLevelIndex["Rogue-level0"] = { }
|
||||
classAndLevelIndex["Neutral-level0"] = { }
|
||||
cycleIndex = { }
|
||||
basicWeaknessList = { }
|
||||
uniqueWeaknessList = { }
|
||||
end
|
||||
|
||||
-- Clears the bag indexes and starts the coroutine to rebuild the indexes
|
||||
@ -121,27 +125,27 @@ function buildSupplementalIndexes()
|
||||
local cardMetadata = card.metadata
|
||||
-- If the ID key and the metadata ID don't match this is a duplicate card created by an
|
||||
-- alternate_id, and we should skip it
|
||||
if (cardId == cardMetadata.id) then
|
||||
if cardId == cardMetadata.id then
|
||||
-- Add card to the basic weakness list, if appropriate. Some weaknesses have
|
||||
-- multiple copies, and are added multiple times
|
||||
if (cardMetadata.weakness and cardMetadata.basicWeaknessCount ~= nil) then
|
||||
if cardMetadata.weakness and cardMetadata.basicWeaknessCount ~= nil then
|
||||
table.insert(uniqueWeaknessList, cardMetadata.id)
|
||||
for i = 1, cardMetadata.basicWeaknessCount do
|
||||
table.insert(basicWeaknessList, cardMetadata.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(basicWeaknessList, cardComparator)
|
||||
|
||||
-- Add the card to the appropriate class and level indexes
|
||||
local isGuardian = false
|
||||
local isSeeker = false
|
||||
local isMystic = false
|
||||
local isRogue = false
|
||||
local isSurvivor = false
|
||||
local isNeutral = false
|
||||
local upgradeKey
|
||||
-- Excludes signature cards (which have no class or level) and alternate
|
||||
-- ID entries
|
||||
if (cardMetadata.class ~= nil and cardMetadata.level ~= nil) then
|
||||
-- Add the card to the appropriate class and level indexes
|
||||
local isGuardian = false
|
||||
local isSeeker = false
|
||||
local isMystic = false
|
||||
local isRogue = false
|
||||
local isSurvivor = false
|
||||
local isNeutral = false
|
||||
local upgradeKey
|
||||
-- Excludes signature cards (which have no class or level) and alternate
|
||||
-- ID entries
|
||||
if (cardMetadata.class ~= nil and cardMetadata.level ~= nil) then
|
||||
isGuardian = string.match(cardMetadata.class, "Guardian")
|
||||
isSeeker = string.match(cardMetadata.class, "Seeker")
|
||||
isMystic = string.match(cardMetadata.class, "Mystic")
|
||||
@ -171,12 +175,32 @@ function buildSupplementalIndexes()
|
||||
if (isNeutral) then
|
||||
table.insert(classAndLevelIndex["Neutral"..upgradeKey], cardMetadata.id)
|
||||
end
|
||||
|
||||
local cycleName = cardMetadata.cycle
|
||||
if cycleName ~= nil then
|
||||
cycleName = string.lower(cycleName)
|
||||
if string.match(cycleName, "return") then
|
||||
cycleName = string.sub(cycleName, 11)
|
||||
end
|
||||
if cycleName == "the night of the zealot" then
|
||||
cycleName = "core"
|
||||
end
|
||||
if cycleIndex[cycleName] == nil then
|
||||
cycleIndex[cycleName] = { }
|
||||
end
|
||||
table.insert(cycleIndex[cycleName], cardMetadata.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, indexTable in pairs(classAndLevelIndex) do
|
||||
table.sort(indexTable, cardComparator)
|
||||
end
|
||||
for _, indexTable in pairs(cycleIndex) do
|
||||
table.sort(indexTable)
|
||||
end
|
||||
table.sort(basicWeaknessList, cardComparator)
|
||||
table.sort(uniqueWeaknessList, cardComparator)
|
||||
end
|
||||
|
||||
-- Comparison function used to sort the class card bag indexes. Sorts by card
|
||||
@ -235,6 +259,14 @@ function getCardsByClassAndLevel(params)
|
||||
return classAndLevelIndex[params.class..upgradeKey];
|
||||
end
|
||||
|
||||
function getCardsByCycle(cycleName)
|
||||
if (not indexingDone) then
|
||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
||||
return { }
|
||||
end
|
||||
return cycleIndex[string.lower(cycleName)]
|
||||
end
|
||||
|
||||
-- Searches the bag for cards which match the given name and returns a list. Note that this is
|
||||
-- an O(n) search without index support. It may be slow.
|
||||
-- Parameter array must contain these fields to define the search:
|
||||
@ -303,6 +335,10 @@ function getBasicWeaknesses()
|
||||
return basicWeaknessList
|
||||
end
|
||||
|
||||
function getUniqueWeaknesses()
|
||||
return uniqueWeaknessList
|
||||
end
|
||||
|
||||
-- Helper function that adds one to the table entry for the number of weaknesses in play
|
||||
function incrementWeaknessCount(table, cardMetadata)
|
||||
if (isBasicWeakness(cardMetadata)) then
|
||||
@ -322,6 +358,7 @@ function isInPlayArea(object)
|
||||
return position.x < WEAKNESS_CHECK_X
|
||||
and position.z < WEAKNESS_CHECK_Z
|
||||
end
|
||||
|
||||
function isBasicWeakness(cardMetadata)
|
||||
return cardMetadata ~= nil
|
||||
and cardMetadata.weakness
|
||||
|
@ -2,24 +2,49 @@ require("playercards/PlayerCardPanelData")
|
||||
local spawnBag = require("playercards/spawnbag/SpawnBag")
|
||||
local arkhamDb = require("arkhamdb/ArkhamDb")
|
||||
|
||||
-- TODO: Update when the real UI image is in place
|
||||
local BUTTON_WIDTH = 150
|
||||
local BUTTON_HEIGHT = 550
|
||||
-- Size and position information for the three rows of class buttons
|
||||
local CIRCLE_BUTTON_SIZE = 250
|
||||
local CLASS_BUTTONS_X_OFFSET = 0.1325
|
||||
local INVESTIGATOR_ROW_START = Vector(0.125, 0.1, -0.447)
|
||||
local LEVEL_ZERO_ROW_START = Vector(0.125, 0.1, -0.007)
|
||||
local UPGRADED_ROW_START = Vector(0.125, 0.1, 0.333)
|
||||
|
||||
-- Size and position information for the two blocks of other buttons
|
||||
local MISC_BUTTONS_X_OFFSET = 0.155
|
||||
local WEAKNESS_ROW_START = Vector(0.157, 0.1, 0.666)
|
||||
local OTHER_ROW_START = Vector(0.605, 0.1, 0.666)
|
||||
|
||||
-- Size and position information for the Cycle (box) buttons
|
||||
local CYCLE_BUTTON_SIZE = 468
|
||||
local CYCLE_BUTTON_START = Vector(-0.716, 0.1, -0.39)
|
||||
local CYCLE_COLUMN_COUNT = 3
|
||||
local CYCLE_BUTTONS_X_OFFSET = 0.267
|
||||
local CYCLE_BUTTONS_Z_OFFSET = 0.2665
|
||||
|
||||
local ALL_CARDS_BAG_GUID = "15bb07"
|
||||
|
||||
local STARTER_DECK_MODE_SELECTED_COLOR = { 0.2, 0.2, 0.2, 0.8 }
|
||||
local TRANSPARENT = { 0, 0, 0, 0 }
|
||||
local STARTER_DECK_MODE_STARTERS = "starters"
|
||||
local STARTER_DECK_MODE_CARDS_ONLY = "cards"
|
||||
|
||||
local FACE_UP_ROTATION = { x = 0, y = 270, z = 0}
|
||||
local FACE_DOWN_ROTATION = { x = 0, y = 270, z = 180}
|
||||
|
||||
-- Coordinates to begin laying out cards to match the reserved areas of the
|
||||
-- table. Cards will lay out horizontally, then create additional rows
|
||||
-- Coordinates to begin laying out cards. These vary based on the cards that are being placed
|
||||
local START_POSITIONS = {
|
||||
skill = Vector(58.384, 1.36, 92.4),
|
||||
event = Vector(53.229, 1.36, 92.4),
|
||||
asset = Vector(40.960, 1.36, 92.4),
|
||||
investigator = Vector(60, 1.36, 80)
|
||||
classCards = Vector(58.384, 1.36, 92.4),
|
||||
investigator = Vector(60, 1.36, 86),
|
||||
cycle = Vector(48, 1.36, 92.4),
|
||||
other = Vector(56, 1.36, 86),
|
||||
summonedServitor = Vector(55.5, 1.36, 60.2),
|
||||
randomWeakness = Vector(55, 1.36, 75)
|
||||
}
|
||||
|
||||
-- Shifts to move rows of cards, and groups of rows, as different groupings are laid out
|
||||
local CARD_ROW_OFFSET = 3.7
|
||||
local CARD_GROUP_OFFSET = 2
|
||||
|
||||
-- Position offsets for investigator decks in investigator mode, defines the spacing for how the
|
||||
-- rows and columns are laid out
|
||||
local INVESTIGATOR_POSITION_SHIFT_ROW = Vector(-11, 0, 0)
|
||||
@ -31,7 +56,21 @@ local INVESTIGATOR_MAX_COLS = 6
|
||||
local INVESTIGATOR_CARD_OFFSET = Vector(-2.55, 0, 0)
|
||||
local INVESTIGATOR_SIGNATURE_OFFSET = Vector(-5.75, 0, 0)
|
||||
|
||||
local spawnStarterDecks = false
|
||||
local CLASS_LIST = { "Guardian", "Seeker", "Rogue", "Mystic", "Survivor", "Neutral" }
|
||||
local CYCLE_LIST = {
|
||||
"Core",
|
||||
"The Dunwich Legacy",
|
||||
"The Path to Carcosa",
|
||||
"The Forgotten Age",
|
||||
"The Circle Undone",
|
||||
"The Dream-Eaters",
|
||||
"The Innsmouth Conspiracy",
|
||||
"Edge of the Earth",
|
||||
"The Scarlet Keys",
|
||||
"Investigator Packs"
|
||||
}
|
||||
|
||||
local starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY
|
||||
|
||||
function onSave()
|
||||
local saveState = {
|
||||
@ -48,232 +87,223 @@ function onLoad(savedData)
|
||||
spawnBag.loadFromSave(saveState.spawnBagState)
|
||||
end
|
||||
end
|
||||
createButtons()
|
||||
end
|
||||
|
||||
self.createButton({
|
||||
label="Guardian", click_function="spawnInvestigatorsGuardian", function_owner=self,
|
||||
position={-0.3,0.2,-0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Seeker", click_function="spawnInvestigatorsSeeker", function_owner=self,
|
||||
position={0,0.2,-0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Mystic", click_function="spawnInvestigatorsMystic", function_owner=self,
|
||||
position={0.3,0.2,-0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Rogue", click_function="spawnInvestigatorsRogue", function_owner=self,
|
||||
position={-0.3,0.2,-0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Survivor", click_function="spawnSurvivor", function_owner=self,
|
||||
position={0,0.2,-0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Neutral", click_function="spawnNeutral", function_owner=self,
|
||||
position={0.3,0.2,-0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
function createButtons()
|
||||
createInvestigatorButtons()
|
||||
createLevelZeroButtons()
|
||||
createUpgradedButtons()
|
||||
createWeaknessButtons()
|
||||
createOtherButtons()
|
||||
createCycleButtons()
|
||||
createClearButton()
|
||||
-- Create investigator mode buttons last so the indexes are set when we need to update them
|
||||
createInvestigatorModeButtons()
|
||||
end
|
||||
|
||||
self.createButton({
|
||||
label="Core", click_function="spawnCore", function_owner=self,
|
||||
position={-0.3,0.2,-0.2}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Dunwich", click_function="spawnDunwich", function_owner=self,
|
||||
position={0,0.2,-0.2}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Carcosa", click_function="spawnCarcosa", function_owner=self,
|
||||
position={0.3,0.2,-0.2}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Forgotten Age", click_function="spawnForgottenAge", function_owner=self,
|
||||
position={-0.3,0.2,-0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Circle Undone", click_function="spawnCircleUndone", function_owner=self,
|
||||
position={0,0.2,-0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Dream Eaters", click_function="spawnDreamEaters", function_owner=self,
|
||||
position={0.3,0.2,-0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Innsmouth", click_function="spawnInnsmouth", function_owner=self,
|
||||
position={-0.3,0.2,0}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="EotE", click_function="spawnEotE", function_owner=self,
|
||||
position={0,0.2,0}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Scarlet Keys", click_function="spawnScarletKeys", function_owner=self,
|
||||
position={0.3,0.2,0}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="InvPacks", click_function="spawnInvestigatorDecks", function_owner=self,
|
||||
position={-0.3,0.2,0.1}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Investigators", click_function="setInvestigators", function_owner=self,
|
||||
position={-0.15,0.2,-0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Starters", click_function="setStarters", function_owner=self,
|
||||
position={0.15,0.2,-0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
|
||||
self.createButton({
|
||||
label="L0 Guardian", click_function="spawnBasicGuardian", function_owner=self,
|
||||
position={-0.15,0.2,0.3}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Guardian", click_function="spawnUpgradedGuardian", function_owner=self,
|
||||
position={0.15,0.2,0.3}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Seeker", click_function="spawnBasicSeeker", function_owner=self,
|
||||
position={-0.15,0.2,0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Seeker", click_function="spawnUpgradedSeeker", function_owner=self,
|
||||
position={0.15,0.2,0.4}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Mystic", click_function="spawnBasicMystic", function_owner=self,
|
||||
position={-0.15,0.2,0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Mystic", click_function="spawnUpgradedGuardian", function_owner=self,
|
||||
position={0.15,0.2,0.5}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Rogue", click_function="spawnBasicRogue", function_owner=self,
|
||||
position={-0.15,0.2,0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Rogue", click_function="spawnUpgradedRogue", function_owner=self,
|
||||
position={0.15,0.2,0.6}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Survivor", click_function="spawnBasicSurvivor", function_owner=self,
|
||||
position={-0.15,0.2,0.7}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Survivor", click_function="spawnUpgradedSurvivor", function_owner=self,
|
||||
position={0.15,0.2,0.7}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L0 Neutral", click_function="spawnBasicNeutral", function_owner=self,
|
||||
position={-0.15,0.2,0.8}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="L1-5 Neutral", click_function="spawnUpgradedNeutral", function_owner=self,
|
||||
position={0.15,0.2,0.8}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Clear", click_function="deleteAll", function_owner=self,
|
||||
position={0.5,0.2,0.9}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
self.createButton({
|
||||
label="Weaknesses", click_function="spawnWeaknesses", function_owner=self,
|
||||
position={-0.5,0.2,0.9}, rotation={0,0,0}, height=BUTTON_WIDTH, width=BUTTON_HEIGHT,
|
||||
font_size=64, color={0,0,0}, font_color={1,1,1}, scale={0.25, 0.25, 0.25}
|
||||
})
|
||||
local classList = { "Guardian", "Seeker", "Mystic", "Rogue", "Survivor", "Neutral" }
|
||||
for _, className in ipairs(classList) do
|
||||
local funcName = "spawnInvestigators"..className
|
||||
self.setVar(funcName, function(_, _, _) spawnGroup(className) end)
|
||||
funcName = "spawnBasic"..className
|
||||
self.setVar(funcName, function(_, _, _) spawnClassCards(className, false) end)
|
||||
funcName = "spawnUpgraded"..className
|
||||
self.setVar(funcName, function(_, _, _) spawnClassCards(className, true) end)
|
||||
function createInvestigatorButtons()
|
||||
local invButtonParams = {
|
||||
function_owner = self,
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = CIRCLE_BUTTON_SIZE,
|
||||
width = CIRCLE_BUTTON_SIZE,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
}
|
||||
local buttonPos = INVESTIGATOR_ROW_START:copy()
|
||||
for _, class in ipairs(CLASS_LIST) do
|
||||
invButtonParams.click_function = "spawnInvestigators" .. class
|
||||
invButtonParams.position = buttonPos
|
||||
self.createButton(invButtonParams)
|
||||
buttonPos.x = buttonPos.x + CLASS_BUTTONS_X_OFFSET
|
||||
self.setVar(invButtonParams.click_function, function(_, _, _) spawnInvestigatorGroup(class) end)
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Replace these with something less manual once the full data in in place so we know what
|
||||
-- keys to use
|
||||
function placeCore()
|
||||
spawnGroup("Core")
|
||||
function createLevelZeroButtons()
|
||||
local l0ButtonParams = {
|
||||
function_owner = self,
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = CIRCLE_BUTTON_SIZE,
|
||||
width = CIRCLE_BUTTON_SIZE,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
}
|
||||
local buttonPos = LEVEL_ZERO_ROW_START:copy()
|
||||
for _, class in ipairs(CLASS_LIST) do
|
||||
l0ButtonParams.click_function = "spawnBasic" .. class
|
||||
l0ButtonParams.position = buttonPos
|
||||
self.createButton(l0ButtonParams)
|
||||
buttonPos.x = buttonPos.x + CLASS_BUTTONS_X_OFFSET
|
||||
self.setVar(l0ButtonParams.click_function, function(_, _, _) spawnClassCards(class, false) end)
|
||||
end
|
||||
end
|
||||
|
||||
function placeDunwich()
|
||||
spawnGroup("Dunwich")
|
||||
function createUpgradedButtons()
|
||||
local upgradedButtonParams = {
|
||||
function_owner = self,
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = CIRCLE_BUTTON_SIZE,
|
||||
width = CIRCLE_BUTTON_SIZE,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
}
|
||||
local buttonPos = UPGRADED_ROW_START:copy()
|
||||
for _, class in ipairs(CLASS_LIST) do
|
||||
upgradedButtonParams.click_function = "spawnUpgraded" .. class
|
||||
upgradedButtonParams.position = buttonPos
|
||||
self.createButton(upgradedButtonParams)
|
||||
buttonPos.x = buttonPos.x + CLASS_BUTTONS_X_OFFSET
|
||||
self.setVar(upgradedButtonParams.click_function, function(_, _, _) spawnClassCards(class, true) end)
|
||||
end
|
||||
end
|
||||
|
||||
function placeCarcosa()
|
||||
spawnGroup("Carcosa")
|
||||
function createWeaknessButtons()
|
||||
local weaknessButtonParams = {
|
||||
function_owner = self,
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = CIRCLE_BUTTON_SIZE,
|
||||
width = CIRCLE_BUTTON_SIZE,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
}
|
||||
local buttonPos = WEAKNESS_ROW_START:copy()
|
||||
weaknessButtonParams.click_function = "spawnWeaknesses"
|
||||
weaknessButtonParams.tooltip = "Basic Weaknesses"
|
||||
weaknessButtonParams.position = buttonPos
|
||||
self.createButton(weaknessButtonParams)
|
||||
buttonPos.x = buttonPos.x + MISC_BUTTONS_X_OFFSET
|
||||
weaknessButtonParams.click_function = "spawnRandomWeakness"
|
||||
weaknessButtonParams.tooltip = "Random Weakness"
|
||||
weaknessButtonParams.position = buttonPos
|
||||
self.createButton(weaknessButtonParams)
|
||||
end
|
||||
|
||||
function placeForgottenAge()
|
||||
spawnGroup("ForgottenAge")
|
||||
function createOtherButtons()
|
||||
local otherButtonParams = {
|
||||
function_owner = self,
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = CIRCLE_BUTTON_SIZE,
|
||||
width = CIRCLE_BUTTON_SIZE,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
}
|
||||
local buttonPos = OTHER_ROW_START:copy()
|
||||
otherButtonParams.click_function = "spawnBonded"
|
||||
otherButtonParams.tooltip = "Bonded Cards"
|
||||
otherButtonParams.position = buttonPos
|
||||
self.createButton(otherButtonParams)
|
||||
buttonPos.x = buttonPos.x + MISC_BUTTONS_X_OFFSET
|
||||
otherButtonParams.click_function = "spawnUpgradeSheets"
|
||||
otherButtonParams.tooltip = "Customization Upgrade Sheets"
|
||||
otherButtonParams.position = buttonPos
|
||||
self.createButton(otherButtonParams)
|
||||
end
|
||||
|
||||
function placeCircleUndone()
|
||||
spawnGroup("CircleUndone")
|
||||
function createCycleButtons()
|
||||
local cycleButtonParams = {
|
||||
function_owner = self,
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = CYCLE_BUTTON_SIZE,
|
||||
width = CYCLE_BUTTON_SIZE,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
}
|
||||
local buttonPos = CYCLE_BUTTON_START:copy()
|
||||
local rowCount = 0
|
||||
local colCount = 0
|
||||
for _, cycle in ipairs(CYCLE_LIST) do
|
||||
cycleButtonParams.click_function = "spawnCycle" .. cycle
|
||||
cycleButtonParams.position = buttonPos
|
||||
cycleButtonParams.tooltip = cycle
|
||||
self.createButton(cycleButtonParams)
|
||||
self.setVar(cycleButtonParams.click_function, function(_, _, _) spawnCycle(cycle) end)
|
||||
colCount = colCount + 1
|
||||
-- If we've reached the end of a row, shift down and back to the first column
|
||||
if colCount >= CYCLE_COLUMN_COUNT then
|
||||
buttonPos = CYCLE_BUTTON_START:copy()
|
||||
rowCount = rowCount + 1
|
||||
colCount = 0
|
||||
buttonPos.z = buttonPos.z + CYCLE_BUTTONS_Z_OFFSET * rowCount
|
||||
if rowCount == 3 then
|
||||
-- Account for centered button on the final row
|
||||
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
|
||||
end
|
||||
else
|
||||
buttonPos.x = buttonPos.x + CYCLE_BUTTONS_X_OFFSET
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function placeDreamEaters()
|
||||
spawnGroup("DreamEaters")
|
||||
function createClearButton()
|
||||
self.createButton({
|
||||
function_owner = self,
|
||||
click_function = "deleteAll",
|
||||
position = Vector(0, 0.1, 0.852),
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = 170,
|
||||
width = 750,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = TRANSPARENT,
|
||||
})
|
||||
end
|
||||
|
||||
function placeInnsmouth()
|
||||
spawnGroup("Innsmouth")
|
||||
function createInvestigatorModeButtons()
|
||||
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
|
||||
|
||||
self.createButton({
|
||||
function_owner = self,
|
||||
click_function = "setCardsOnlyMode",
|
||||
position = Vector(0.251, 0.1, -0.322),
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = 170,
|
||||
width = 760,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = starterMode and TRANSPARENT or STARTER_DECK_MODE_SELECTED_COLOR
|
||||
})
|
||||
self.createButton({
|
||||
function_owner = self,
|
||||
click_function = "setStarterDeckMode",
|
||||
position = Vector(0.66, 0.1, -0.322),
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = 170,
|
||||
width = 760,
|
||||
scale = Vector(0.25, 1, 0.25),
|
||||
color = starterMode and STARTER_DECK_MODE_SELECTED_COLOR or TRANSPARENT
|
||||
})
|
||||
local checkX = starterMode and 0.52 or 0.11
|
||||
self.createButton({
|
||||
function_owner = self,
|
||||
label = "✓",
|
||||
click_function = "doNothing",
|
||||
position = Vector(checkX, 0.11, -0.317),
|
||||
rotation = Vector(0, 0, 0),
|
||||
height = 0,
|
||||
width = 0,
|
||||
scale = Vector(0.3, 1, 0.3),
|
||||
font_color = { 0, 0, 0 },
|
||||
color = { 1, 1, 1 }
|
||||
})
|
||||
end
|
||||
|
||||
function placeEotE()
|
||||
spawnGroup("EotE")
|
||||
function setStarterDeckMode()
|
||||
starterDeckMode = STARTER_DECK_MODE_STARTERS
|
||||
updateStarterModeButtons()
|
||||
end
|
||||
|
||||
function placeScarletKeys()
|
||||
spawnGroup("ScarletKeys")
|
||||
function setCardsOnlyMode()
|
||||
starterDeckMode = STARTER_DECK_MODE_CARDS_ONLY
|
||||
updateStarterModeButtons()
|
||||
end
|
||||
|
||||
function placeInvestigatorDecks()
|
||||
spawnGroup("InvestigatorDecks")
|
||||
end
|
||||
|
||||
-- UI handler to put the investigator spawn in investigator mode.
|
||||
function setInvestigators()
|
||||
spawnStarterDecks = false
|
||||
printToAll("Spawning investigator piles")
|
||||
end
|
||||
|
||||
-- UI handler to put the investigator spawn in starter deck mode.
|
||||
function setStarters()
|
||||
spawnStarterDecks = true
|
||||
printToAll("Spawning starter decks")
|
||||
function updateStarterModeButtons()
|
||||
local buttonCount = #self.getButtons()
|
||||
-- Buttons are 0-indexed, so the last three are -1, -2, and -3 from the size
|
||||
self.removeButton(buttonCount - 1)
|
||||
self.removeButton(buttonCount - 2)
|
||||
self.removeButton(buttonCount - 3)
|
||||
createInvestigatorModeButtons()
|
||||
end
|
||||
|
||||
-- Deletes all cards currently placed on the table
|
||||
@ -284,10 +314,11 @@ end
|
||||
-- Spawn an investigator group, based on the current UI setting for either investigators or starter
|
||||
-- decks.
|
||||
---@param groupName String. Name of the group to spawn, matching a key in InvestigatorPanelData
|
||||
function spawnGroup(groupName)
|
||||
function spawnInvestigatorGroup(groupName)
|
||||
local starterMode = starterDeckMode == STARTER_DECK_MODE_STARTERS
|
||||
spawnBag.recall(true)
|
||||
Wait.frames(function()
|
||||
if spawnStarterDecks then
|
||||
if starterMode then
|
||||
spawnStarters(groupName)
|
||||
else
|
||||
spawnInvestigators(groupName)
|
||||
@ -359,13 +390,13 @@ function buildCommonSpawnSpec(investigatorName, investigatorData, position, oneC
|
||||
return {
|
||||
{
|
||||
name = investigatorName.."minicards",
|
||||
cards = oneCardOnly and investigatorData.minicards[1] or investigatorData.minicards,
|
||||
cards = oneCardOnly and { investigatorData.minicards[1] } or investigatorData.minicards,
|
||||
globalPos = position,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
},
|
||||
{
|
||||
name = investigatorName.."cards",
|
||||
cards = oneCardOnly and investigatorData.cards[1] or investigatorData.cards,
|
||||
cards = oneCardOnly and { investigatorData.cards[1] } or investigatorData.cards,
|
||||
globalPos = cardPos,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
},
|
||||
@ -452,31 +483,34 @@ function placeClassCards(cardClass, isUpgraded)
|
||||
table.insert(assetList, cardId)
|
||||
end
|
||||
end
|
||||
local groupPos = Vector(START_POSITIONS.classCards)
|
||||
if #skillList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = skillList,
|
||||
globalPos = START_POSITIONS.skill,
|
||||
globalPos = groupPos,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#skillList / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
end
|
||||
if #eventList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. "event" .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = eventList,
|
||||
globalPos = START_POSITIONS.event,
|
||||
globalPos = groupPos,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#eventList / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
end
|
||||
if #assetList > 0 then
|
||||
spawnBag.spawn({
|
||||
name = cardClass .. "asset" .. (isUpgraded and "upgraded" or "basic"),
|
||||
cards = assetList,
|
||||
globalPos = START_POSITIONS.asset,
|
||||
globalPos = groupPos,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
@ -484,26 +518,108 @@ function placeClassCards(cardClass, isUpgraded)
|
||||
end
|
||||
end
|
||||
|
||||
-- Clears the current cards, and places all basic weaknesses on the table.
|
||||
function spawnWeaknesses()
|
||||
spawnBag.recall(fast)
|
||||
-- Spawns the investigator sets and all cards for the given cycle
|
||||
---@param cycle String Name of a cycle, should match the standard used in card metadata
|
||||
function spawnCycle(cycle)
|
||||
spawnBag.recall(true)
|
||||
spawnInvestigators(cycle)
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
|
||||
local indexReady = allCardsBag.call("isIndexReady")
|
||||
if (not indexReady) then
|
||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
||||
return
|
||||
end
|
||||
local weaknessIdList = allCardsBag.call("getBasicWeaknesses")
|
||||
local cycleCardList = allCardsBag.call("getCardsByCycle", cycle)
|
||||
local copiedList = { }
|
||||
for i, id in ipairs(weaknessIdList) do
|
||||
for i, id in ipairs(cycleCardList) do
|
||||
copiedList[i] = id
|
||||
end
|
||||
spawnBag.spawn({
|
||||
name = "weaknesses",
|
||||
name = "cycle"..cycle,
|
||||
cards = copiedList,
|
||||
globalPos = START_POSITIONS.asset,
|
||||
globalPos = START_POSITIONS.cycle,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
||||
|
||||
function spawnBonded()
|
||||
spawnBag.recall(true)
|
||||
spawnBag.spawn({
|
||||
name = "bonded",
|
||||
cards = BONDED_CARD_LIST,
|
||||
globalPos = START_POSITIONS.classCards,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
||||
|
||||
function spawnUpgradeSheets()
|
||||
spawnBag.recall(true)
|
||||
spawnBag.spawn({
|
||||
name = "upgradeSheets",
|
||||
cards = UPGRADE_SHEET_LIST,
|
||||
globalPos = START_POSITIONS.classCards,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
spawnBag.spawn({
|
||||
name = "servitor",
|
||||
cards = { "09080-m" },
|
||||
globalPos = START_POSITIONS.summonedServitor,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
})
|
||||
end
|
||||
|
||||
-- Clears the current cards, and places all basic weaknesses on the table.
|
||||
function spawnWeaknesses()
|
||||
spawnBag.recall(true)
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
|
||||
local indexReady = allCardsBag.call("isIndexReady")
|
||||
if (not indexReady) then
|
||||
broadcastToAll("Still loading player cards, please try again in a few seconds", {0.9, 0.2, 0.2})
|
||||
return
|
||||
end
|
||||
local weaknessIdList = allCardsBag.call("getUniqueWeaknesses")
|
||||
local copiedList = { }
|
||||
for i, id in ipairs(weaknessIdList) do
|
||||
copiedList[i] = id
|
||||
end
|
||||
local groupPos = Vector(START_POSITIONS.classCards)
|
||||
spawnBag.spawn({
|
||||
name = "weaknesses",
|
||||
cards = copiedList,
|
||||
globalPos = groupPos,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
groupPos.x = groupPos.x - math.ceil(#copiedList / 20) * CARD_ROW_OFFSET - CARD_GROUP_OFFSET
|
||||
spawnBag.spawn({
|
||||
name = "evolvedWeaknesses",
|
||||
cards = EVOLVED_WEAKNESSES,
|
||||
globalPos = groupPos,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
spread = true,
|
||||
spreadCols = 20
|
||||
})
|
||||
end
|
||||
|
||||
function spawnRandomWeakness()
|
||||
spawnBag.recall(true)
|
||||
local allCardsBag = getObjectFromGUID(ALL_CARDS_BAG_GUID)
|
||||
local weaknessId = allCardsBag.call("getRandomWeaknessId")
|
||||
if (weaknessId == nil) then
|
||||
broadcastToAll("All basic weaknesses are in play!", {0.9, 0.2, 0.2})
|
||||
return
|
||||
end
|
||||
spawnBag.spawn({
|
||||
name = "randomWeakness",
|
||||
cards = { weaknessId },
|
||||
globalPos = START_POSITIONS.randomWeakness,
|
||||
rotation = FACE_UP_ROTATION,
|
||||
})
|
||||
end
|
||||
|
@ -1,3 +1,45 @@
|
||||
BONDED_CARD_LIST = {
|
||||
"05314", -- Soothing Melody
|
||||
"06277", -- Wish Eater
|
||||
"06019", -- Bloodlust
|
||||
"06022", -- Pendant of the Queen
|
||||
"05317", -- Blood-rite
|
||||
"06113", -- Essence of the Dream
|
||||
"06028", -- Stars Are Right
|
||||
"06025", -- Guardian of the Crystallizer
|
||||
"06283", -- Unbound Beast
|
||||
"06032", -- Zeal
|
||||
"06031", -- Hope
|
||||
"06033", -- Augur
|
||||
"06331", -- Dream Parasite
|
||||
"06015a", -- Dream-Gate
|
||||
}
|
||||
|
||||
UPGRADE_SHEET_LIST = {
|
||||
"09040-c", -- Alchemical Distillation
|
||||
"09023-c", -- Custom Modifications
|
||||
"09059-c", -- Damning Testimony
|
||||
"09041-c", -- Emperical Hypothesis
|
||||
"09060-c", -- Friends in Low Places
|
||||
"09101-c", -- Grizzled
|
||||
"09061-c", -- Honed Instinct
|
||||
"09021-c", -- Hunter's Armor
|
||||
"09119-c", -- Hyperphysical Shotcaster
|
||||
"09079-c", -- Living Ink
|
||||
"09100-c", -- Makeshift Trap
|
||||
"09099-c", -- Pocket Multi Tool
|
||||
"09081-c", -- Power Word
|
||||
"09022-c", -- Runic Axe
|
||||
"09080-c", -- Summoned Servitor
|
||||
"09042-c", -- Raven's Quill
|
||||
}
|
||||
|
||||
EVOLVED_WEAKNESSES = {
|
||||
"04039",
|
||||
"04041",
|
||||
"04042",
|
||||
}
|
||||
|
||||
------------------ START INVESTIGATOR DATA DEFINITION ------------------
|
||||
INVESTIGATOR_GROUPS = {
|
||||
Guardian = {
|
||||
@ -13,10 +55,6 @@ INVESTIGATOR_GROUPS = {
|
||||
"D2",
|
||||
"R3",
|
||||
"D3",
|
||||
"R4",
|
||||
"D4",
|
||||
"R5",
|
||||
"D5",
|
||||
},
|
||||
}
|
||||
|
||||
@ -25,13 +63,13 @@ INVESTIGATORS["Roland Banks"] = {
|
||||
cards = { "01001", "01001-promo", "01001-p", "01001-pf", "01001-pb", },
|
||||
minicards = { "01001-m", "01001-promo-m", },
|
||||
signatures = { "01006", "01007", "90030", "90031", },
|
||||
starterDeck = "1462",
|
||||
starterDeck = "2624931",
|
||||
}
|
||||
INVESTIGATORS["Daisy Walker"] = {
|
||||
cards = { "01002", "01002-p", "01002-pf", "01002-pb", },
|
||||
minicards = { "01002-m", },
|
||||
signatures = { "01008", "01009", "90002", "90003" },
|
||||
starterDeck = "42652",
|
||||
starterDeck = "2624938",
|
||||
}
|
||||
------------------ END INVESTIGATOR DATA DEFINITION ------------------
|
||||
INVESTIGATORS["R2"] = INVESTIGATORS["Roland Banks"]
|
||||
|
@ -409,12 +409,18 @@ function replenishTokens(card, count, replenish)
|
||||
|
||||
-- get current amount of resource tokens on the card
|
||||
local search = searchArea(cardPos, { 2.5, 0.5, 3.5 })
|
||||
local clickableResourceCounter = nil
|
||||
local foundTokens = 0
|
||||
|
||||
for _, obj in ipairs(search) do
|
||||
local obj = obj.hit_object
|
||||
if obj.getCustomObject().image == "http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then
|
||||
foundTokens = foundTokens + math.abs(obj.getQuantity())
|
||||
obj.destruct()
|
||||
elseif obj.getName() == "Resource Counter" then
|
||||
foundTokens = obj.getVar("val")
|
||||
clickableResourceCounter = obj
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
@ -434,7 +440,12 @@ function replenishTokens(card, count, replenish)
|
||||
|
||||
local newCount = foundTokens + replenish
|
||||
if newCount > count then newCount = count end
|
||||
tokenManager.spawnTokenGroup(card, "resource", newCount)
|
||||
|
||||
if clickableResourceCounter then
|
||||
clickableResourceCounter.call("updateVal", newCount)
|
||||
else
|
||||
tokenManager.spawnTokenGroup(card, "resource", newCount)
|
||||
end
|
||||
end
|
||||
|
||||
function syncCustomizableMetadata(card)
|
||||
@ -663,7 +674,7 @@ function clickableClues(showCounter)
|
||||
local pos = self.positionToWorld({x = -1.12, y = 0.05, z = 0.7})
|
||||
for i = 1, clueCount do
|
||||
pos.y = pos.y + 0.045 * i
|
||||
TokenManager.spawnToken(pos, "clue", PLAY_ZONE_ROTATION)
|
||||
tokenManager.spawnToken(pos, "clue", PLAY_ZONE_ROTATION)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,72 +1,74 @@
|
||||
---
|
||||
--- Generated by EmmyLua(https://github.com/EmmyLua)
|
||||
--- Created by Whimsical.
|
||||
--- DateTime: 2021-02-02 9:41 a.m.
|
||||
---
|
||||
|
||||
local zone = nil
|
||||
local CHAOS_TOKEN_NAMES = {
|
||||
["Elder Sign"] = true,
|
||||
["+1"] = true,
|
||||
["0"] = true,
|
||||
["-1"] = true,
|
||||
["-2"] = true,
|
||||
["-3"] = true,
|
||||
["-4"] = true,
|
||||
["-5"] = true,
|
||||
["-6"] = true,
|
||||
["-7"] = true,
|
||||
["-8"] = true,
|
||||
["Skull"] = true,
|
||||
["Cultist"] = true,
|
||||
["Tablet"] = true,
|
||||
["Elder Thing"] = true,
|
||||
["Auto-fail"] = true,
|
||||
["Bless"] = true,
|
||||
["Curse"] = true,
|
||||
["Frost"] = true
|
||||
}
|
||||
|
||||
-- Forward Declaration
|
||||
---@param is_enabled boolean
|
||||
local setMenu = function(is_enabled) end
|
||||
|
||||
local function enable()
|
||||
if self.held_by_color~=nil then return end
|
||||
local position = self:getPosition()
|
||||
local rotation = self:getRotation()
|
||||
local scale = self:getScale()
|
||||
|
||||
zone = spawnObject {
|
||||
type = "ScriptingTrigger",
|
||||
position = Vector(position.x, position.y+25+(bit32.rshift(scale.y, 1))+0.41, position.z),
|
||||
rotation = rotation,
|
||||
scale = Vector(scale.x*2, 50, scale.z*2),
|
||||
sound = true,
|
||||
snap_to_grid = true
|
||||
}
|
||||
|
||||
setMenu(false)
|
||||
end
|
||||
|
||||
local function disable()
|
||||
if zone~=nil then zone:destruct() end
|
||||
setMenu(true)
|
||||
end
|
||||
|
||||
---@param is_enabled boolean
|
||||
setMenu = function(is_enabled)
|
||||
self:clearContextMenu()
|
||||
if is_enabled then
|
||||
self:addContextMenuItem("Enable", enable, false)
|
||||
else
|
||||
self:addContextMenuItem("Disable", disable, false)
|
||||
end
|
||||
end
|
||||
|
||||
function onLoad(save_state)
|
||||
if save_state=="" then return end
|
||||
local data = JSON.decode(save_state)
|
||||
zone = getObjectFromGUID(data.zone)
|
||||
setMenu(zone==nil)
|
||||
end
|
||||
|
||||
-- general code
|
||||
function onSave()
|
||||
return JSON.encode {
|
||||
zone = zone and zone:getGUID() or nil
|
||||
}
|
||||
return JSON.encode(zone and zone.getGUID() or nil)
|
||||
end
|
||||
|
||||
---@param entering TTSObject
|
||||
---@param object TTSObject
|
||||
function onObjectEnterScriptingZone(entering , object)
|
||||
if zone~=entering then return end
|
||||
if object==self then return end
|
||||
if object.type=="Deck" or object.type=="Card" then return end
|
||||
|
||||
object:destruct()
|
||||
function onLoad(savedData)
|
||||
if savedData ~= "" and savedData ~= nil then
|
||||
zone = getObjectFromGUID(JSON.decode(savedData))
|
||||
end
|
||||
setMenu(zone == nil)
|
||||
end
|
||||
|
||||
---@param color string
|
||||
function onPickUp(color)
|
||||
disable()
|
||||
-- context menu functions
|
||||
function enable()
|
||||
local scale = self.getScale()
|
||||
|
||||
zone = spawnObject({
|
||||
type = "ScriptingTrigger",
|
||||
position = self.getPosition() + Vector(0, 2.5 + 0.11, 0),
|
||||
rotation = self.getRotation(),
|
||||
scale = { scale.x * 2, 5, scale.z * 2 }
|
||||
})
|
||||
|
||||
setMenu(false)
|
||||
end
|
||||
|
||||
function disable()
|
||||
if zone ~= nil then zone.destruct() end
|
||||
setMenu(true)
|
||||
end
|
||||
|
||||
-- core functions
|
||||
function setMenu(isEnabled)
|
||||
self.clearContextMenu()
|
||||
if isEnabled then
|
||||
self.addContextMenuItem("Enable", enable)
|
||||
else
|
||||
self.addContextMenuItem("Disable", disable)
|
||||
end
|
||||
end
|
||||
|
||||
function onObjectEnterScriptingZone(entering, object)
|
||||
if zone ~= entering then return end
|
||||
if object == self or object.type == "Deck" or object.type == "Card" then return end
|
||||
if CHAOS_TOKEN_NAMES[object.getName()] then return end
|
||||
object.destruct()
|
||||
end
|
||||
|
||||
function onPickUp()
|
||||
disable()
|
||||
end
|
||||
|
@ -104,4 +104,17 @@
|
||||
</Panel>
|
||||
</VerticalLayout>
|
||||
|
||||
<!-- Title Splash when starting a scenario -->
|
||||
<Text id="title_splash"
|
||||
fontSize="150"
|
||||
font="font_teutonic-arkham"
|
||||
outline="black"
|
||||
outlineSize="3 -3"
|
||||
showAnimation="FadeIn"
|
||||
hideAnimation="FadeOut"
|
||||
active="false"
|
||||
animationDuration="2"
|
||||
horizontalOverflow="Wrap">
|
||||
</Text>
|
||||
|
||||
<Include src="OptionPanel.xml"/>
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
<!-- options -->
|
||||
<Row class="option-text"
|
||||
preferredHeight="85"/>
|
||||
preferredHeight="70"/>
|
||||
<Cell class="option-text"
|
||||
color="#333333"/>
|
||||
<Cell class="option-button"
|
||||
@ -105,7 +105,7 @@
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Enable snap tags</Text>
|
||||
<Text class="description">Only cards with the tag "Asset" will snap (official cards are supported by default).
Disable this if you are having issues with custom content.</Text>
|
||||
<Text class="description">Only cards with the tag "Asset" will snap (official cards are supported by default). Disable this if you are having issues with custom content.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
@ -142,6 +142,34 @@
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: use clickable resource counters -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Use clickable resource counters</Text>
|
||||
<Text class="description">This enables spawning of clickable resource tokens for player cards.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
<Toggle id="useResourceCounters"
|
||||
onValueChanged="onClick_toggleOption(useResourceCounters)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: splash scenario name on setup -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Show Scenario Title on Setup</Text>
|
||||
<Text class="description">Fade in the name of the scenario for 2 seconds when placing down a scenario.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
<Toggle id="showTitleSplash"
|
||||
onValueChanged="onClick_toggleOption(showTitleSplash)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Group: fan-made accessories -->
|
||||
<Row class="group-header">
|
||||
<Cell class="group-header">
|
||||
@ -185,7 +213,7 @@
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Hand Helper</Text>
|
||||
<Text class="description">Never count your hand cards again! This tool does that for you and can even take "Dream-Enhancing Serum" into account. Also includes a button for randomly discard a card.</Text>
|
||||
<Text class="description">Never count your hand cards again! This tool does that for you and additionally enables easy discarding of random cards.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
@ -208,6 +236,20 @@
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: show attachment helper -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Attachment Helper</Text>
|
||||
<Text class="description">Provides a card-sized bag for cards that are attached to other cards (e.g. Backpack).</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
<Toggle id="showAttachmentHelper"
|
||||
onValueChanged="onClick_toggleOption(showAttachmentHelper)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: show navigation overlay -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
@ -221,6 +263,48 @@
|
||||
onValueChanged="onClick_toggleOption(showNavigationOverlay)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: show CYOA campaign guides -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">CYOA Campaign Guides</Text>
|
||||
<Text class="description">Displays in a "Choose Your Own Adventure" style redesigned campaign guides.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
<Toggle id="showCYOA"
|
||||
onValueChanged="onClick_toggleOption(showCYOA)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: show custom playmat images -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Custom Playmat Images</Text>
|
||||
<Text class="description">Places a tool that displays custom playmat images for all cycles in a gallery-like fashion.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
<Toggle id="showCustomPlaymatImages"
|
||||
onValueChanged="onClick_toggleOption(showCustomPlaymatImages)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
|
||||
<!-- Option: show displacement tool -->
|
||||
<Row class="option-text">
|
||||
<Cell class="option-text">
|
||||
<VerticalLayout class="text-column">
|
||||
<Text class="option-header">Displacement Tool</Text>
|
||||
<Text class="description">This allows moving all objects on the main playmat in a chosen direction.</Text>
|
||||
</VerticalLayout>
|
||||
</Cell>
|
||||
<Cell class="option-button">
|
||||
<Toggle id="showDisplacementTool"
|
||||
onValueChanged="onClick_toggleOption(showDisplacementTool)"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
</TableLayout>
|
||||
</VerticalScrollView>
|
||||
</Cell>
|
||||
|
Loading…
Reference in New Issue
Block a user