Merge branch 'utility-file' into custom-card-importing

This commit is contained in:
Chr1Z93 2023-12-18 15:26:55 +01:00
commit 85526429f0
21 changed files with 199 additions and 474 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
end
function getClues()
return searchLib.inArea(self.getPosition(), self.getRotation(), { 2, 1, 2 }, "isClue")
end

View File

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

View File

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

66
src/util/SearchLib.ttslua Normal file
View File

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

View File

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