Merge pull request #440 from argonui/discard-hotkey

Added discard hotkey & remove one use hotkey
This commit is contained in:
Entrox-Licher 2023-11-06 10:04:30 -05:00 committed by GitHub
commit 1ad154720f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 184 additions and 24 deletions

View File

@ -7,7 +7,9 @@ local victoryDisplayApi = require("core/VictoryDisplayApi")
function onLoad() function onLoad()
addHotkey("Add Doom to Agenda", addDoomToAgenda) addHotkey("Add Doom to Agenda", addDoomToAgenda)
addHotkey("Bless/Curse Status", showBlessCurseStatus) addHotkey("Bless/Curse Status", showBlessCurseStatus)
addHotkey("Discard Object", discardObject)
addHotkey("Move card to Victory Display", moveCardToVictoryDisplay) addHotkey("Move card to Victory Display", moveCardToVictoryDisplay)
addHotkey("Remove a use", removeOneUse)
addHotkey("Take clue from location", takeClueFromLocation) addHotkey("Take clue from location", takeClueFromLocation)
addHotkey("Upkeep", triggerUpkeep) addHotkey("Upkeep", triggerUpkeep)
addHotkey("Upkeep (Multi-handed)", triggerUpkeepMultihanded) addHotkey("Upkeep (Multi-handed)", triggerUpkeepMultihanded)
@ -46,11 +48,142 @@ function addDoomToAgenda()
doomCounter.call("addVal", 1) doomCounter.call("addVal", 1)
end end
-- discard the hovered object to the respective trashcan and discard tokens on it if it was a card
function discardObject(playerColor, hoveredObject)
-- only continue if an unlocked card, deck or tile was hovered
if hoveredObject == nil
or (hoveredObject.type ~= "Card" and hoveredObject.type ~= "Deck" and hoveredObject.type ~= "Tile")
or hoveredObject.locked then
broadcastToColor("Hover a token/tile or a card/deck and try again.", playerColor, "Yellow")
return
end
-- warning for locations since these are usually not meant to be discarded
if hoveredObject.hasTag("Location") then
broadcastToAll("Watch out: A location was discarded.", "Yellow")
end
-- initialize list of objects to discard
local discardTheseObjects = { hoveredObject }
-- discard tokens / tiles on cards / decks
if hoveredObject.type ~= "Tile" then
for _, v in ipairs(searchOnObj(hoveredObject)) do
if v.hit_object.type == "Tile" then
table.insert(discardTheseObjects, v.hit_object)
end
end
end
local discardForMatColor = getColorToDiscardFor(hoveredObject, playerColor)
playmatApi.discardListOfObjects(discardForMatColor, discardTheseObjects)
end
-- helper function to get the player to trigger the discard function for
function getColorToDiscardFor(hoveredObject, playerColor)
local pos = hoveredObject.getPosition()
local closestMatColor = playmatApi.getMatColorByPosition(pos)
-- check if actually on the closest playmat
local closestMat = guidReferenceApi.getObjectByOwnerAndType(closestMatColor, "Playermat")
local bounds = closestMat.getBounds()
-- define the area "near" the playmat
local bufferAroundPlaymat = 2
local areaNearPlaymat = {}
areaNearPlaymat.minX = bounds.center.x - bounds.size.x / 2 - bufferAroundPlaymat
areaNearPlaymat.maxX = bounds.center.x + bounds.size.x / 2 + bufferAroundPlaymat
areaNearPlaymat.minZ = bounds.center.z - bounds.size.z / 2 - bufferAroundPlaymat
areaNearPlaymat.maxZ = bounds.center.z + bounds.size.z / 2 + bufferAroundPlaymat
-- discard to closest mat if near it, use triggering playmat if not
local discardForMatColor
if inArea(pos, areaNearPlaymat) then
return closestMatColor
else
return playmatApi.getMatColor(playerColor)
end
end
-- moves the hovered card to the victory display -- moves the hovered card to the victory display
function moveCardToVictoryDisplay(_, hoveredObject) function moveCardToVictoryDisplay(_, hoveredObject)
victoryDisplayApi.placeCard(hoveredObject) victoryDisplayApi.placeCard(hoveredObject)
end end
-- removes a use from a card (or a token if hovered)
function removeOneUse(playerColor, hoveredObject)
-- only continue if an unlocked card or tile was hovered
if hoveredObject == nil
or (hoveredObject.type ~= "Card" and hoveredObject.type ~= "Tile")
or hoveredObject.locked then
broadcastToColor("Hover a token/tile or a card and try again.", playerColor, "Yellow")
return
end
local targetObject = nil
-- discard hovered token / tile
if hoveredObject.type == "Tile" then
targetObject = hoveredObject
elseif hoveredObject.type == "Card" then
-- grab the first use type from the metadata (or nil)
local notes = JSON.decode(hoveredObject.getGMNotes()) or {}
local usesData = notes.uses or {}
local useInfo = usesData[1] or {}
local searchForType = useInfo.type
if searchForType then searchForType = searchForType:lower() end
for _, v in ipairs(searchOnObj(hoveredObject)) do
local obj = v.hit_object
if obj.type == "Tile" and not obj.locked and obj.memo ~= "resourceCounter" then
-- check for matching object, otherwise use the first hit
if obj.memo == searchForType then
targetObject = obj
break
elseif not targetObject then
targetObject = obj
end
end
end
end
-- error handling
if not targetObject then
broadcastToColor("No tokens found!", playerColor, "Yellow")
return
end
-- handling for stacked tokens
if targetObject.getQuantity() > 1 then
targetObject = targetObject.takeObject()
end
-- feedback message
local tokenName = targetObject.getName()
if tokenName == "" then
if targetObject.memo ~= "" then
-- name handling for clue / doom
if targetObject.memo == "clueDoom" then
if targetObject.is_face_down then
tokenName = "Doom"
else
tokenName = "Clue"
end
else
tokenName = titleCase(targetObject.memo)
end
else
tokenName = "Unknown"
end
end
local playerName = Player[playerColor].steam_name
broadcastToAll(playerName .. " removed a token: " .. tokenName, playerColor)
local discardForMatColor = getColorToDiscardFor(hoveredObject, playerColor)
playmatApi.discardListOfObjects(discardForMatColor, { targetObject })
end
-- takes a clue from a location, player needs to hover the clue directly or the location -- takes a clue from a location, player needs to hover the clue directly or the location
function takeClueFromLocation(playerColor, hoveredObject) function takeClueFromLocation(playerColor, hoveredObject)
local cardName, clue local cardName, clue
@ -58,7 +191,7 @@ function takeClueFromLocation(playerColor, hoveredObject)
if hoveredObject == nil then if hoveredObject == nil then
broadcastToColor("Hover a clue or card with clues and try again.", playerColor, "Yellow") broadcastToColor("Hover a clue or card with clues and try again.", playerColor, "Yellow")
return return
elseif hoveredObject.tag == "Card" then elseif hoveredObject.type == "Card" then
cardName = hoveredObject.getName() cardName = hoveredObject.getName()
for _, v in ipairs(searchOnObj(hoveredObject)) do for _, v in ipairs(searchOnObj(hoveredObject)) do
@ -91,7 +224,7 @@ function takeClueFromLocation(playerColor, hoveredObject)
for _, v in ipairs(search) do for _, v in ipairs(search) do
local obj = v.hit_object local obj = v.hit_object
if obj.tag == "Card" then if obj.type == "Card" then
cardName = obj.getName() cardName = obj.getName()
break break
end end
@ -152,3 +285,20 @@ function searchOnObj(obj)
origin = obj.getPosition() origin = obj.getPosition()
}) })
end end
-- Simple method to check if the given point is in a specified area
---@param point Vector Point to check, only x and z values are relevant
---@param bounds Table Defined area to see if the point is within
function inArea(point, bounds)
return (point.x > bounds.minX
and point.x < bounds.maxX
and point.z > bounds.minZ
and point.z < bounds.maxZ)
end
-- capitalizes the first letter
function titleCase(str)
local first = str:sub(1, 1)
local rest = str:sub(2)
return first:upper() .. rest:lower()
end

