diff --git a/src/core/GameKeyHandler.ttslua b/src/core/GameKeyHandler.ttslua index 1b2ec83b..23a336ba 100644 --- a/src/core/GameKeyHandler.ttslua +++ b/src/core/GameKeyHandler.ttslua @@ -1,17 +1,20 @@ local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local guidReferenceApi = require("core/GUIDReferenceApi") +local navigationOverlayApi = require("core/NavigationOverlayApi") local optionPanelApi = require("core/OptionPanelApi") local playmatApi = require("playermat/PlaymatApi") local searchLib = require("util/SearchLib") local victoryDisplayApi = require("core/VictoryDisplayApi") function onLoad() - addHotkey("Add Doom to Agenda", addDoomToAgenda) - addHotkey("Bless/Curse Status", showBlessCurseStatus) - addHotkey("Discard Object", discardObject) + addHotkey("Add doom to agenda", addDoomToAgenda) + addHotkey("Discard object", discardObject) addHotkey("Discard top card", discardTopDeck) + addHotkey("Display Bless/Curse status", showBlessCurseStatus) addHotkey("Move card to Victory Display", moveCardToVictoryDisplay) addHotkey("Remove a use", removeOneUse) + addHotkey("Switch seat clockwise", switchSeatClockwise) + addHotkey("Switch seat counter-clockwise", switchSeatCounterClockwise) addHotkey("Take clue from location", takeClueFromLocation) addHotkey("Upkeep", triggerUpkeep) addHotkey("Upkeep (Multi-handed)", triggerUpkeepMultihanded) @@ -200,6 +203,59 @@ function removeOneUse(playerColor, hoveredObject) playmatApi.discardListOfObjects(discardForMatColor, { targetObject }) end +-- switches the triggering player to the next seat (clockwise) +function switchSeatClockwise(playerColor) + switchSeat(playerColor, "clockwise") +end + +-- switches the triggering player to the next seat (counter-clockwise) +function switchSeatCounterClockwise(playerColor) + switchSeat(playerColor, "counter-clockwise") +end + +-- handles seat switching in the given direction +function switchSeat(playerColor, direction) + if playerColor == "Black" or playerColor == "Grey" then + broadcastToColor("This hotkey is only available to seated players.", playerColor, "Orange") + return + end + + -- sort function for matcolors based on hand position (Green, White, Orange, Red) + local function sortByHandPosition(color1, color2) + local pos1 = Player[color1].getHandTransform().position + local pos2 = Player[color2].getHandTransform().position + return pos1.z > pos2.z + end + + -- get used playermats + local usedColors = playmatApi.getUsedMatColors() + table.sort(usedColors, sortByHandPosition) + + -- get current seat index + local index + for i, color in ipairs(usedColors) do + if color == playerColor then + index = i + break + end + end + if not index then + broadcastToColor("Couldn't detect investigator.", playerColor, "Orange") + return + end + + -- get next color + index = index + ((direction == "clockwise") and -1 or 1) + if index == 0 then + index = #usedColors + elseif index > #usedColors then + index = 1 + end + + -- swap color + navigationOverlayApi.loadCamera(playerColor, usedColors[index]) +end + -- takes a clue from a location, player needs to hover the clue directly or the location function takeClueFromLocation(playerColor, hoveredObject) local cardName, clue diff --git a/src/core/Global.ttslua b/src/core/Global.ttslua index 0319094e..8ee04751 100644 --- a/src/core/Global.ttslua +++ b/src/core/Global.ttslua @@ -204,6 +204,22 @@ function onObjectEnterZone(zone, enteringObj) end end +-- handle card drawing via number typing for multihanded gameplay +-- (and additionally allow Norman Withers to draw multiple cards via number) +function onObjectNumberTyped(hoveredObject, playerColor, number) + -- only continue for decks or cards + if hoveredObject.type ~= "Deck" and hoveredObject.type ~= "Card" then return end + + -- check whether the hovered object is part of a players draw objects + for _, color in ipairs(playmatApi.getUsedMatColors()) do + local deckAreaObjects = playmatApi.getDeckAreaObjects(color) + if deckAreaObjects.topCard == hoveredObject or deckAreaObjects.draw == hoveredObject then + playmatApi.drawCardsWithReshuffle(color, number) + return true + end + end +end + --------------------------------------------------------- -- chaos token drawing --------------------------------------------------------- diff --git a/src/core/NavigationOverlayApi.ttslua b/src/core/NavigationOverlayApi.ttslua index a39b0879..9a94d04e 100644 --- a/src/core/NavigationOverlayApi.ttslua +++ b/src/core/NavigationOverlayApi.ttslua @@ -22,5 +22,15 @@ do getNOHandler().call("cycleVisibility", playerColor) end + -- loads the specified camera for a player + ---@param player TTSPlayerInstance Player whose camera should be moved + ---@param camera Variant If number: Index of the camera view to load | If string: Color of the playermat to swap to + NavigationOverlayApi.loadCamera = function(playerColor, camera) + getNOHandler().call("loadCameraFromApi", { + playerColor = playerColor, + camera = camera + }) + end + return NavigationOverlayApi end diff --git a/src/core/NavigationOverlayHandler.ttslua b/src/core/NavigationOverlayHandler.ttslua index 6283a815..55f2de16 100644 --- a/src/core/NavigationOverlayHandler.ttslua +++ b/src/core/NavigationOverlayHandler.ttslua @@ -291,9 +291,30 @@ function getDynamicViewBounds(objList) return totalBounds end +function loadCameraFromApi(params) + loadCamera(Player[params.playerColor], params.camera) +end + -- loads the specified camera for a player -function loadCamera(player, index) - local lookHere +---@param player TTSPlayerInstance Player whose camera should be moved +---@param camera Variant If number: Index of the camera view to load | If string: Color of the playermat to swap to +function loadCamera(player, camera) + local lookHere, index, matColor + local matColorList = { "White", "Orange", "Green", "Red" } + local indexList = { + White = 3, + Orange = 4, + Green = 5, + Red = 6 + } + + if tonumber(camera) then + index = tonumber(camera) + matColor = matColorList[index - 2] -- mat index 1 - 4 + else + index = indexList[camera] + matColor = camera + end -- dynamic view of the play area if index == 2 then @@ -307,9 +328,6 @@ function loadCamera(player, index) } -- dynamic view of the clicked play mat elseif index >= 3 and index <= 6 then - local matColorList = { "White", "Orange", "Green", "Red" } - local matColor = matColorList[index - 2] -- mat index 1 - 4 - -- check if anyone (except for yourself) has claimed this color local isClaimed = false @@ -325,6 +343,7 @@ function loadCamera(player, index) local newPlayerColor = playmatApi.getPlayerColor(matColor) copyVisibility({ startColor = player.color, targetColor = newPlayerColor }) player.changeColor(newPlayerColor) + player = Player[newPlayerColor] end -- search on the playmat for objects diff --git a/src/playermat/InvestigatorSkillTracker.ttslua b/src/playermat/InvestigatorSkillTracker.ttslua index 5abf7b19..1f20685c 100644 --- a/src/playermat/InvestigatorSkillTracker.ttslua +++ b/src/playermat/InvestigatorSkillTracker.ttslua @@ -1,62 +1,56 @@ -local BUTTON_PARAMETERS = {} -BUTTON_PARAMETERS.function_owner = self -BUTTON_PARAMETERS.height = 650 -BUTTON_PARAMETERS.width = 700 -BUTTON_PARAMETERS.position = { x = -4.775, y = 0.1, z = -0.03 } -BUTTON_PARAMETERS.color = { 0, 0, 0, 0 } -BUTTON_PARAMETERS.font_color = { 0, 0, 0, 100 } -BUTTON_PARAMETERS.font_size = 450 +local buttonParameters = {} +buttonParameters.function_owner = self +buttonParameters.height = 650 +buttonParameters.width = 700 +buttonParameters.position = { x = -4.775, y = 0.1, z = -0.03 } +buttonParameters.color = { 0, 0, 0, 0 } +buttonParameters.font_color = { 0, 0, 0, 100 } +buttonParameters.font_size = 450 function onSave() return JSON.encode(stats) end -- load stats and make buttons (left to right) -function onLoad(saved_data) - stats = JSON.decode(saved_data) or { 1, 1, 1, 1 } +function onLoad(savedData) + stats = JSON.decode(savedData) or { 1, 1, 1, 1 } - for i = 1, 4 do - BUTTON_PARAMETERS.label = stats[i] .. " " - BUTTON_PARAMETERS.position.x = BUTTON_PARAMETERS.position.x + 1.91 - BUTTON_PARAMETERS.click_function = attachIndex("button_click", i) - self.createButton(BUTTON_PARAMETERS) + for index = 1, 4 do + local fnName = "buttonClick" .. index + _G[fnName] = function(_, _, isRightClick) buttonClick(isRightClick, index) end + buttonParameters.click_function = fnName + buttonParameters.position.x = buttonParameters.position.x + 1.91 + self.createButton(buttonParameters) + updateButtonLabel(index) end self.addContextMenuItem("Reset to 1s", function() updateStats({ 1, 1, 1, 1 }) end) end --- helper function to carry index -function attachIndex(click_function, index) - local fn_name = click_function .. index - _G[fn_name] = function(obj, player_color, isRightClick) - _G[click_function](obj, player_color, isRightClick, index) - end - return fn_name -end - -function button_click(_, _, isRightClick, index) +function buttonClick(isRightClick, index) stats[index] = math.min(math.max(stats[index] + (isRightClick and -1 or 1), 0), 99) - changeButton(index) + updateButtonLabel(index) end -function changeButton(index) - local font_size = BUTTON_PARAMETERS.font_size +-- sync the button label to the internal value +function updateButtonLabel(index) + local fontSize = buttonParameters.font_size local whitespace = " " if stats[index] > 9 then - font_size = BUTTON_PARAMETERS.font_size * 0.65 + fontSize = buttonParameters.font_size * 0.65 whitespace = " " end - self.editButton({ index = index - 1, label = stats[index] .. whitespace, font_size = font_size }) + self.editButton({ index = index - 1, label = stats[index] .. whitespace, font_size = fontSize }) end --- formatting of "newStats": {Willpower, Intellect, Fight, Agility} +-- update the stats to the provided values +---@param newStats Table Contains the new values for the stats: {Willpower, Intellect, Fight, Agility} function updateStats(newStats) if newStats and #newStats == 4 then stats = newStats + + for i = 1, 4 do updateButtonLabel(i) end elseif newStats then printToAll("Provided new stats are incomplete or incorrectly formatted.", "Red") - return end - - for i = 1, 4 do changeButton(i) end end diff --git a/src/playermat/PlaymatApi.ttslua b/src/playermat/PlaymatApi.ttslua index 8ac011e9..1e067bc7 100644 --- a/src/playermat/PlaymatApi.ttslua +++ b/src/playermat/PlaymatApi.ttslua @@ -1,6 +1,7 @@ do local PlaymatApi = {} local guidReferenceApi = require("core/GUIDReferenceApi") + local searchLib = require("util/SearchLib") -- Convenience function to look up a mat's object by color, or get all mats. ---@param matColor String Color of the playmat - White, Orange, Green, Red or All @@ -180,6 +181,15 @@ do end end + -- triggers the draw function for the specified playmat + ---@param matColor String Color of the playmat - White, Orange, Green, Red or All + ---@param number Number Amount of cards to draw + PlaymatApi.drawCardsWithReshuffle = function(matColor, number) + for _, mat in pairs(getMatForColor(matColor)) do + mat.call("drawCardsWithReshuffle", number) + end + end + -- returns the resource counter amount ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param type String Counter to target @@ -189,6 +199,22 @@ do end end + -- returns a list of mat colors that have an investigator placed + PlaymatApi.getUsedMatColors = function() + local localInvestigatorPosition = { x = -1.17, y = 1, z = -0.01 } + local usedColors = {} + + for matColor, mat in pairs(getMatForColor("All")) do + local searchPos = mat.positionToWorld(localInvestigatorPosition) + local searchResult = searchLib.atPosition(searchPos, "isCardOrDeck") + + if #searchResult > 0 then + table.insert(usedColors, matColor) + end + end + return usedColors + end + -- resets the specified skill tracker to "1, 1, 1, 1" ---@param matColor String Color of the playmat - White, Orange, Green, Red or All PlaymatApi.resetSkillTracker = function(matColor)