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",
|
"ComponentTags_path": "ComponentTags.json",
|
||||||
"CustomUIAssets_path": "CustomUIAssets.json",
|
"CustomUIAssets_path": "CustomUIAssets.json",
|
||||||
"DecalPallet_path": "DecalPallet.json",
|
"DecalPallet_path": "DecalPallet.json",
|
||||||
"Decals_path": "Decals.json",
|
|
||||||
"GameComplexity": "",
|
"GameComplexity": "",
|
||||||
"GameMode": "Arkham Horror LCG - Super Complete Edition",
|
"GameMode": "Arkham Horror LCG - Super Complete Edition",
|
||||||
"GameType": "",
|
"GameType": "",
|
||||||
@ -188,7 +187,8 @@
|
|||||||
"Decoration-Ammo.0a3b03",
|
"Decoration-Ammo.0a3b03",
|
||||||
"Decoration-Ammo.b43845",
|
"Decoration-Ammo.b43845",
|
||||||
"Decoration-Ammo.d35ee9",
|
"Decoration-Ammo.d35ee9",
|
||||||
"ArkhamSCE300-1272023-Page1.f873a8"
|
"ArkhamSCE300-1272023-Page1.f873a8",
|
||||||
|
"VictoryDisplay.6ccd6d"
|
||||||
],
|
],
|
||||||
"PlayArea": 1,
|
"PlayArea": 1,
|
||||||
"PlayerCounts": [
|
"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
|
"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": {
|
"Position": {
|
||||||
"x": 65,
|
"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 locationData
|
||||||
local currentScenario
|
local currentScenario
|
||||||
|
|
||||||
|
local missingData = {}
|
||||||
|
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
-- general code
|
-- general code
|
||||||
---------------------------------------------------------
|
---------------------------------------------------------
|
||||||
@ -96,7 +98,12 @@ function onCollisionEnter(collisionInfo)
|
|||||||
local objType = obj.name
|
local objType = obj.name
|
||||||
|
|
||||||
-- only continue for cards
|
-- 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
|
-- check if we should spawn clues here and do so according to playercount
|
||||||
local card = collisionInfo.collision_object
|
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
|
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
|
||||||
@ -188,15 +199,27 @@ 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())
|
||||||
|
if metadata == nil then
|
||||||
|
table.insert(missingData, card)
|
||||||
|
else
|
||||||
if metadata.type == "Location" then
|
if metadata.type == "Location" then
|
||||||
locations[card.getGUID()] = metadata
|
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()
|
rebuildConnectionList()
|
||||||
drawBaseConnections()
|
drawBaseConnections()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Stop tracking a location for connection drawing. This should be called for both collision exit
|
-- Stop tracking a location for connection drawing. This should be called for both collision exit
|
||||||
-- and destruction, as a destroyed object does not trigger collision exit. An object can also be
|
-- and destruction, as a destroyed object does not trigger collision exit. An object can also be
|
||||||
@ -239,20 +262,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
|
||||||
@ -260,15 +283,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
|
||||||
@ -277,25 +299,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
|
||||||
@ -303,22 +324,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()
|
||||||
@ -553,6 +558,64 @@ 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
|
||||||
|
|
||||||
|
-- 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)
|
-- 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,5 +55,21 @@ 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
|
||||||
|
|
||||||
|
-- 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
|
return PlayAreaApi
|
||||||
end
|
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