optimization
This commit is contained in:
parent
77d0339354
commit
8bef677eda
@ -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}
|
||||||
|
@ -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)
|
||||||
|
@ -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 },
|
||||||
|
Loading…
Reference in New Issue
Block a user