diff --git a/src/accessories/AttachmentHelper.ttslua b/src/accessories/AttachmentHelper.ttslua index 2f8fcb13..4b2345ed 100644 --- a/src/accessories/AttachmentHelper.ttslua +++ b/src/accessories/AttachmentHelper.ttslua @@ -1,3 +1,4 @@ +local searchLib = require("util/SearchLib") local fontColor local BACKGROUNDS = { { @@ -112,25 +113,11 @@ end -- attempt to load image from below card when dropped function onDrop(playerColor) local pos = self.getPosition():setAt("y", 2) - local search = Physics.cast({ - direction = { 0, -1, 0 }, - max_distance = 2, - type = 3, - size = { 0.1, 0.1, 0.1 }, - origin = pos - }) + local searchResult = searchLib.belowPosition(pos, "isCard") + if #searchResult == 0 then return end + local syncName = searchResult[1].getName() - local syncName - for _, v in ipairs(search) do - if v.hit_object.tag == "Card" then - syncName = v.hit_object.getName() - break - end - end - - if not syncName then return end - - -- remove level information fron syncName + -- remove level information from syncName syncName = syncName:gsub("%s%(%d%)", "") -- loop through background table @@ -170,9 +157,9 @@ end -- only allow cards to enter, split decks and reject other objects function onObjectEnterContainer(container, object) if container ~= self then return end - if object.tag == "Deck" then + if object.type == "Deck" then takeDeckOut(object.getGUID(), self.getPosition() + Vector(0, 0.1, 0)) - elseif object.tag ~= "Card" then + elseif object.type ~= "Card" then broadcastToAll("The 'Attachment Helper' is meant to be used for cards.", "White") else findCard(object.getGUID(), object.getName(), object.getGMNotes()) diff --git a/src/accessories/CleanUpHelper.ttslua b/src/accessories/CleanUpHelper.ttslua index 05a52741..9c146992 100644 --- a/src/accessories/CleanUpHelper.ttslua +++ b/src/accessories/CleanUpHelper.ttslua @@ -8,6 +8,7 @@ local chaosBagApi = require("chaosbag/ChaosBagApi") local guidReferenceApi = require("core/GUIDReferenceApi") local playAreaApi = require("core/PlayAreaApi") local playmatApi = require("playermat/PlaymatApi") +local searchLib = require("util/SearchLib") local soundCubeApi = require("core/SoundCubeApi") local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") @@ -284,7 +285,7 @@ function tidyPlayerMatCoroutine() if i < 5 then objList = playmatApi.searchAroundPlaymat(COLORS[i]) else - objList = searchMythosArea() + objList = searchLib.inArea({ -2, 2, 10 }, { 0, 270, 0 }, { 55, 1, 13.5 }) end for _, obj in ipairs(objList) do @@ -321,26 +322,3 @@ function tidyPlayerMatCoroutine() printToAll("Clean up completed!", "Green") return 1 end - ---------------------------------------------------------- --- helper functions ---------------------------------------------------------- - --- find objects in the mythos area -function searchMythosArea() - local searchResult = Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 1, - type = 3, - size = { 55, 1, 13.5 }, - origin = { -2, 2, 10 }, - orientation = { 0, 270, 0 }, - debug = false - }) - - local objList = {} - for _, v in ipairs(searchResult) do - table.insert(objList, v.hit_object) - end - return objList -end diff --git a/src/accessories/PlayermatHider.ttslua b/src/accessories/PlayermatHider.ttslua index e01f1ccb..42e7532c 100644 --- a/src/accessories/PlayermatHider.ttslua +++ b/src/accessories/PlayermatHider.ttslua @@ -1,9 +1,9 @@ local guidReferenceApi = require("core/GUIDReferenceApi") -local objects +local playmatApi = require("playermat/PlaymatApi") function onClick_hideShow(player, matColor) - objects = guidReferenceApi.getObjectsByOwner(matColor) - local actionTokens = searchMat(objects.Playermat.positionToWorld({-1.1, 0.05, -0.27}), {4, 1, 1}, isActionToken) + local objects = guidReferenceApi.getObjectsByOwner(matColor) + local actionTokens = playmatApi.searchAroundPlaymat(matColor, "isActionToken") local pos = objects.Playermat.getPosition() local mod = (pos.y > 0) and -2 or 2 @@ -18,24 +18,3 @@ function onClick_hideShow(player, matColor) obj.setPosition(obj.getPosition() + Vector(0, mod, 0)) end end - -function isActionToken(x) return x.getDescription() == 'Action Token' end - -function searchMat(origin, size, filter) - local searchResult = Physics.cast({ - origin = origin, - direction = { 0, 1, 0 }, - orientation = objects.Playermat.getRotation(), - type = 3, - size = size, - max_distance = 0 - }) - - local objList = {} - for _, v in ipairs(searchResult) do - if not filter or (filter and filter(v.hit_object)) then - table.insert(objList, v.hit_object) - end - end - return objList -end diff --git a/src/accessories/SearchAssistant.ttslua b/src/accessories/SearchAssistant.ttslua index 8cd9ef67..8a742ebf 100644 --- a/src/accessories/SearchAssistant.ttslua +++ b/src/accessories/SearchAssistant.ttslua @@ -1,3 +1,4 @@ +local searchLib = require("util/SearchLib") local playmatApi = require("playermat/PlaymatApi") -- forward declaration of variables that are used across functions @@ -159,17 +160,12 @@ function endSearch(_, _, isRightClick) end -- draw set aside cards (from the ground!) - for _, v in ipairs(searchArea(setAsidePosition)) do - local obj = v.hit_object + for _, obj in ipairs(searchLib.atPosition(setAsidePosition), "isCardOrDeck") do if obj.type == "Deck" then - Wait.time(function() - obj.deal(#obj.getObjects(), handColor) - end, 1) - break + Wait.time(function() obj.deal(#obj.getObjects(), handColor) end, 1) elseif obj.type == "Card" then obj.setPosition(Player[handColor].getHandTransform().position) obj.flip() - break end end @@ -190,14 +186,3 @@ function endSearch(_, _, isRightClick) Wait.time(function() playmatApi.flipTopCardFromDeck(matColor) end, #handCards * 0.1) end end - --- utility function -function searchArea(position) - return Physics.cast({ - origin = position, - direction = { 0, 1, 0 }, - type = 3, - size = { 2, 2, 2 }, - max_distance = 0 - }) -end diff --git a/src/accessories/Subject5U-21Helper.ttslua b/src/accessories/Subject5U-21Helper.ttslua index 7c01d914..3510f9b0 100644 --- a/src/accessories/Subject5U-21Helper.ttslua +++ b/src/accessories/Subject5U-21Helper.ttslua @@ -1,3 +1,5 @@ +local searchLib = require("util/SearchLib") + local classOrder = { "Guardian", "Seeker", @@ -75,17 +77,8 @@ function updateDisplayButtons(_, playerColor) end function getNotesFromCardsAndContainers() - local search = Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 0, - type = 3, - size = self.getBounds().size:setAt("y", 1), - origin = self.getPosition() + Vector(0, 0.5, 0), - }) - local notesList = {} - for _, hit in ipairs(search) do - local obj = hit.hit_object + for _, obj in ipairs(searchLib.onObject(self)) do local notes = {} if obj.type == "Card" then notes = JSON.decode(obj.getGMNotes()) or {} diff --git a/src/accessories/UnderworldMarketHelper.ttslua b/src/accessories/UnderworldMarketHelper.ttslua index af9fbce2..53af7d07 100644 --- a/src/accessories/UnderworldMarketHelper.ttslua +++ b/src/accessories/UnderworldMarketHelper.ttslua @@ -1,3 +1,5 @@ +local searchLib = require("util/SearchLib") + function onload(saved_data) revealCardPositions = { Vector(3.5, 0.25, 0), @@ -176,17 +178,8 @@ function getRevealedCards() local revealedCards = {} for _, pos in ipairs(revealCardPositions) do - local hitList = Physics.cast({ - origin = self.positionToWorld(pos) + Vector(0, 0.25, 0), - direction = {0,-1,0}, - type = 1, - max_distance = 2 - }) - - for _, hit in ipairs(hitList) do - if hit.hit_object != self and hit.hit_object.tag == "Card" then - table.insert(revealedCards, hit.hit_object.getGUID()) - end + for _, obj in ipairs(searchLib.atPosition(self.positionToWorld(pos), "isCard")) do + table.insert(revealedCards, obj.getGUID()) end end diff --git a/src/core/DoomCounter.ttslua b/src/core/DoomCounter.ttslua index 05be6ba1..be99c5f3 100644 --- a/src/core/DoomCounter.ttslua +++ b/src/core/DoomCounter.ttslua @@ -1,3 +1,4 @@ +local searchLib = require("util/SearchLib") local guidReferenceApi = require("core/GUIDReferenceApi") local playAreaApi = require("core/PlayAreaApi") @@ -62,13 +63,12 @@ function updateVal(number) broadcastDoom(val) end ---called by updateVal and addVal to broadcast total doom in play, including doom threshold +-- called by updateVal and addVal to broadcast total doom in play, including doom threshold function broadcastDoom(val) - if val ~= 0 then local doomInPlayCounter = guidReferenceApi.getObjectByOwnerAndType("Mythos", "DoomInPlayCounter") - otherDoom = doomInPlayCounter.call("countDoom") - totalDoomThreshold = getDoomThreshold() + local otherDoom = doomInPlayCounter.call("countDoom") + local totalDoomThreshold = getDoomThreshold() if totalDoomThreshold ~= nil then broadcastToAll(val .. " doom on the agenda (" .. otherDoom + val .. "/" .. totalDoomThreshold .. " in play)", "White") @@ -91,13 +91,10 @@ function startReset() end end --- get Doom Threshold from top card of Agenda deck +-- get doom threshold from top card of Agenda deck function getDoomThreshold() - - local origin = { -2.72, 1.6, 0.37 } -- this needs to be edited to the actual coordinates of the agenda deck - local size = { 0.1, 0.1, 0.1 } -- very slim area, basically a single raycast - local searchResult = searchArea(origin, size, isCardOrDeck) - + local agendaPos = { -2.72, 1.6, 0.37 } + local searchResult = searchLib.atPosition(agendaPos, "isCardOrDeck") local metadata = {} if #searchResult == 0 then -- handle no agenda found @@ -110,32 +107,27 @@ function getDoomThreshold() if metadata == nil then return nil else - if(metadata.doomThresholdPerInvestigator) then - + if metadata.doomThresholdPerInvestigator then return metadata.doomThresholdPerInvestigator*playAreaApi.getInvestigatorCount() + metadata.doomThreshold else - return metadata.doomThreshold + return metadata.doomThreshold end end - else -- handle agenda deck - local cardData = searchResult[1].getData().ContainedObjects local topCardData = cardData[#cardData] metadata = JSON.decode(topCardData.GMNotes) if metadata == nil then return nil else - if(metadata.doomThresholdPerInvestigator) then + if metadata.doomThresholdPerInvestigator then return metadata.doomThresholdPerInvestigator*playAreaApi.getInvestigatorCount() + metadata.doomThreshold else return metadata.doomThreshold end end - end - else -- handle multiple cards / decks found return nil @@ -157,28 +149,3 @@ function toggleOptions() self.UI.hide("Options") end end - --- searches an area and optionally filters the result -function searchArea(origin, size, filter) - local searchResult = Physics.cast({ - origin = origin, - direction = { 0, 1, 0 }, - orientation = self.getRotation(), - type = 3, - size = size, - max_distance = 0 - }) - - local objList = {} - for _, v in ipairs(searchResult) do - if not filter or (filter and filter(v.hit_object)) then - table.insert(objList, v.hit_object) - end - end - return objList -end - --- filter functions for searchArea() -function isCard(x) return x.type == 'Card' end -function isDeck(x) return x.type == 'Deck' end -function isCardOrDeck(x) return x.type == 'Card' or x.type == 'Deck' end diff --git a/src/core/GameKeyHandler.ttslua b/src/core/GameKeyHandler.ttslua index d713e720..6766a828 100644 --- a/src/core/GameKeyHandler.ttslua +++ b/src/core/GameKeyHandler.ttslua @@ -2,6 +2,7 @@ local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi") local guidReferenceApi = require("core/GUIDReferenceApi") local optionPanelApi = require("core/OptionPanelApi") local playmatApi = require("playermat/PlaymatApi") +local searchLib = require("util/SearchLib") local victoryDisplayApi = require("core/VictoryDisplayApi") function onLoad() @@ -69,10 +70,8 @@ function discardObject(playerColor, 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 + for _, obj in ipairs(searchLib.onObject(hoveredObject), "isTileOrToken") do + table.insert(discardTheseObjects, obj) end end @@ -151,9 +150,8 @@ function removeOneUse(playerColor, hoveredObject) 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 + for _, obj in ipairs(searchLib.onObject(hoveredObject), "isTileOrToken") do + if not obj.locked and obj.memo ~= "resourceCounter" then -- check for matching object, otherwise use the first hit if obj.memo == searchForType then targetObject = obj @@ -211,18 +209,13 @@ function takeClueFromLocation(playerColor, hoveredObject) return elseif hoveredObject.type == "Card" then cardName = hoveredObject.getName() + local searchResult = searchLib.onObject(hoveredObject, "isClue") - for _, v in ipairs(searchOnObj(hoveredObject)) do - local obj = v.hit_object - if obj.memo == "clueDoom" and obj.is_face_down == false then - clue = obj - break - end - end - - if clue == nil then + if #searchResult == 0 then broadcastToColor("This card does not have any clues on it.", playerColor, "Yellow") return + else + clue = searchResult[1] end elseif hoveredObject.memo == "clueDoom" then if hoveredObject.is_face_down then @@ -230,22 +223,10 @@ function takeClueFromLocation(playerColor, hoveredObject) return end - clue = hoveredObject - - local search = Physics.cast({ - direction = { 0, -1, 0 }, - max_distance = 0.1, - type = 3, - size = { 0.1, 0.1, 0.1 }, - origin = clue.getPosition() - }) + local searchResult = searchLib.belowPosition(hoveredObject.getPosition(), "isCard") - for _, v in ipairs(search) do - local obj = v.hit_object - if obj.type == "Card" then - cardName = obj.getName() - break - end + if #searchResult ~= 0 then + cardName = searchResult[1].getName() end else broadcastToColor("Hover a clue or card with clues and try again.", playerColor, "Yellow") @@ -292,18 +273,6 @@ function addWendysMenu(playerColor, hoveredObject) blessCurseManagerApi.addWendysMenu(playerColor, hoveredObject) end --- searches on an object (by using its bounds) ----@param obj Object Object to search on -function searchOnObj(obj) - return Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 0.5, - type = 3, - size = obj.getBounds().size, - origin = obj.getPosition() - }) -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 @@ -319,4 +288,4 @@ function titleCase(str) local first = str:sub(1, 1) local rest = str:sub(2) return first:upper() .. rest:lower() -end \ No newline at end of file +end diff --git a/src/core/Global.ttslua b/src/core/Global.ttslua index a8ad385e..22a2ff1a 100644 --- a/src/core/Global.ttslua +++ b/src/core/Global.ttslua @@ -4,6 +4,7 @@ local mythosAreaApi = require("core/MythosAreaApi") local navigationOverlayApi = require("core/NavigationOverlayApi") local playAreaApi = require("core/PlayAreaApi") local playmatApi = require("playermat/PlaymatApi") +local searchLib = require("util/SearchLib") local soundCubeApi = require("core/SoundCubeApi") local tokenArrangerApi = require("accessories/TokenArrangerApi") local tokenChecker = require("core/token/TokenChecker") @@ -1101,15 +1102,10 @@ end -- checks whether something is in the specified position -- returns true if empty function checkPositionForContentSpawn(checkPos) - local search = Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 0.1, - type = 3, - size = { 0.1, 0.1, 0.1 }, - origin = checkPos - }) + local searchResult = searchLib.atPosition(checkPos) + -- first hit is the table surface, additional hits means something is there - return #search == 1 + return #searchResult == 1 end -- downloading of the library file diff --git a/src/core/MythosArea.ttslua b/src/core/MythosArea.ttslua index 4245df02..2146741a 100644 --- a/src/core/MythosArea.ttslua +++ b/src/core/MythosArea.ttslua @@ -1,3 +1,4 @@ +local searchLib = require("util/SearchLib") local guidReferenceApi = require("core/GUIDReferenceApi") local playAreaApi = require("core/PlayAreaApi") local tokenArrangerApi = require("accessories/TokenArrangerApi") @@ -129,18 +130,17 @@ end -- gets the encounter deck (for internal functions and Api calls) function getEncounterDeck() - local search = searchArea(ENCOUNTER_DECK_POS, { 3, 1, 4 }, isCardOrDeck) + local searchResult = searchLib.atPosition(ENCOUNTER_DECK_POS, "isCardOrDeck") - for _, v in ipairs(search) do - local obj = v.hit_object + for _, obj in ipairs(searchResult) do if obj.type == 'Deck' then return obj end end -- if no deck was found, return the first hit (a card) - if #search > 0 then - return search[1].hit_object + if #searchResult > 0 then + return searchResult[1] end end @@ -180,9 +180,9 @@ function reshuffleEncounterDeck(params) isReshuffling = true -- shuffle and flip deck, draw card after completion - local discarded = searchArea(ENCOUNTER_DISCARD_POSITION, { 3, 1, 4 }, isDeck) - if #discarded > 0 then - local deck = discarded[1].hit_object + local searchResult = searchLib.atPosition(ENCOUNTER_DISCARD_POSITION, "isCardOrDeck") + if #searchResult > 0 then + local deck = searchResult[1] if not deck.is_face_down then deck.flip() end deck.shuffle() deck.setPositionSmooth(Vector(ENCOUNTER_DECK_POS) + Vector(0, 2, 0), false, true) @@ -213,8 +213,7 @@ end -- removes tokens from the provided card/deck function removeTokensFromObject(object) - for _, v in ipairs(searchArea(object.getPosition(), { 3, 1, 4 })) do - local obj = v.hit_object + for _, obj in ipairs(searchLib.onObject(object)) do if obj.getGUID() ~= "4ee1f2" and -- table obj ~= self and obj.type ~= "Deck" and @@ -226,32 +225,3 @@ function removeTokensFromObject(object) end end end - --- searches an area and optionally filters the result -function searchArea(origin, size, filter) - local objList = Physics.cast({ - origin = origin, - direction = { 0, 1, 0 }, - orientation = self.getRotation(), - type = 3, - size = size, - max_distance = 1 - }) - - if filter then - local filteredList = {} - for _, obj in ipairs(objList) do - if filter(obj.hit_object) then - table.insert(filteredList, obj) - end - end - return filteredList - else - return objList - end -end - --- filter functions for searchArea -function isDeck(x) return x.tag == 'Deck' end - -function isCardOrDeck(x) return x.tag == 'Card' or x.tag == 'Deck' end diff --git a/src/core/NavigationOverlayHandler.ttslua b/src/core/NavigationOverlayHandler.ttslua index ce94cb6e..6283a815 100644 --- a/src/core/NavigationOverlayHandler.ttslua +++ b/src/core/NavigationOverlayHandler.ttslua @@ -260,9 +260,6 @@ function getDynamicViewBounds(objList) } for _, obj in pairs(objList) do - -- handling for Physics.cast() results - if not obj.type then obj = obj.hit_object end - if not obj.hasTag("CameraZoom_ignore") and not obj.hasTag("CampaignLog") then count = count + 1 local bounds = obj.getBounds() diff --git a/src/core/PlayArea.ttslua b/src/core/PlayArea.ttslua index bf1c578c..e01e811c 100644 --- a/src/core/PlayArea.ttslua +++ b/src/core/PlayArea.ttslua @@ -1,6 +1,6 @@ ---------------------------------------------------------- --- general setup ---------------------------------------------------------- +local guidReferenceApi = require("core/GUIDReferenceApi") +local searchLib = require("util/SearchLib") +local tokenManager = require("core/token/TokenManager") -- Location connection directional options local BIDIRECTIONAL = 0 @@ -25,8 +25,7 @@ local collisionEnabled = false -- used for recreating the link to a custom data helper after image change customDataHelper = nil -local DEFAULT_URL = -"http://cloud-3.steamusercontent.com/ugc/998015670465071049/FFAE162920D67CF38045EFBD3B85AD0F916147B2/" +local DEFAULT_URL = "http://cloud-3.steamusercontent.com/ugc/998015670465071049/FFAE162920D67CF38045EFBD3B85AD0F916147B2/" local SHIFT_OFFSETS = { left = { x = 0.00, y = 0, z = 7.67 }, @@ -44,8 +43,6 @@ local LOC_LINK_EXCLUDE_SCENARIOS = { ["The Heart of Madness"] = true } -local guidReferenceApi = require("core/GUIDReferenceApi") -local tokenManager = require("core/token/TokenManager") local clueData = {} local spawnedLocationGUIDs = {} local locations = {} @@ -608,25 +605,8 @@ end -- checks if a card has clues on it, returns true if clues are on it ---@param card TTSObject Card to check for clues function cardHasClues(card) - for _, v in ipairs(searchOnObj(card)) do - local obj = v.hit_object - if obj.memo == "clueDoom" and obj.is_face_down == false then - return true - end - end - return false -end - --- searches on an object (by using its bounds) ----@param obj Object Object to search on -function searchOnObj(obj) - return Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 0.5, - type = 3, - size = obj.getBounds().size, - origin = obj.getPosition() - }) + local searchResult = searchLib.onObject(card, "isClue") + return #searchResult > 0 end -- highlights all locations in the play area without metadata diff --git a/src/core/VictoryDisplay.ttslua b/src/core/VictoryDisplay.ttslua index 404f8cdd..fdf8e582 100644 --- a/src/core/VictoryDisplay.ttslua +++ b/src/core/VictoryDisplay.ttslua @@ -1,12 +1,12 @@ -local chaosBagApi = require("chaosbag/ChaosBagApi") +local searchLib = require("util/SearchLib") +local chaosBagApi = require("chaosbag/ChaosBagApi") local guidReferenceApi = require("core/GUIDReferenceApi") -local playAreaApi = require("core/PlayAreaApi") -local tokenChecker = require("core/token/TokenChecker") +local playAreaApi = require("core/PlayAreaApi") +local tokenChecker = require("core/token/TokenChecker") -local pendingCall = false -local messageSent = {} -local missingData = {} -local countedVP = {} +local pendingCall = false +local missingData = {} +local countedVP = {} local highlightMissing = false local highlightCounted = false @@ -35,34 +35,23 @@ function onLoad() self.createButton(buttonParameters) -- index 3: highlighting button (missing data) - self.createButton({ - label = "!", - click_function = "highlightMissingData", - tooltip = "Enable highlighting of cards without metadata (VP on these is not counted).", - function_owner = self, - scale = { 0.15, 0.15, 0.15 }, - color = { 1, 0, 0 }, - width = 700, - height = 800, - font_size = 700, - font_color = { 1, 1, 1 }, - position = { x = 1.82, y = 0.06, z = -1.32 } - }) + buttonParameters.label = "!" + buttonParameters.click_function = "highlightMissingData" + buttonParameters.tooltip = "Enable highlighting of cards without metadata (VP on these is not counted)." + buttonParameters.color = { 1, 0, 0 } + buttonParameters.width = 700 + buttonParameters.height = 800 + buttonParameters.font_size = 700 + buttonParameters.position = { x = 1.82, y = 0.06, z = -1.32 } + self.createButton(buttonParameters) -- index 4: highlighting button (counted VP) - self.createButton({ - label = "?", - click_function = "highlightCountedVP", - tooltip = "Enable highlighting of cards with VP.", - function_owner = self, - scale = { 0.15, 0.15, 0.15 }, - color = { 0, 1, 0 }, - width = 700, - height = 800, - font_size = 700, - font_color = { 1, 1, 1 }, - position = { x = 1.5, y = 0.06, z = -1.32 } - }) + buttonParameters.label = "?" + buttonParameters.click_function = "highlightCountedVP" + buttonParameters.tooltip = "Enable highlighting of cards with VP." + buttonParameters.color = { 0, 1, 0 } + buttonParameters.position.x = 1.5 + self.createButton(buttonParameters) -- update the display label once Wait.time(updateCount, 1) @@ -147,19 +136,17 @@ function updateCount() victoryPoints.playArea = playAreaApi.countVP() -- count cards in victory display - for _, v in ipairs(searchOnObj(self)) do - local obj = v.hit_object - + for _, obj in ipairs(searchLib.onObject(self), "isCardOrDeck") do -- check metadata for VP - if obj.tag == "Card" then + if obj.type == "Card" then local VP = getCardVP(obj, JSON.decode(obj.getGMNotes())) victoryPoints.display = victoryPoints.display + VP if VP > 0 then table.insert(countedVP, obj) end - -- handling for stacked cards - elseif obj.tag == "Deck" then + -- handling for stacked cards + elseif obj.type == "Deck" then local VP = 0 for _, deepObj in ipairs(obj.getObjects()) do local deepVP = getCardVP(obj, JSON.decode(deepObj.gm_notes)) @@ -212,8 +199,7 @@ end function highlightMissingData() self.editButton({ index = 3, - tooltip = (highlightMissing and "Enable" or "Disable") .. " highlighting of cards without metadata (VP on these is not counted)." - }) + tooltip = (highlightMissing and "Enable" or "Disable") .. " highlighting of cards without metadata (VP on these is not counted)." }) for _, obj in pairs(missingData) do if obj ~= nil then if highlightMissing then @@ -249,7 +235,7 @@ end -- places the provided card in the first empty spot function placeCard(card) local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash") - + -- check snap point states local snaps = self.getSnapPoints() table.sort(snaps, function(a, b) return a.position.x > b.position.x end) @@ -260,21 +246,15 @@ function placeCard(card) local positions = {} for i, snap in ipairs(snaps) do positions[i] = self.positionToWorld(snap.position) - local hits = checkSnapPointState(positions[i]) - - -- first hit is self, additional hits must be cards / decks - if #hits > 1 then - fullSlots[i] = true - end + local searchResult = searchLib.atPosition(positions[i], "isCardOrDeck") + fullSlots[i] = #searchResult > 0 end -- remove tokens from the card - for _, v in ipairs(searchOnObj(card)) do - local obj = v.hit_object - + for _, obj in ipairs(searchLib.onObject(card)) do -- don't touch decks / cards - if obj.tag == "Deck" or obj.tag == "Card" then - -- put chaos tokens back into bag + if obj.type == "Deck" or obj.type == "Card" then + -- put chaos tokens back into bag elseif tokenChecker.isChaosToken(obj) then local chaosBag = chaosBagApi.findChaosBag() chaosBag.putObject(obj) @@ -282,7 +262,7 @@ function placeCard(card) trash.putObject(obj) end end - + -- place the card local name = card.getName() or "Unnamed card" for i = 1, 10 do @@ -298,28 +278,3 @@ function placeCard(card) broadcastToAll("Victory Display is full! " .. name .. " placed into slot 1.", "Orange") card.setPositionSmooth(positions[1], false, true) end - ---------------------------------------------------------- --- utility functions ---------------------------------------------------------- - --- searches on an object -function searchOnObj(obj) - return Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 0.5, - type = 3, - size = obj.getBounds().size, - origin = obj.getPosition() - }) -end - -function checkSnapPointState(pos) - return Physics.cast({ - direction = { 0, 1, 0 }, - max_distance = 0.1, - type = 3, - size = { 0.1, 0.1, 0.1 }, - origin = pos - }) -end diff --git a/src/core/token/TokenManager.ttslua b/src/core/token/TokenManager.ttslua index a5cae52a..e70ce8d7 100644 --- a/src/core/token/TokenManager.ttslua +++ b/src/core/token/TokenManager.ttslua @@ -2,6 +2,7 @@ do local guidReferenceApi = require("core/GUIDReferenceApi") local optionPanelApi = require("core/OptionPanelApi") local playAreaApi = require("core/PlayAreaApi") + local searchLib = require("util/SearchLib") local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi") local PLAYER_CARD_TOKEN_OFFSETS = { @@ -487,12 +488,10 @@ do if mat.positionToLocal(cardPos).x < -1 then return end -- get current amount of resource tokens on the card - local search = internal.searchOnCard(cardPos, card.getRotation()) local clickableResourceCounter = nil local foundTokens = 0 - for _, obj in ipairs(search) do - local obj = obj.hit_object + for _, obj in ipairs(searchLib.onObject(card), "isTileOrToken") do local memo = obj.getMemo() if (stateTable[memo] or 0) > 0 then @@ -524,20 +523,5 @@ do end end - -- searches on a card (standard size) and returns the result - ---@param position Table Position of the card - ---@param rotation Table Rotation of the card - internal.searchOnCard = function(position, rotation) - return Physics.cast({ - origin = position, - direction = {0, 1, 0}, - orientation = rotation, - type = 3, - size = { 2.5, 0.5, 3.5 }, - max_distance = 1, - debug = false - }) - end - return TokenManager end diff --git a/src/playercards/cards/EmpiricalHypothesis.ttslua b/src/playercards/cards/EmpiricalHypothesis.ttslua index 1631c985..22455fca 100644 --- a/src/playercards/cards/EmpiricalHypothesis.ttslua +++ b/src/playercards/cards/EmpiricalHypothesis.ttslua @@ -1,4 +1,5 @@ --- this helper creates buttons to help the user track which hypothesis has been chosen each round (if user forgot to choose one at round start, the old one stays active) +-- this helper creates buttons to help the user track which hypothesis has been chosen each round +-- (if user forgot to choose one at round start, the old one stays active) local playmatApi = require("playermat/PlaymatApi") local upgradeSheetLibrary = require("playercards/customizable/UpgradeSheetLibrary") @@ -103,11 +104,10 @@ function createButtons() end function findUpgradeSheet() - matColor = playmatApi.getMatColorByPosition(self.getPosition()) - local result = playmatApi.searchAroundPlaymat(matColor, filter) + local matColor = playmatApi.getMatColorByPosition(self.getPosition()) + local result = playmatApi.searchAroundPlaymat(matColor, "isCard") for j, card in ipairs(result) do local metadata = JSON.decode(card.getGMNotes()) or {} - if metadata.id == "09041-c" then return card end diff --git a/src/playercards/cards/FamilyInheritance.ttslua b/src/playercards/cards/FamilyInheritance.ttslua index b08559e9..e66b4b8e 100644 --- a/src/playercards/cards/FamilyInheritance.ttslua +++ b/src/playercards/cards/FamilyInheritance.ttslua @@ -1,4 +1,5 @@ local playmatApi = require("playermat/PlaymatApi") +local searchLib = require("util/SearchLib") local tokenManager = require("core/token/TokenManager") local clickableResourceCounter = nil @@ -14,8 +15,7 @@ function searchSelf() clickableResourceCounter = nil foundTokens = 0 - for _, obj in ipairs(searchArea(self.getPosition(), { 2.5, 0.5, 3.5 })) do - local obj = obj.hit_object + for _, obj in ipairs(searchLib.onObject(self), "isTileOrToken") do local image = obj.getCustomObject().image if image == "http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then foundTokens = foundTokens + math.abs(obj.getQuantity()) @@ -63,14 +63,3 @@ function loseAll(playerColor) end printToColor("Discarded " .. foundTokens .. " resource(s).", playerColor) end - -function searchArea(origin, size) - return Physics.cast({ - origin = origin, - direction = { 0, 1, 0 }, - orientation = PLAY_ZONE_ROTATION, - type = 3, - size = size, - max_distance = 1 - }) -end diff --git a/src/playermat/ClueCounter.ttslua b/src/playermat/ClueCounter.ttslua index cce78085..5bd1fef8 100644 --- a/src/playermat/ClueCounter.ttslua +++ b/src/playermat/ClueCounter.ttslua @@ -1,12 +1,4 @@ --- Table of items which can be counted in this Bowl --- Each entry has 2 things to enter --- a name (what is in the name field of that object) --- a value (how much it is worth) --- a number in the items description will override the number entry in this table -local validCountItemList = { - ["Clue"] = 1, - [""] = 1 -} +local searchLib = require("util/SearchLib") exposedValue = 0 function onLoad() @@ -20,49 +12,25 @@ function onLoad() font_color = { 0, 0, 0 }, font_size = 2000 }) - loopID = Wait.time(countItems, 1, -1) + loopID = Wait.time(countItems, 1.5, -1) end -- Activated once per second, counts items in bowls function countItems() local totalValue = 0 - for _, item in ipairs(findValidItemsInSphere()) do - local descValue = tonumber(item.getDescription()) - local stackMult = math.abs(item.getQuantity()) - -- Use value in description if available - if descValue ~= nil then - totalValue = totalValue + descValue * stackMult - else - -- Otherwise use the value in validCountItemList - totalValue = totalValue + validCountItemList[item.getName()] * stackMult - end + for _, item in ipairs(getClues()) do + totalValue = totalValue + math.abs(item.getQuantity()) end exposedValue = totalValue self.editButton({ index = 0, label = totalValue }) end -function findValidItemsInSphere() - local items = Physics.cast({ - origin = self.getPosition(), - direction = { 0, 1, 0 }, - type = 2, - max_distance = 0, - size = { 2, 2, 2 } - }) - - local validItemList = {} - for _, entry in ipairs(items) do - if entry.hit_object ~= self then - if validCountItemList[entry.hit_object.getName()] ~= nil then - table.insert(validItemList, entry.hit_object) - end - end - end - return validItemList -end - function removeAllClues(trash) - for _, obj in ipairs(findValidItemsInSphere()) do + for _, obj in ipairs(getClues()) do trash.putObject(obj) end -end \ No newline at end of file +end + +function getClues() + return searchLib.inArea(self.getPosition(), self.getRotation(), { 2, 1, 2 }, "isClue") +end diff --git a/src/playermat/Playmat.ttslua b/src/playermat/Playmat.ttslua index 655715d4..0d41779f 100644 --- a/src/playermat/Playmat.ttslua +++ b/src/playermat/Playmat.ttslua @@ -1,3 +1,4 @@ +local searchLib = require("util/SearchLib") local chaosBagApi = require("chaosbag/ChaosBagApi") local guidReferenceApi = require("core/GUIDReferenceApi") local mythosAreaApi = require("core/MythosAreaApi") @@ -5,9 +6,6 @@ local navigationOverlayApi = require("core/NavigationOverlayApi") local tokenChecker = require("core/token/TokenChecker") local tokenManager = require("core/token/TokenManager") --- set true to enable debug logging and show Physics.cast() -local DEBUG = false - -- we use this to turn off collision handling until onLoad() is complete local collisionEnabled = false @@ -20,7 +18,7 @@ local DISCARD_BUTTON_OFFSETS = {-1.365, -0.91, -0.455, 0, 0.455, 0.91} local SEARCH_AROUND_SELF_X_BUFFER = 8 --- defined areas for "inArea()" and "Physics.cast()" +-- defined areas for object searching local MAIN_PLAY_AREA = { upperLeft = { x = 1.98, @@ -101,7 +99,7 @@ function onSave() end function onLoad(saveState) - self.interactable = DEBUG + self.interactable = false -- get object references to owned objects ownedObjects = guidReferenceApi.getObjectsByOwner(matColor) @@ -159,29 +157,9 @@ end -- searches an area and optionally filters the result function searchArea(origin, size, filter) - local searchResult = Physics.cast({ - origin = origin, - direction = { 0, 1, 0 }, - orientation = self.getRotation(), - type = 3, - size = size, - max_distance = 0 - }) - - local objList = {} - for _, v in ipairs(searchResult) do - if not filter or (filter and filter(v.hit_object)) then - table.insert(objList, v.hit_object) - end - end - return objList + return searchLib.inArea(origin, self.getRotation(), size, filter) end --- filter functions for searchArea() -function isCard(x) return x.type == 'Card' end -function isDeck(x) return x.type == 'Deck' end -function isCardOrDeck(x) return x.type == 'Card' or x.type == 'Deck' end - -- finds all objects on the playmat and associated set aside zone. function searchAroundSelf(filter) local bounds = self.getBoundsNormalized() @@ -230,7 +208,7 @@ end ---@param objList Table List of objects to discard function discardListOfObjects(objList) for _, obj in ipairs(objList) do - if isCardOrDeck(obj) then + if obj.type == "Card" or obj.type == "Deck" then if obj.hasTag("PlayerCard") then placeOrMergeIntoDeck(obj, returnGlobalDiscardPosition(), self.getRotation()) else @@ -255,7 +233,7 @@ function placeOrMergeIntoDeck(obj, pos, rot) local offset = 0.5 -- search the new position for existing card/deck - local searchResult = searchArea(pos, { 1, 1, 1 }, isCardOrDeck) + local searchResult = searchArea(pos, { 1, 1, 1 }, "isCardOrDeck") -- get new position local newPos @@ -320,7 +298,6 @@ function doUpkeepFromHotkey(color) end function doUpkeep(_, clickedByColor, isRightClick) - -- right-click allow color changing if isRightClick then changeColor(clickedByColor) return @@ -480,7 +457,7 @@ end -- get the draw deck and discard pile objects and returns the references function getDeckAreaObjects() local deckAreaObjects = {} - for _, object in ipairs(searchDeckAndDiscardArea(isCardOrDeck)) do + for _, object in ipairs(searchDeckAndDiscardArea("isCardOrDeck")) do if self.positionToLocal(object.getPosition()).z > 0.5 then deckAreaObjects.discard = object -- Norman Withers handling @@ -613,7 +590,7 @@ end -- called when a checkbox is added or removed in-game (which should be rare), and is bounded by the -- number of customizable cards in play. function syncAllCustomizableCards() - for _, card in ipairs(searchAroundSelf(isCard)) do + for _, card in ipairs(searchAroundSelf("isCard")) do syncCustomizableMetadata(card) end end @@ -623,7 +600,7 @@ function syncCustomizableMetadata(card) if cardMetadata == nil or cardMetadata.customizations == nil then return end - for _, upgradeSheet in ipairs(searchAroundSelf(isCard)) do + for _, upgradeSheet in ipairs(searchAroundSelf("isCard")) do local upgradeSheetMetadata = JSON.decode(upgradeSheet.getGMNotes()) or { } if upgradeSheetMetadata.id == (cardMetadata.id .. "-c") then for i, customization in ipairs(cardMetadata.customizations) do @@ -660,7 +637,7 @@ function onCollisionEnter(collisionInfo) if not collisionEnabled then return end -- only continue for cards - if not isCard(object) then return end + if object.type ~= "Card" then return end -- detect if "Dream-Enhancing Serum" is placed if object.getName() == "Dream-Enhancing Serum" then isDES = true end @@ -715,7 +692,7 @@ function shouldSpawnTokens(card) end function onObjectEnterContainer(container, object) - if not isCard(object) then return end + if object.type ~= "Card" then return end local localCardPos = self.positionToLocal(object.getPosition()) if inArea(localCardPos, DECK_DISCARD_AREA) then @@ -726,7 +703,7 @@ end -- removes tokens from the provided card/deck function removeTokensFromObject(object) - for _, obj in ipairs(searchArea(object.getPosition(), { 3, 1, 4 })) do + for _, obj in ipairs(searchLib.onObject(object)) do if obj.getGUID() ~= "4ee1f2" and -- table obj ~= self and obj.type ~= "Deck" and diff --git a/src/playermat/PlaymatApi.ttslua b/src/playermat/PlaymatApi.ttslua index a405d589..8ac011e9 100644 --- a/src/playermat/PlaymatApi.ttslua +++ b/src/playermat/PlaymatApi.ttslua @@ -199,7 +199,7 @@ do -- finds all objects on the playmat and associated set aside zone and returns a table ---@param matColor String Color of the playmat - White, Orange, Green, Red or All - ---@param filter Function Optional filter function (return true for desired objects) + ---@param filter String Name of the filte function (see util/SearchLib) PlaymatApi.searchAroundPlaymat = function(matColor, filter) local objList = {} for _, mat in pairs(getMatForColor(matColor)) do diff --git a/src/util/SearchLib.ttslua b/src/util/SearchLib.ttslua new file mode 100644 index 00000000..13550179 --- /dev/null +++ b/src/util/SearchLib.ttslua @@ -0,0 +1,66 @@ +do + local SearchLib = {} + local filterFunctions = { + isActionToken = function(x) return x.getDescription() == "Action Token" end, + isCard = function(x) return x.type == "Card" end, + isDeck = function(x) return x.type == "Deck" end, + isCardOrDeck = function(x) return x.type == "Card" or x.type == "Deck" end, + isClue = function(x) return x.memo == "clueDoom" and x.is_face_down == false end, + isTileOrToken = function(x) return x.type == "Tile" end + } + + -- performs the actual search and returns a filtered list of object references + ---@param pos Table Global position + ---@param rot Table Global rotation + ---@param size Table Size + ---@param filter String Name of the filter function + ---@param direction Table Direction (positive is up) + ---@param maxDistance Number Distance for the cast + local function returnSearchResult(pos, rot, size, filter, direction, maxDistance) + if filter then filter = filterFunctions[filter] end + local searchResult = Physics.cast({ + origin = pos, + direction = direction or { 0, 1, 0 }, + orientation = rot or { 0, 0, 0 }, + type = 3, + size = size, + max_distance = maxDistance or 0 + }) + + -- filtering the result + local objList = {} + for _, v in ipairs(searchResult) do + if not filter or filter(v.hit_object) then + table.insert(objList, v.hit_object) + end + end + return objList + end + + -- searches the specified area + SearchLib.inArea = function(pos, rot, size, filter) + return returnSearchResult(pos, rot, size, filter) + end + + -- searches the area on an object + SearchLib.onObject = function(obj, filter) + pos = obj.getPosition() + size = obj.getBounds().size:setAt("y", 1) + return returnSearchResult(pos, _, size, filter) + end + + -- searches the specified position (a single point) + SearchLib.atPosition = function(pos, filter) + size = { 0.1, 2, 0.1 } + return returnSearchResult(pos, _, size, filter) + end + + -- searches below the specified position (downwards until y = 0) + SearchLib.belowPosition = function(pos, filter) + direction = { 0, -1, 0 } + maxDistance = pos.y + return returnSearchResult(pos, _, size, filter, direction, maxDistance) + end + + return SearchLib +end diff --git a/src/util/TokenSpawnTool.ttslua b/src/util/TokenSpawnTool.ttslua index 91193734..57f5dc12 100644 --- a/src/util/TokenSpawnTool.ttslua +++ b/src/util/TokenSpawnTool.ttslua @@ -1,3 +1,4 @@ +local searchLib = require("util/SearchLib") local tokenManager = require("core/token/TokenManager") local TOKEN_INDEX = {} TOKEN_INDEX[3] = "resourceCounter" @@ -31,19 +32,10 @@ function onScriptingButtonDown(index, playerColor) -- check for subtype of resource based on card below if tokenType == "resource" then - local search = Physics.cast({ - direction = { 0, -1, 0 }, - max_distance = 2, - type = 3, - size = { 0.1, 0.1, 0.1 }, - origin = position:setAt("y", 2) - }) - - for _, v in ipairs(search) do - if v.hit_object.tag == "Card" and not v.hit_object.is_face_down then - local metadata = JSON.decode(v.hit_object.getGMNotes()) or {} + for _, obj in ipairs(searchLib.belowPosition(position), "isCard") do + if not obj.is_face_down then + local metadata = JSON.decode(obj.getGMNotes()) or {} local uses = metadata.uses or {} - for _, useInfo in ipairs(uses) do if useInfo.token == "resource" then subType = useInfo.type