optimization

This commit is contained in:
Chr1Z93 2023-02-04 00:36:46 +01:00
parent 77d0339354
commit 8bef677eda
3 changed files with 142 additions and 94 deletions

View File

@ -141,13 +141,17 @@ function onObjectPickUp(player, object)
if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then
local pickedUpGuid = object.getGUID() local pickedUpGuid = object.getGUID()
local metadata = JSON.decode(object.getGMNotes()) or { } local metadata = JSON.decode(object.getGMNotes()) or { }
if (metadata.type == "Location") then if metadata.type == "Location" then
-- onCollisionExit sometimes comes 1 frame after onObjectPickUp (rather than before it or in -- onCollisionExit sometimes comes 1 frame after onObjectPickUp (rather than before it or in
-- the same frame). This causes a mismatch in the data between dragging the on-table, and -- the same frame). This causes a mismatch in the data between dragging the on-table, and
-- that one frame draws connectors on the card which then show up as shadows for snap points. -- that one frame draws connectors on the card which then show up as shadows for snap points.
-- Waiting ensures we always do thing in the expected Exit->PickUp order -- Waiting ensures we always do thing in the expected Exit->PickUp order
Wait.frames(function() Wait.frames(function()
draggingGuids[pickedUpGuid] = metadata if object.is_face_down then
draggingGuids[pickedUpGuid] = metadata.locationBack
else
draggingGuids[pickedUpGuid] = metadata.locationFront
end
rebuildConnectionList() rebuildConnectionList()
end, 2) end, 2)
end end
@ -187,12 +191,20 @@ end
function maybeTrackLocation(card) function maybeTrackLocation(card)
-- Collision checks for any part of the card overlap, but our other tracking is centerpoint -- Collision checks for any part of the card overlap, but our other tracking is centerpoint
-- Ignore any collision where the centerpoint isn't in the area -- Ignore any collision where the centerpoint isn't in the area
if showLocationLinks() and isInPlayArea(card) then if isInPlayArea(card) then
local metadata = JSON.decode(card.getGMNotes()) or { } local metadata = JSON.decode(card.getGMNotes()) or { }
if metadata.type == "Location" then if metadata.type == "Location" then
locations[card.getGUID()] = metadata if card.is_face_down then
rebuildConnectionList() locations[card.getGUID()] = metadata.locationBack
drawBaseConnections() else
locations[card.getGUID()] = metadata.locationFront
end
-- only draw connection lines for not-excluded scenarios
if showLocationLinks() then
rebuildConnectionList()
drawBaseConnections()
end
end end
end end
end end
@ -238,20 +250,20 @@ function rebuildConnectionList()
-- Build a list of cards with each icon as their location ID -- Build a list of cards with each icon as their location ID
for cardId, metadata in pairs(draggingGuids) do for cardId, metadata in pairs(draggingGuids) do
buildLocListByIcon(cardId, iconCardList) buildLocListByIcon(cardId, iconCardList, metadata)
end end
for cardId, metadata in pairs(locations) do for cardId, metadata in pairs(locations) do
buildLocListByIcon(cardId, iconCardList) buildLocListByIcon(cardId, iconCardList, metadata)
end end
-- Pair up all the icons -- Pair up all the icons
locationConnections = { } locationConnections = { }
for cardId, metadata in pairs(draggingGuids) do for cardId, metadata in pairs(draggingGuids) do
buildConnection(cardId, iconCardList) buildConnection(cardId, iconCardList, metadata)
end end
for cardId, metadata in pairs(locations) do for cardId, metadata in pairs(locations) do
if draggingGuids[cardId] == nil then if draggingGuids[cardId] == nil then
buildConnection(cardId, iconCardList) buildConnection(cardId, iconCardList, metadata)
end end
end end
end end
@ -259,15 +271,14 @@ end
-- Extracts the card's icon string into a list of individual location icons -- Extracts the card's icon string into a list of individual location icons
---@param cardID String GUID of the card to pull the icon data from ---@param cardID String GUID of the card to pull the icon data from
---@param iconCardList Table A table of icon->GUID list. Mutable, will be updated by this method ---@param iconCardList Table A table of icon->GUID list. Mutable, will be updated by this method
function buildLocListByIcon(cardId, iconCardList) ---@param locData Table A table containing the metadata for the card (for the correct side)
local card = getObjectFromGUID(cardId) function buildLocListByIcon(cardId, iconCardList, locData)
local locData = getLocationData(card)
if locData ~= nil and locData.icons ~= nil then if locData ~= nil and locData.icons ~= nil then
for icon in string.gmatch(locData.icons, "%a+") do for icon in string.gmatch(locData.icons, "%a+") do
if iconCardList[icon] == nil then if iconCardList[icon] == nil then
iconCardList[icon] = { } iconCardList[icon] = { }
end end
table.insert(iconCardList[icon], card.getGUID()) table.insert(iconCardList[icon], cardId)
end end
end end
end end
@ -276,25 +287,24 @@ end
-- Playarea's locationConnections table. -- Playarea's locationConnections table.
---@param cardId String GUID of the card to build the connections for ---@param cardId String GUID of the card to build the connections for
---@param iconCardList Table A table of icon->GUID List. Used to find matching icons for connections. ---@param iconCardList Table A table of icon->GUID List. Used to find matching icons for connections.
function buildConnection(cardId, iconCardList) ---@param locData Table A table containing the metadata for the card (for the correct side)
local card = getObjectFromGUID(cardId) function buildConnection(cardId, iconCardList, locData)
local locData = getLocationData(card)
if locData ~= nil and locData.connections ~= nil then if locData ~= nil and locData.connections ~= nil then
locationConnections[card.getGUID()] = { } locationConnections[cardId] = { }
for icon in string.gmatch(locData.connections, "%a+") do for icon in string.gmatch(locData.connections, "%a+") do
if iconCardList[icon] ~= nil then if iconCardList[icon] ~= nil then
for _, connectedGuid in ipairs(iconCardList[icon]) do for _, connectedGuid in ipairs(iconCardList[icon]) do
-- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way -- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way
if locationConnections[connectedGuid] ~= nil if locationConnections[connectedGuid] ~= nil
and locationConnections[connectedGuid][card.getGUID()] == ONE_WAY then and locationConnections[connectedGuid][cardId] == ONE_WAY then
locationConnections[connectedGuid][card.getGUID()] = BIDIRECTIONAL locationConnections[connectedGuid][cardId] = BIDIRECTIONAL
locationConnections[card.getGUID()][connectedGuid] = nil locationConnections[cardId][connectedGuid] = nil
else else
if locationConnections[connectedGuid] == nil then if locationConnections[connectedGuid] == nil then
locationConnections[connectedGuid] = { } locationConnections[connectedGuid] = { }
end end
locationConnections[card.getGUID()][connectedGuid] = ONE_WAY locationConnections[cardId][connectedGuid] = ONE_WAY
locationConnections[connectedGuid][card.getGUID()] = INCOMING_ONE_WAY locationConnections[connectedGuid][cardId] = INCOMING_ONE_WAY
end end
end end
end end
@ -302,22 +312,6 @@ function buildConnection(cardId, iconCardList)
end end
end end
-- Helper method to extract the location metadata from a card based on whether it's front or back
-- is showing.
---@param card String Card object to extract data from
---@return. Table with either the locationFront or locationBack metadata structure, or nil if the
-- metadata doesn't exist
function getLocationData(card)
if card == nil then
return nil
end
if card.is_face_down then
return JSON.decode(card.getGMNotes()).locationBack
else
return JSON.decode(card.getGMNotes()).locationFront
end
end
-- Draws the lines for connections currently in locationConnections but not in draggingGuids. -- Draws the lines for connections currently in locationConnections but not in draggingGuids.
-- Constructed vectors will be set to the playmat -- Constructed vectors will be set to the playmat
function drawBaseConnections() function drawBaseConnections()
@ -552,6 +546,48 @@ function setLimitSnapsByType(matchTypes)
self.setSnapPoints(snaps) self.setSnapPoints(snaps)
end end
-- count victory points on locations in play area
---@return. Returns the total amount of VP found in the play area
function countVP()
local totalVP = 0
for cardId, metadata in pairs(locations) do
if metadata ~= nil then
local cardVP = tonumber(metadata.victory) or 0
if cardVP ~= 0 and not cardHasClues(cardId) then
totalVP = totalVP + cardVP
end
end
end
return totalVP
end
-- checks if a card has clues on it, returns true if clues are on it
---@param cardId String GUID of the card to check for clues
function cardHasClues(cardId)
local card = getObjectFromGUID(cardId)
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()
})
end
-- rebuilds local snap points (could be useful in the future again) -- rebuilds local snap points (could be useful in the future again)
function buildSnaps() function buildSnaps()
local upperleft = { x = 1.53, z = -1.09} local upperleft = { x = 1.53, z = -1.09}

