Merge branch 'main' into location-snap-tags

This commit is contained in:
Chr1Z93 2023-01-04 00:50:37 +01:00
commit 560435a3bc
18 changed files with 329 additions and 146 deletions

View File

@ -154,7 +154,7 @@
"ChaosBagStatTracker.766620",
"Blesstokens.afa06b",
"Cursetokens.bd0253",
"WhimsicalsTokenRemover.0a5a29",
"TokenRemover.0a5a29",
"TokenSpawner.36b4ee",
"Fan-MadeScenariosCampaignsMiscellany.66e97c",
"OfficialStandaloneChallengeScenarios.0ef5c8",

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1 @@
{"optionPanel":{"playAreaSnapTags":true,"showChaosBagManager":false,"showCleanUpHelper":false,"showDrawButton":false,"showHandHelper":[],"showNavigationOverlay":false,"showTitleSplash":true,"showTokenArranger":false,"useClueClickers":false,"useSnapTags":true}}
{"optionPanel":{"playAreaSnapTags":true,"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}}

View File

@ -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,15 +45,15 @@
],
"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": ""

View File

@ -18,7 +18,8 @@
"Damage.cd2a02",
"Horror.36be72",
"ClueDoom.a3fb6c",
"Resource.00d19a"
"Resource.00d19a",
"ResourceCounter.498ec0"
],
"ContainedObjects_path": "TokenSource.124381",
"Description": "",

View File

@ -40,7 +40,7 @@
"Nickname": "ClueDoom",
"Snap": false,
"Sticky": true,
"Tooltip": false,
"Tooltip": true,
"Transform": {
"posX": 78.661,
"posY": 2.398,

View File

@ -40,7 +40,7 @@
"Nickname": "ClueDoom",
"Snap": false,
"Sticky": true,
"Tooltip": false,
"Tooltip": true,
"Transform": {
"posX": 78.738,
"posY": 2.287,

View File

@ -40,7 +40,7 @@
"Nickname": "Resource",
"Snap": false,
"Sticky": true,
"Tooltip": false,
"Tooltip": true,
"Transform": {
"posX": 78.848,
"posY": 2.273,

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

View File

@ -1,18 +1,18 @@
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)
function onLoad(savedData)
if savedData ~= nil then
val = JSON.decode(savedData)
end
local name = self.getName()
local position = {}
if name == "Damage" or name == "Resources" then
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 }
@ -32,19 +32,21 @@ function onLoad(saved_data)
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)
})
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)
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

View File

@ -1,5 +1,3 @@
local tokenManager = require("core/token/TokenManager")
---------------------------------------------------------
-- general setup
---------------------------------------------------------
@ -36,6 +34,7 @@ local IS_RESHUFFLING = false
local bagSearchers = {}
local hideTitleSplashWaitFunctionId = nil
local playmatAPI = require("playermat/PlaymatApi")
local tokenManager = require("core/token/TokenManager")
local playAreaAPI = require("core/PlayAreaApi")
---------------------------------------------------------
@ -820,6 +819,10 @@ 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: Play area snap tags
elseif id == "playAreaSnapTags" then
playAreaAPI.setLimitSnapsByType(state)
@ -834,7 +837,7 @@ function applyOptionPanelChange(id, state)
-- 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
@ -849,11 +852,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
@ -862,8 +881,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)
@ -875,7 +894,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)
@ -918,7 +937,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]]
@ -950,16 +973,20 @@ function onClick_defaultSettings()
-- clean reset of variable
optionPanel = {
useSnapTags = true,
playAreaSnapTags = true,
showAttachmentHelper = false,
showCleanUpHelper = false,
showChaosBagManager = false,
showCustomPlaymatImages = false,
showCYOA = false,
showDisplacementTool = false,
showDrawButton = false,
useClueClickers = false,
showHandHelper = {},
showNavigationOverlay = false,
showTitleSplash = true,
showTokenArranger = false,
showCleanUpHelper = false,
showHandHelper = {},
showChaosBagManager = false,
showNavigationOverlay = false
useClueClickers = false,
useSnapTags = true
}
-- update UI

View File

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

View File

@ -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,8 +440,13 @@ function replenishTokens(card, count, replenish)
local newCount = foundTokens + replenish
if newCount > count then newCount = count end
if clickableResourceCounter then
clickableResourceCounter.call("updateVal", newCount)
else
tokenManager.spawnTokenGroup(card, "resource", newCount)
end
end
function syncCustomizableMetadata(card)
local cardMetadata = JSON.decode(card.getGMNotes()) or { }
@ -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

View File

@ -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
-- 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
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
}
-- general code
function onSave()
return JSON.encode(zone and zone.getGUID() or nil)
end
function onLoad(savedData)
if savedData ~= "" and savedData ~= nil then
zone = getObjectFromGUID(JSON.decode(savedData))
end
setMenu(zone == nil)
end
-- 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
local function disable()
if zone~=nil then zone:destruct() end
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)
-- core functions
function setMenu(isEnabled)
self.clearContextMenu()
if isEnabled then
self.addContextMenuItem("Enable", enable)
else
self:addContextMenuItem("Disable", disable, false)
self.addContextMenuItem("Disable", disable)
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
function onSave()
return JSON.encode {
zone = 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()
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
---@param color string
function onPickUp(color)
function onPickUp()
disable()
end

View File

@ -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).&#xA;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">
@ -156,6 +156,20 @@
</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">
@ -213,7 +227,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">
@ -236,6 +250,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">
@ -249,6 +277,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>