Merge pull request #211 from argonui/victory-display
Victory Display: Visual update and automation
This commit is contained in:
commit
26c0e92e86
@ -3,7 +3,6 @@
|
||||
"ComponentTags_path": "ComponentTags.json",
|
||||
"CustomUIAssets_path": "CustomUIAssets.json",
|
||||
"DecalPallet_path": "DecalPallet.json",
|
||||
"Decals_path": "Decals.json",
|
||||
"GameComplexity": "",
|
||||
"GameMode": "Arkham Horror LCG - Super Complete Edition",
|
||||
"GameType": "",
|
||||
@ -188,7 +187,8 @@
|
||||
"Decoration-Ammo.0a3b03",
|
||||
"Decoration-Ammo.b43845",
|
||||
"Decoration-Ammo.d35ee9",
|
||||
"ArkhamSCE300-1272023-Page1.f873a8"
|
||||
"ArkhamSCE300-1272023-Page1.f873a8",
|
||||
"VictoryDisplay.6ccd6d"
|
||||
],
|
||||
"PlayArea": 1,
|
||||
"PlayerCounts": [
|
||||
|
@ -1,20 +0,0 @@
|
||||
[
|
||||
{
|
||||
"CustomDecal": {
|
||||
"ImageURL": "https://i.imgur.com/saWedQ0.png",
|
||||
"Name": "Victory Display",
|
||||
"Size": 15
|
||||
},
|
||||
"Transform": {
|
||||
"posX": -1.76003075,
|
||||
"posY": 1.491499,
|
||||
"posZ": 28.6174583,
|
||||
"rotX": 90,
|
||||
"rotY": 89.6667938,
|
||||
"rotZ": 0,
|
||||
"scaleX": 15,
|
||||
"scaleY": 15,
|
||||
"scaleZ": 15
|
||||
}
|
||||
}
|
||||
]
|
@ -207,78 +207,6 @@
|
||||
"z": -32.233
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 0.7,
|
||||
"y": 1.481,
|
||||
"z": 33.6
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 270,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -5.35,
|
||||
"y": 1.481,
|
||||
"z": 33.6
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 270,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -5.35,
|
||||
"y": 1.481,
|
||||
"z": 28.6
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 270,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 0.7,
|
||||
"y": 1.481,
|
||||
"z": 28.6
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 270,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 0.7,
|
||||
"y": 1.481,
|
||||
"z": 23.6
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 270,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -5.35,
|
||||
"y": 1.481,
|
||||
"z": 23.6
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 270,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 65,
|
||||
|
182
objects/VictoryDisplay.6ccd6d.json
Normal file
182
objects/VictoryDisplay.6ccd6d.json
Normal file
@ -0,0 +1,182 @@
|
||||
{
|
||||
"AltLookAngle": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
},
|
||||
"AttachedSnapPoints": [
|
||||
{
|
||||
"Position": {
|
||||
"x": 0,
|
||||
"y": 0.05,
|
||||
"z": 0.05
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 0.8,
|
||||
"y": 0.05,
|
||||
"z": 1
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 0,
|
||||
"y": 0.05,
|
||||
"z": 1
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 1.6,
|
||||
"y": 0.05,
|
||||
"z": 0.05
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 1.6,
|
||||
"y": 0.05,
|
||||
"z": 1
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -0.8,
|
||||
"y": 0.05,
|
||||
"z": 0.05
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -0.8,
|
||||
"y": 0.05,
|
||||
"z": 1
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -1.6,
|
||||
"y": 0.05,
|
||||
"z": 0.05
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": -1.6,
|
||||
"y": 0.05,
|
||||
"z": 1
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"Position": {
|
||||
"x": 0.8,
|
||||
"y": 0.05,
|
||||
"z": 0.05
|
||||
},
|
||||
"Rotation": {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"z": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"Autoraise": true,
|
||||
"ColorDiffuse": {
|
||||
"b": 1,
|
||||
"g": 1,
|
||||
"r": 1
|
||||
},
|
||||
"CustomImage": {
|
||||
"CustomToken": {
|
||||
"MergeDistancePixels": 15,
|
||||
"Stackable": false,
|
||||
"StandUp": false,
|
||||
"Thickness": 0.1
|
||||
},
|
||||
"ImageScalar": 1,
|
||||
"ImageSecondaryURL": "",
|
||||
"ImageURL": "http://cloud-3.steamusercontent.com/ugc/2053113893674531758/8E71AC0C7F8198A791AB4EA4ED9D301904F9A752/",
|
||||
"WidthScale": 0
|
||||
},
|
||||
"Description": "Automatically counts the earned VP from cards in the victory display and locations in the play area without clues on them.",
|
||||
"DragSelectable": true,
|
||||
"GMNotes": "",
|
||||
"GUID": "6ccd6d",
|
||||
"Grid": true,
|
||||
"GridProjection": false,
|
||||
"Hands": false,
|
||||
"HideWhenFaceDown": false,
|
||||
"IgnoreFoW": false,
|
||||
"LayoutGroupSortIndex": 0,
|
||||
"Locked": true,
|
||||
"LuaScript": "require(\"core/VictoryDisplay\")",
|
||||
"LuaScriptState": "",
|
||||
"MeasureMovement": false,
|
||||
"Name": "Custom_Token",
|
||||
"Nickname": "Victory Display",
|
||||
"Snap": false,
|
||||
"Sticky": true,
|
||||
"Tooltip": true,
|
||||
"Tags": [
|
||||
"CleanUpHelper_ignore"
|
||||
],
|
||||
"Transform": {
|
||||
"posX": -1.5,
|
||||
"posY": 1.531,
|
||||
"posZ": 30,
|
||||
"rotX": 0,
|
||||
"rotY": 270,
|
||||
"rotZ": 0,
|
||||
"scaleX": 4.5,
|
||||
"scaleY": 1,
|
||||
"scaleZ": 4.5
|
||||
},
|
||||
"Value": 0,
|
||||
"XmlUI": ""
|
||||
}
|
@ -56,6 +56,8 @@ local draggingGuids = {}
|
||||
local locationData
|
||||
local currentScenario
|
||||
|
||||
local missingData = {}
|
||||
|
||||
---------------------------------------------------------
|
||||
-- general code
|
||||
---------------------------------------------------------
|
||||
@ -96,7 +98,12 @@ function onCollisionEnter(collisionInfo)
|
||||
local objType = obj.name
|
||||
|
||||
-- only continue for cards
|
||||
if not collisionEnabled or (objType ~= "Card" and objType ~= "CardCustom") then return end
|
||||
if not collisionEnabled or (objType ~= "Card" and objType ~= "CardCustom") then
|
||||
if objType == "Deck" then
|
||||
table.insert(missingData, obj)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- check if we should spawn clues here and do so according to playercount
|
||||
local card = collisionInfo.collision_object
|
||||
@ -142,13 +149,17 @@ function onObjectPickUp(player, object)
|
||||
if showLocationLinks() and isInPlayArea(object) and object.getGMNotes() ~= nil and object.getGMNotes() ~= "" then
|
||||
local pickedUpGuid = object.getGUID()
|
||||
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
|
||||
-- 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.
|
||||
-- Waiting ensures we always do thing in the expected Exit->PickUp order
|
||||
Wait.frames(function()
|
||||
draggingGuids[pickedUpGuid] = metadata
|
||||
if object.is_face_down then
|
||||
draggingGuids[pickedUpGuid] = metadata.locationBack
|
||||
else
|
||||
draggingGuids[pickedUpGuid] = metadata.locationFront
|
||||
end
|
||||
rebuildConnectionList()
|
||||
end, 2)
|
||||
end
|
||||
@ -188,12 +199,24 @@ end
|
||||
function maybeTrackLocation(card)
|
||||
-- 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
|
||||
if showLocationLinks() and isInPlayArea(card) then
|
||||
local metadata = JSON.decode(card.getGMNotes()) or { }
|
||||
if metadata.type == "Location" then
|
||||
locations[card.getGUID()] = metadata
|
||||
rebuildConnectionList()
|
||||
drawBaseConnections()
|
||||
if isInPlayArea(card) then
|
||||
local metadata = JSON.decode(card.getGMNotes())
|
||||
if metadata == nil then
|
||||
table.insert(missingData, card)
|
||||
else
|
||||
if metadata.type == "Location" then
|
||||
if card.is_face_down then
|
||||
locations[card.getGUID()] = metadata.locationBack
|
||||
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
|
||||
@ -239,20 +262,20 @@ function rebuildConnectionList()
|
||||
|
||||
-- Build a list of cards with each icon as their location ID
|
||||
for cardId, metadata in pairs(draggingGuids) do
|
||||
buildLocListByIcon(cardId, iconCardList)
|
||||
buildLocListByIcon(cardId, iconCardList, metadata)
|
||||
end
|
||||
for cardId, metadata in pairs(locations) do
|
||||
buildLocListByIcon(cardId, iconCardList)
|
||||
buildLocListByIcon(cardId, iconCardList, metadata)
|
||||
end
|
||||
|
||||
-- Pair up all the icons
|
||||
locationConnections = { }
|
||||
for cardId, metadata in pairs(draggingGuids) do
|
||||
buildConnection(cardId, iconCardList)
|
||||
buildConnection(cardId, iconCardList, metadata)
|
||||
end
|
||||
for cardId, metadata in pairs(locations) do
|
||||
if draggingGuids[cardId] == nil then
|
||||
buildConnection(cardId, iconCardList)
|
||||
buildConnection(cardId, iconCardList, metadata)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -260,15 +283,14 @@ end
|
||||
-- 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 iconCardList Table A table of icon->GUID list. Mutable, will be updated by this method
|
||||
function buildLocListByIcon(cardId, iconCardList)
|
||||
local card = getObjectFromGUID(cardId)
|
||||
local locData = getLocationData(card)
|
||||
---@param locData Table A table containing the metadata for the card (for the correct side)
|
||||
function buildLocListByIcon(cardId, iconCardList, locData)
|
||||
if locData ~= nil and locData.icons ~= nil then
|
||||
for icon in string.gmatch(locData.icons, "%a+") do
|
||||
if iconCardList[icon] == nil then
|
||||
iconCardList[icon] = { }
|
||||
end
|
||||
table.insert(iconCardList[icon], card.getGUID())
|
||||
table.insert(iconCardList[icon], cardId)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -277,25 +299,24 @@ end
|
||||
-- Playarea's locationConnections table.
|
||||
---@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.
|
||||
function buildConnection(cardId, iconCardList)
|
||||
local card = getObjectFromGUID(cardId)
|
||||
local locData = getLocationData(card)
|
||||
---@param locData Table A table containing the metadata for the card (for the correct side)
|
||||
function buildConnection(cardId, iconCardList, locData)
|
||||
if locData ~= nil and locData.connections ~= nil then
|
||||
locationConnections[card.getGUID()] = { }
|
||||
locationConnections[cardId] = { }
|
||||
for icon in string.gmatch(locData.connections, "%a+") do
|
||||
if iconCardList[icon] ~= nil then
|
||||
for _, connectedGuid in ipairs(iconCardList[icon]) do
|
||||
-- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way
|
||||
if locationConnections[connectedGuid] ~= nil
|
||||
and locationConnections[connectedGuid][card.getGUID()] == ONE_WAY then
|
||||
locationConnections[connectedGuid][card.getGUID()] = BIDIRECTIONAL
|
||||
locationConnections[card.getGUID()][connectedGuid] = nil
|
||||
and locationConnections[connectedGuid][cardId] == ONE_WAY then
|
||||
locationConnections[connectedGuid][cardId] = BIDIRECTIONAL
|
||||
locationConnections[cardId][connectedGuid] = nil
|
||||
else
|
||||
if locationConnections[connectedGuid] == nil then
|
||||
locationConnections[connectedGuid] = { }
|
||||
end
|
||||
locationConnections[card.getGUID()][connectedGuid] = ONE_WAY
|
||||
locationConnections[connectedGuid][card.getGUID()] = INCOMING_ONE_WAY
|
||||
locationConnections[cardId][connectedGuid] = ONE_WAY
|
||||
locationConnections[connectedGuid][cardId] = INCOMING_ONE_WAY
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -303,22 +324,6 @@ function buildConnection(cardId, iconCardList)
|
||||
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.
|
||||
-- Constructed vectors will be set to the playmat
|
||||
function drawBaseConnections()
|
||||
@ -553,6 +558,64 @@ function setLimitSnapsByType(matchTypes)
|
||||
self.setSnapPoints(snaps)
|
||||
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
|
||||
|
||||
-- highlights all locations in the play area without metadata
|
||||
---@param state Boolean True if highlighting should be enabled
|
||||
function highlightMissingData(state)
|
||||
for i, obj in pairs(missingData) do
|
||||
if obj ~= nil then
|
||||
if state then
|
||||
obj.highlightOff("Red")
|
||||
else
|
||||
obj.highlightOn("Red")
|
||||
end
|
||||
else
|
||||
missingData[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- rebuilds local snap points (could be useful in the future again)
|
||||
function buildSnaps()
|
||||
local upperleft = { x = 1.53, z = -1.09}
|
||||
|
@ -55,5 +55,21 @@ do
|
||||
{ container = container, object = object })
|
||||
end
|
||||
|
||||
-- counts the VP on locations in the play area
|
||||
PlayAreaApi.countVP = function()
|
||||
return getObjectFromGUID(PLAY_AREA_GUID).call("countVP")
|
||||
end
|
||||
|
||||
-- highlights all locations in the play area without metadata
|
||||
---@param state Boolean True if highlighting should be enabled
|
||||
PlayAreaApi.highlightMissingData = function(state)
|
||||
return getObjectFromGUID(PLAY_AREA_GUID).call("highlightMissingData", state)
|
||||
end
|
||||
|
||||
-- Checks if an object is in the play area (returns true or false)
|
||||
PlayAreaApi.isInPlayArea = function(object)
|
||||
return getObjectFromGUID(PLAY_AREA_GUID).call("isInPlayArea", object)
|
||||
end
|
||||
|
||||
return PlayAreaApi
|
||||
end
|
||||
|
218
src/core/VictoryDisplay.ttslua
Normal file
218
src/core/VictoryDisplay.ttslua
Normal file
@ -0,0 +1,218 @@
|
||||
local playAreaApi = require("core/PlayAreaApi")
|
||||
local pendingCall = false
|
||||
local messageSent = {}
|
||||
local currentlyHighlighting = false
|
||||
local missingData = {}
|
||||
|
||||
-- button creation when loading the game
|
||||
function onLoad()
|
||||
-- index 0: VP - "Display"
|
||||
local buttonParameters = {}
|
||||
buttonParameters.label = "0"
|
||||
buttonParameters.click_function = "none"
|
||||
buttonParameters.function_owner = self
|
||||
buttonParameters.scale = { 0.15, 0.15, 0.15 }
|
||||
buttonParameters.width = 0
|
||||
buttonParameters.height = 0
|
||||
buttonParameters.font_size = 600
|
||||
buttonParameters.font_color = { 1, 1, 1 }
|
||||
buttonParameters.position = { x = -0.72, y = 0.06, z = -0.69 }
|
||||
self.createButton(buttonParameters)
|
||||
|
||||
-- index 1: VP - "Play Area"
|
||||
buttonParameters.position.x = 0.65
|
||||
self.createButton(buttonParameters)
|
||||
|
||||
-- index 2: VP - "Total"
|
||||
buttonParameters.position.x = 1.69
|
||||
self.createButton(buttonParameters)
|
||||
|
||||
-- index 3: highlighting button
|
||||
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 }
|
||||
})
|
||||
-- update the display label once
|
||||
Wait.time(updateCount, 1)
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- events with descriptions
|
||||
---------------------------------------------------------
|
||||
|
||||
-- dropping an object on the victory display
|
||||
function onCollisionEnter()
|
||||
-- stop if there is already an update call running
|
||||
if pendingCall then return end
|
||||
pendingCall = true
|
||||
Wait.time(updateCount, 0.2)
|
||||
end
|
||||
|
||||
-- removing an object from the victory display
|
||||
function onCollisionExit()
|
||||
-- stop if there is already an update call running
|
||||
if pendingCall then return end
|
||||
pendingCall = true
|
||||
Wait.time(updateCount, 0.2)
|
||||
end
|
||||
|
||||
-- picking a clue or location up
|
||||
function onObjectPickUp(_, obj)
|
||||
maybeUpdate(obj)
|
||||
end
|
||||
|
||||
-- dropping a clue or location
|
||||
function onObjectDrop(_, obj)
|
||||
maybeUpdate(obj, 1)
|
||||
end
|
||||
|
||||
-- flipping a clue/doom or location
|
||||
function onObjectRotate(obj, _, flip, _, _, oldFlip)
|
||||
if flip == oldFlip then return end
|
||||
maybeUpdate(obj, 1, true)
|
||||
end
|
||||
|
||||
-- destroying a clue or location
|
||||
function onObjectDestroy(obj)
|
||||
maybeUpdate(obj)
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- main functionality
|
||||
---------------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
-- only continue for clues / doom tokens or locations
|
||||
if obj.hasTag("Location") then
|
||||
elseif obj.memo == "clueDoom" then
|
||||
-- only continue if the clue side is up or a doom token is being flipped
|
||||
if obj.is_face_down == true and flipped ~= true then return end
|
||||
else
|
||||
return
|
||||
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
|
||||
|
||||
-- counts the VP in the victory display and request the VP count from the play area
|
||||
function updateCount()
|
||||
missingData = {}
|
||||
local victoryPoints = {}
|
||||
victoryPoints.display = 0
|
||||
victoryPoints.playArea = playAreaApi.countVP()
|
||||
|
||||
-- count cards in victory display
|
||||
for _, v in ipairs(searchOnObj(self)) do
|
||||
local obj = v.hit_object
|
||||
|
||||
-- check metadata for VP
|
||||
if obj.tag == "Card" then
|
||||
victoryPoints.display = victoryPoints.display + getCardVP(obj, JSON.decode(obj.getGMNotes()))
|
||||
|
||||
-- handling for stacked cards
|
||||
elseif obj.tag == "Deck" then
|
||||
for _, deepObj in ipairs(obj.getObjects()) do
|
||||
victoryPoints.display = victoryPoints.display + getCardVP(obj, JSON.decode(deepObj.gm_notes))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- update the buttons that are used as labels
|
||||
self.editButton({ index = 0, label = victoryPoints.display })
|
||||
self.editButton({ index = 1, label = victoryPoints.playArea })
|
||||
self.editButton({ index = 2, label = victoryPoints.display + victoryPoints.playArea })
|
||||
|
||||
-- allow new update calls
|
||||
pendingCall = false
|
||||
end
|
||||
|
||||
-- gets the VP count from the notes
|
||||
function getCardVP(obj, notes)
|
||||
local cardVP
|
||||
if notes ~= nil then
|
||||
-- enemy, treachery etc.
|
||||
cardVP = tonumber(notes.victory)
|
||||
|
||||
-- location
|
||||
if not cardVP then
|
||||
-- check the correct side of the location
|
||||
if not obj.is_face_down and notes.locationFront ~= nil then
|
||||
cardVP = tonumber(notes.locationFront.victory)
|
||||
elseif notes.locationBack ~= nil then
|
||||
cardVP = tonumber(notes.locationBack.victory)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(missingData, obj)
|
||||
end
|
||||
return cardVP or 0
|
||||
end
|
||||
|
||||
-- toggles the highlight for objects with missing metadata
|
||||
function highlightMissingData()
|
||||
self.editButton({
|
||||
index = 3,
|
||||
tooltip = (currentlyHighlighting 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 currentlyHighlighting then
|
||||
obj.highlightOff("Red")
|
||||
else
|
||||
obj.highlightOn("Red")
|
||||
end
|
||||
end
|
||||
end
|
||||
playAreaApi.highlightMissingData(currentlyHighlighting)
|
||||
currentlyHighlighting = not currentlyHighlighting
|
||||
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
|
||||
|
||||
-- search a table for a value, return true if found (else returns false)
|
||||
function tableContains(table, value)
|
||||
for _, v in ipairs(table) do
|
||||
if v == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
Loading…
Reference in New Issue
Block a user