View File

@ -55,6 +55,11 @@ do
{ container = container, object = object }) { container = container, object = object })
end end
-- counts the VP on locations in the play area
PlayAreaApi.countVP = function()
return getObjectFromGUID(PLAY_AREA_GUID).call("countVP")
end
-- Checks if an object is in the play area (returns true or false) -- Checks if an object is in the play area (returns true or false)
PlayAreaApi.isInPlayArea = function(object) PlayAreaApi.isInPlayArea = function(object)
return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object) return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object)

View File

@ -1,11 +1,8 @@
local playAreaApi = require("core/PlayAreaApi") local playAreaApi = require("core/PlayAreaApi")
local pendingCall = false local pendingCall = false
local messageSent = {} local messageSent = {}
local victoryPoints = {
display = 0,
playArea = 0
}
-- button creation when loading the game
function onLoad() function onLoad()
-- index 0: VP - "Display" -- index 0: VP - "Display"
local buttonParameters = {} local buttonParameters = {}
@ -17,7 +14,7 @@ function onLoad()
buttonParameters.height = 0 buttonParameters.height = 0
buttonParameters.font_size = 600 buttonParameters.font_size = 600
buttonParameters.font_color = { 1, 1, 1 } buttonParameters.font_color = { 1, 1, 1 }
buttonParameters.position = { x = -0.71, y = 0.06, z = -0.69 } buttonParameters.position = { x = -0.72, y = 0.06, z = -0.69 }
self.createButton(buttonParameters) self.createButton(buttonParameters)
-- index 1: VP - "Play Area" -- index 1: VP - "Play Area"
@ -25,63 +22,91 @@ function onLoad()
self.createButton(buttonParameters) self.createButton(buttonParameters)
-- index 2: VP - "Total" -- index 2: VP - "Total"
buttonParameters.position.x = 1.685 buttonParameters.position.x = 1.69
self.createButton(buttonParameters) self.createButton(buttonParameters)
-- update the display label once -- update the display label once
Wait.time(updateCount, 1) Wait.time(updateCount, 1)
end end
-- automatically update when cards are dropped or removed ---------------------------------------------------------
-- events with descriptions
---------------------------------------------------------
-- dropping an object on the victory display
function onCollisionEnter() function onCollisionEnter()
updateCount() -- stop if there is already an update call running
if pendingCall then return end
pendingCall = true
Wait.time(updateCount, 0.2)
end end
-- removing an object from the victory display
function onCollisionExit() function onCollisionExit()
updateCount() -- stop if there is already an update call running
if pendingCall then return end
pendingCall = true
Wait.time(updateCount, 0.2)
end end
-- picking a clue or location up
function onObjectPickUp(_, obj) function onObjectPickUp(_, obj)
maybeUpdate(obj) maybeUpdate(obj)
end end
-- dropping a clue or location
function onObjectDrop(_, obj) function onObjectDrop(_, obj)
maybeUpdate(obj, 1) maybeUpdate(obj, 1)
end end
-- flipping a clue/doom or location
function onObjectRotate(obj, _, flip, _, _, oldFlip) function onObjectRotate(obj, _, flip, _, _, oldFlip)
if flip == oldFlip then return end if flip == oldFlip then return end
maybeUpdate(obj, 1, true) maybeUpdate(obj, 1, true)
end end
-- destroying a clue or location
function onObjectDestroy(obj) function onObjectDestroy(obj)
maybeUpdate(obj) maybeUpdate(obj)
end end
---------------------------------------------------------
-- main functionality
---------------------------------------------------------
function maybeUpdate(obj, delay, flipped) function maybeUpdate(obj, delay, flipped)
-- stop if there is already an update call running
if pendingCall then return end
-- stop if obj is nil (by e.g. dropping a clue onto another and making a stack)
if obj == nil then return end if obj == nil then return end
if obj.memo ~= "clueDoom" then return end
if obj.is_face_down == true and flipped ~= true then return end
if not playAreaApi.isInPlayArea(obj) then return end
delay = tonumber(delay) or 0
Wait.time(function() updateCount() end, delay)
end
function isClue(obj) -- only continue for clues / doom tokens or locations
return obj.memo == "clueDoom" and obj.is_face_down == false if obj.hasTag("Location") then
end elseif obj.memo == "clueDoom" then
-- only continue if the clue side is up or a doom token is being flipped
-- works as a sinkhole for all refresh calls if obj.is_face_down == true and flipped ~= true then return end
function updateCount() else
if not pendingCall then return
pendingCall = true
Wait.time(function() updateCountNow() end, 0.2)
end end
-- only continue if the obj in in the play area
if not playAreaApi.isInPlayArea(obj) then return end
-- set this flag to limit function calls (will be reset by "updateCount")
pendingCall = true
-- update the count with delay (or 0 if no delay is provided)
-- this is needed to let tokens drop on the card
delay = tonumber(delay) or 0
Wait.time(updateCount, delay + 0.2)
end end
function updateCountNow() -- counts the VP in the victory display and request the VP count from the play area
function updateCount()
local victoryPoints = {}
victoryPoints.display = 0 victoryPoints.display = 0
victoryPoints.playArea = 0 victoryPoints.playArea = playAreaApi.countVP()
-- count cards in victory display -- count cards in victory display
for _, v in ipairs(searchOnObj(self)) do for _, v in ipairs(searchOnObj(self)) do
@ -102,29 +127,16 @@ function updateCountNow()
end end
end end
-- count locations in playArea -- update the buttons that are used as labels
local playArea = getObjectFromGUID("721ba2")
for _, v in ipairs(searchOnObj(playArea)) do
local obj = v.hit_object
local cardVP = 0
if obj.hasTag("Location") then
cardVP = getCardVP(obj.is_face_down, JSON.decode(obj.getGMNotes())) or 0
if cardVP ~= 0 and not cardHasClues(obj) then
victoryPoints.playArea = victoryPoints.playArea + cardVP
end
end
end
pendingCall = false
updateVP()
end
function updateVP()
self.editButton({ index = 0, label = victoryPoints.display }) self.editButton({ index = 0, label = victoryPoints.display })
self.editButton({ index = 1, label = victoryPoints.playArea }) self.editButton({ index = 1, label = victoryPoints.playArea })
self.editButton({ index = 2, label = victoryPoints.display + victoryPoints.playArea }) self.editButton({ index = 2, label = victoryPoints.display + victoryPoints.playArea })
-- allow new update calls
pendingCall = false
end end
-- sends a message for cards in the victory display that don't have VP
function addOrSendMessage(addition, name) function addOrSendMessage(addition, name)
if tonumber(addition) ~= nil then if tonumber(addition) ~= nil then
return tonumber(addition) return tonumber(addition)
@ -135,17 +147,6 @@ function addOrSendMessage(addition, name)
return 0 return 0
end end
-- checks if a card has clues on it
function cardHasClues(card)
for _, v in ipairs(searchOnObj(card)) do
local obj = v.hit_object
if isClue(obj) then
return true
end
end
return false
end
-- gets the VP count from the notes -- gets the VP count from the notes
function getCardVP(faceDown, notes) function getCardVP(faceDown, notes)
local cardVP local cardVP
@ -155,6 +156,7 @@ function getCardVP(faceDown, notes)
-- location -- location
if not cardVP then if not cardVP then
-- check the correct side of the location
if not faceDown and notes.locationFront ~= nil then if not faceDown and notes.locationFront ~= nil then
cardVP = tonumber(notes.locationFront.victory) cardVP = tonumber(notes.locationFront.victory)
elseif notes.locationBack ~= nil then elseif notes.locationBack ~= nil then
@ -165,6 +167,11 @@ function getCardVP(faceDown, notes)
return cardVP return cardVP
end end
---------------------------------------------------------
-- utility functions
---------------------------------------------------------
-- searches on an object
function searchOnObj(obj) function searchOnObj(obj)
return Physics.cast({ return Physics.cast({
direction = { 0, 1, 0 }, direction = { 0, 1, 0 },