diff --git a/objects/LuaScriptState.luascriptstate b/objects/LuaScriptState.luascriptstate index 44534c67..9e3445b8 100644 --- a/objects/LuaScriptState.luascriptstate +++ b/objects/LuaScriptState.luascriptstate @@ -25,5 +25,6 @@ "useClueClickers": false, "useResourceCounters": "disabled", "useSnapTags": true - } + }, + "handVisibility": [] } diff --git a/src/core/GameKeyHandler.ttslua b/src/core/GameKeyHandler.ttslua index 7b99970e..4fcfc6c5 100644 --- a/src/core/GameKeyHandler.ttslua +++ b/src/core/GameKeyHandler.ttslua @@ -106,7 +106,7 @@ function takeCardIntoThreatArea(playerColor, hoveredObject) -- contruct feedback message local cardName = hoveredObject.getName() if cardName == "" then cardName = "a card" end - local playerName = getColoredName(playerColor) + local playerName = Global.call("getColoredName", playerColor) broadcastToAll("Moved " .. cardName .. " to " .. playerName .. "'s threat area.", "White") -- get new rotation (rounded) @@ -379,7 +379,7 @@ function removeOneUse(playerColor, hoveredObject) end -- construct feedback message - local playerName = getColoredName(playerColor) + local playerName = Global.call("getColoredName", playerColor) local cardInfo = "" if cardName and cardName ~= "" then cardInfo = " from " .. cardName @@ -530,7 +530,7 @@ function takeClueFromLocation(playerColor, hoveredObject) end -- construct feedback message - local playerName = getColoredName(playerColor) + local playerName = Global.call("getColoredName", playerColor) local cardInfo = "" if cardName and cardName ~= "" then cardInfo = " from " .. cardName @@ -574,17 +574,6 @@ function getFirstSeatedPlayer() end end --- returns the colored steam name or color -function getColoredName(playerColor) - local displayName = playerColor - if Player[playerColor].steam_name then - displayName = Player[playerColor].steam_name - end - - -- add bb-code - return "[" .. Color.fromString(playerColor):toHex() .. "]" .. displayName .. "[-]" -end - function roundToMultiple(num, mult) return math.floor((num + mult / 2) / mult) * mult end diff --git a/src/core/Global.ttslua b/src/core/Global.ttslua index a7ba50a9..5ab01793 100644 --- a/src/core/Global.ttslua +++ b/src/core/Global.ttslua @@ -71,6 +71,9 @@ local RESOURCE_OPTIONS = { "disabled" } +-- tracks the visibility of each hand +local handVisibility = {} + --------------------------------------------------------- -- data for tokens --------------------------------------------------------- @@ -119,19 +122,21 @@ function onSave() end return JSON.encode({ - optionPanel = optionPanel, acknowledgedUpgradeVersions = acknowledgedUpgradeVersions, - chaosTokensLastMatGUID = chaosTokensLastMatGUID, - chaosTokensGUID = chaosTokensGUID + chaosTokensLastMatGUID = chaosTokensLastMatGUID, + chaosTokensGUID = chaosTokensGUID, + handVisibility = handVisibility, + optionPanel = optionPanel }) end function onLoad(savedData) if savedData and savedData ~= "" then - local loadedData = JSON.decode(savedData) - optionPanel = loadedData.optionPanel + local loadedData = JSON.decode(savedData) acknowledgedUpgradeVersions = loadedData.acknowledgedUpgradeVersions - chaosTokensLastMatGUID = loadedData.chaosTokensLastMatGUID + chaosTokensLastMatGUID = loadedData.chaosTokensLastMatGUID + handVisibility = loadedData.handVisibility + optionPanel = loadedData.optionPanel -- restore saved state for drawn chaos tokens for _, guid in ipairs(loadedData.chaosTokensGUID or {}) do @@ -147,6 +152,7 @@ function onLoad(savedData) end getModVersion() + updateHandVisibility() math.randomseed(os.time()) -- initialization of loadable objects library (delay to let Navigation Overlay build) @@ -223,6 +229,8 @@ function onObjectEnterZone(zone, object) object.call("resetSealedTokens") end end + + applyHidingToCard(object, zone.getValue()) end end @@ -238,6 +246,9 @@ function onObjectLeaveZone(zone, object) if zone.type == "Hand" and object.hasTag("CardWithHelper") then object.call("syncDisplayWithOptionPanel") end + + -- make object visible + object.setHiddenFrom({}) end, 1) end @@ -432,7 +443,7 @@ function returnAndRedraw(_, tokenGUID) -- perform the actual token replacing trackChaosToken(tokenName, mat.getGUID(), true) - local params = {token = returnedToken, fromBag = true} + local params = { token = returnedToken, fromBag = true } returnChaosTokenToBag(params) chaosTokens[indexOfReturnedToken] = drawChaosToken({ @@ -1234,8 +1245,7 @@ end -- updates the preview window function updatePreviewWindow() local item = library[contentToShow][currentListItem] - local tempImage = - "https://steamusercontent-a.akamaihd.net/ugc/2115061845788345842/2CD6ABC551555CCF58F9D0DDB7620197BA398B06/" + local tempImage = "https://steamusercontent-a.akamaihd.net/ugc/2115061845788345842/2CD6ABC551555CCF58F9D0DDB7620197BA398B06/" -- set default image if not defined if item.boxsize == nil or item.boxsize == "" or item.boxart == nil or item.boxart == "" then @@ -1397,8 +1407,7 @@ function contentDownloadCallback(request, params) if pos then spawnTable.position = pos else - broadcastToAll( - "Please make space in the area below the tentacle stand in the upper middle of the table and try again.", "Red") + broadcastToAll("Please make sure there's space in the upper middle of the table and try again.", "Red") return end end @@ -1819,6 +1828,74 @@ function toggleCardHelpers(state) end end +-- toggles the hand visibility of a hand for a specific player +function handVisibilityToggle(params) + local playerColor = params.playerColor + local handColor = params.handColor + + -- collect data for message + local state, preposition + + -- modify data table + if not handVisibility[handColor] then + handVisibility[handColor] = { playerColor, handColor } + state, preposition = "Showing", "to" + elseif removeValueFromTable(handVisibility[handColor], playerColor) then + -- something was removed, maybe clear the table + if #handVisibility[handColor] == 1 and handVisibility[handColor][1] == handColor then + handVisibility[handColor] = nil + end + state, preposition = "Hiding", "from" + else + -- add the new color as viewer + table.insert(handVisibility[handColor], playerColor) + state, preposition = "Showing", "to" + end + + local sourceName = getColoredName(handColor) + local targetName = getColoredName(playerColor) + broadcastToAll(state .. " " .. sourceName .. "'s hand " .. preposition .. " " .. targetName .. ".") + updateHandVisibility() +end + +function updateHandVisibility() + -- update the global setting + if not next(handVisibility) then + Hands.hiding = 1 + else + Hands.hiding = 3 + end + + -- update visibility of cards already in hands (delay seems to be necessary) + Wait.frames(function() + for _, handColor in ipairs(Player.getAvailableColors()) do + for _, handObj in ipairs(Player[handColor].getHandObjects()) do + applyHidingToCard(handObj, handColor) + end + end + end, 1) +end + +-- maybe hide card from non-allowed players +---@param card tts__Object Card to maybe hide +---@param handColor? string Color of the hand this card is in +function applyHidingToCard(card, handColor) + if next(handVisibility) then + local viewers = { handColor } + if handColor and handVisibility[handColor] then + viewers = handVisibility[handColor] + end + + local hiddenFrom = Player.getColors() + for _, viewer in ipairs(viewers) do + removeValueFromTable(hiddenFrom, viewer) + end + card.setHiddenFrom(hiddenFrom) + else + card.setHiddenFrom({}) + end +end + --------------------------------------------------------- -- Update notification related functionality --------------------------------------------------------- @@ -1892,13 +1969,15 @@ end --------------------------------------------------------- -- removes a value from a table +---@return boolean: True if something was removed function removeValueFromTable(t, val) for i, v in ipairs(t) do if v == val then table.remove(t, i) - break + return true end end + return false end -- checks if a table is empty @@ -1909,3 +1988,14 @@ function isTableEmpty(tbl) return false end end + +-- returns the colored steam name or color +function getColoredName(playerColor) + local displayName = playerColor + if Player[playerColor].steam_name then + displayName = Player[playerColor].steam_name + end + + -- add bb-code + return "[" .. Color.fromString(playerColor):toHex() .. "]" .. displayName .. "[-]" +end diff --git a/src/core/token/TokenManager.ttslua b/src/core/token/TokenManager.ttslua index c14c5490..6fe68f09 100644 --- a/src/core/token/TokenManager.ttslua +++ b/src/core/token/TokenManager.ttslua @@ -245,9 +245,15 @@ do if tokenType == "resource" and stateID ~= nil and stateID ~= 1 then callback = function(spawned) spawned.setState(stateID) end elseif tokenType == "universalActionAbility" then - local matColor = playermatApi.getMatColorByPosition(card.getPosition()) - local class = playermatApi.returnInvestigatorClass(matColor) - callback = function(spawned) spawned.call("updateClassAndSymbol", { class = class, symbol = subType or class }) end + callback = function(spawned) + local matColor = playermatApi.getMatColorByPosition(card.getPosition()) + local activeInvestigatorData = playermatApi.getActiveInvestigatorData(matColor) + + spawned.call("updateClassAndSymbol", { + class = activeInvestigatorData.class, + symbol = subType or activeInvestigatorData.class + }) + end end for i = 1, tokenCount do diff --git a/src/playermat/Playermat.ttslua b/src/playermat/Playermat.ttslua index b1814e18..650b2a38 100644 --- a/src/playermat/Playermat.ttslua +++ b/src/playermat/Playermat.ttslua @@ -25,6 +25,11 @@ local availableOptions = { id = "handColorSelect", title = "Select Hand Color", type = "button" + }, + { + id = "visibilitySelect", + title = "Show/Hide this Hand to me", + type = "button" } } } @@ -618,7 +623,8 @@ function doDiscardOne() local cardId = choices[num] .. "/" .. #hand deckLib.placeOrMergeIntoDeck(card, returnGlobalDiscardPosition(), self.getRotation()) - broadcastToAll(getColoredName(playerColor) .. " randomly discarded " .. cardName " (" .. cardId .. ").", "White") + local playerName = Global.call("getColoredName", playerColor) + broadcastToAll(playerName .. " randomly discarded " .. cardName " (" .. cardId .. ").", "White") end end @@ -923,6 +929,7 @@ function onClick_hideOrShowOptions(player) }) end +-- show the triggering player a dialog to select a playermat texture function onClick_textureSelect(player) local textureList = {} for texture, _ in pairs(nameToTexture) do @@ -931,6 +938,7 @@ function onClick_textureSelect(player) player.showOptionsDialog("Select a texture:", textureList, _, updateTexture) end +-- show the triggering player a dialog to select a new hand color for this mat function onClick_handColorSelect(player) local colorList = Player.getColors() @@ -975,6 +983,16 @@ function onClick_handColorSelect(player) end) end +-- instruct Global to update this mat's hand visibility +function onClick_visibilitySelect(player) + if player.color == playerColor then + printToColor("This is meant to be clicked by other players to be able to see your hand (primarily for multi-handed gameplay). It won't do anything for you.", playerColor) + return + end + + Global.call("handVisibilityToggle", { playerColor = player.color, handColor = playerColor }) +end + -- changes the UI state and the internal variable for the togglebuttons function onClick_toggleOption(player, _, id) local state = optionPanelData[id] @@ -1560,16 +1578,6 @@ function updatePlayerCards(args) tokenManager.addPlayerCardData(playerCardData) end --- returns the colored steam name or color -function getColoredName(playerColor) - local displayName = playerColor - if Player[playerColor].steam_name then - displayName = Player[playerColor].steam_name - end - - -- add bb-code - return "[" .. Color.fromString(playerColor):toHex() .. "]" .. displayName .. "[-]" -end function getActiveInvestigatorData() return activeInvestigatorData end diff --git a/src/util/TokenSpawnTool.ttslua b/src/util/TokenSpawnTool.ttslua index cc91d966..07a2942d 100644 --- a/src/util/TokenSpawnTool.ttslua +++ b/src/util/TokenSpawnTool.ttslua @@ -66,12 +66,16 @@ function onScriptingButtonDown(index, playerColor) -- check for nearest investigator card and change action token state to its class elseif tokenType == "universalActionAbility" then - local matColor = playermatApi.getMatColorByPosition(position) - local matRotation = playermatApi.returnRotation(matColor) - local class = playermatApi.returnInvestigatorClass(matColor) callback = function(spawned) + local matColor = playermatApi.getMatColorByPosition(position) + local matRotation = playermatApi.returnRotation(matColor) + local activeInvestigatorData = playermatApi.getActiveInvestigatorData(matColor) + spawned.setRotation(matRotation) - spawned.call("updateClassAndSymbol", { class = class, symbol = class }) + spawned.call("updateClassAndSymbol", { + class = activeInvestigatorData.class, + symbol = activeInvestigatorData.class + }) end end