View File

@ -226,26 +226,23 @@ end
-- Discard buttons -- Discard buttons
--------------------------------------------------------- ---------------------------------------------------------
-- builds a function that discards things in searchPosition -- handles discarding for a list of objects
-- stuff on the card/deck will be put into the local trashcan ---@param objList Table List of objects to discard
function makeDiscardHandlerFor(searchPosition) function discardListOfObjects(objList)
return function () for _, obj in ipairs(objList) do
local origin = self.positionToWorld(searchPosition) if isCardOrDeck(obj) then
for _, obj in ipairs(searchArea(origin, {2, 1, 3.2})) do if obj.hasTag("PlayerCard") then
if isCardOrDeck(obj) then placeOrMergeIntoDeck(obj, returnGlobalDiscardPosition(), self.getRotation())
if obj.hasTag("PlayerCard") then else
placeOrMergeIntoDeck(obj, returnGlobalDiscardPosition(), self.getRotation()) placeOrMergeIntoDeck(obj, ENCOUNTER_DISCARD_POSITION, {x = 0, y = -90, z = 0})
else
placeOrMergeIntoDeck(obj, ENCOUNTER_DISCARD_POSITION, {x = 0, y = -90, z = 0})
end
-- put chaos tokens back into bag (e.g. Unrelenting)
elseif tokenChecker.isChaosToken(obj) then
local chaosBag = chaosBagApi.findChaosBag()
chaosBag.putObject(obj)
-- don't touch the table or this playmat itself
elseif obj.guid ~= "4ee1f2" and obj ~= self then
ownedObjects.Trash.putObject(obj)
end end
-- put chaos tokens back into bag (e.g. Unrelenting)
elseif tokenChecker.isChaosToken(obj) then
local chaosBag = chaosBagApi.findChaosBag()
chaosBag.putObject(obj)
-- don't touch locked objects (like the table etc.)
elseif not obj.getLock() then
ownedObjects.Trash.putObject(obj)
end end
end end
end end
@ -282,7 +279,7 @@ function placeOrMergeIntoDeck(obj, pos, rot)
function() function()
obj.use_hands = true obj.use_hands = true
-- this avoids a TTS bug that merges unrelated cards that are not resting -- this avoids a TTS bug that merges unrelated cards that are not resting
if #searchResult == 1 then if #searchResult == 1 and searchResult[1] ~= obj then
-- call this with avoiding errors (physics is sometimes too fast so the object doesn't exist for the put) -- call this with avoiding errors (physics is sometimes too fast so the object doesn't exist for the put)
pcall(function() searchResult[1].putObject(obj) end) pcall(function() searchResult[1].putObject(obj) end)
end end
@ -294,9 +291,13 @@ end
function makeDiscardButton(xValue, number) function makeDiscardButton(xValue, number)
local position = { xValue, 0.1, -0.94} local position = { xValue, 0.1, -0.94}
local searchPosition = {-position[1], position[2], position[3] + 0.32} local searchPosition = {-position[1], position[2], position[3] + 0.32}
local handler = makeDiscardHandlerFor(searchPosition)
local handlerName = 'handler' .. number local handlerName = 'handler' .. number
self.setVar(handlerName, handler) self.setVar(handlerName, function()
local cardSizeSearch = {2, 1, 3.2}
local globalSearchPosition = self.positionToWorld(searchPosition)
local searchResult = searchArea(globalSearchPosition, cardSizeSearch)
return discardListOfObjects(searchResult)
end)
self.createButton({ self.createButton({
label = "Discard", label = "Discard",
click_function = handlerName, click_function = handlerName,

View File

@ -97,6 +97,15 @@ do
end end
end end
-- Handles discarding for the requested playmat for the provided list of objects
---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All")
---@param objList Table List of objects to discard
PlaymatApi.discardListOfObjects = function(matColor, objList)
for _, mat in pairs(getMatForColor(matColor)) do
mat.call("discardListOfObjects", objList)
end
end
-- Returns the active investigator id -- Returns the active investigator id
---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All") ---@param matColor String Color of the playmat - White, Orange, Green or Red (does not support "All")
PlaymatApi.returnInvestigatorId = function(matColor) PlaymatApi.returnInvestigatorId = function(matColor)