SCED/src/playermat/Playmat.ttslua

774 lines
25 KiB
Plaintext
Raw Normal View History

2022-11-12 12:43:35 +01:00
-- set true to enable debug logging and show Physics.cast()
local DEBUG = false
2022-11-12 12:43:35 +01:00
-- we use this to turn off collision handling until onLoad() is complete
local COLLISION_ENABLED = false
-- position offsets relative to mat [x, y, z]
local DRAWN_ENCOUNTER_CARD_OFFSET = {1.365, 0.5, -0.635}
local DRAWN_CHAOS_TOKEN_OFFSET = {-1.55, 0.5, -0.58}
local DISCARD_BUTTON_OFFSETS = {
{-1.365, 0.1, -0.945},
{-0.91, 0.1, -0.945},
{-0.455, 0.1, -0.945},
{0, 0.1, -0.945},
{0.455, 0.1, -0.945},
{0.91, 0.1, -0.945}
}
local MAIN_PLAY_AREA = {
upperLeft = {
x = 1.98,
z = 0.736,
},
lowerRight = {
x = -0.79,
z = -0.39,
}
}
local INVESTIGATOR_AREA = {
upperLeft = {
x = -1.084,
z = 0.06517
},
lowerRight = {
x = -1.258,
z = -0.0805,
}
}
2022-11-12 12:43:35 +01:00
local PLAY_ZONE_ROTATION = self.getRotation()
2022-11-16 18:58:59 +01:00
local TRASHCAN
local STAT_TRACKER
local RESOURCE_COUNTER
-- global variable so it can be reset by the Clean Up Helper
activeInvestigatorId = "00000"
2022-12-08 12:01:10 +01:00
local drawButton = false
2022-11-12 12:43:35 +01:00
2022-12-08 12:01:10 +01:00
function onSave() return JSON.encode({zoneID = zoneID, playerColor = PLAYER_COLOR, activeInvestigatorId = activeInvestigatorId, drawButton = drawButton}) end
2022-11-12 12:43:35 +01:00
function onLoad(save_state)
self.interactable = DEBUG
DATA_HELPER = getObjectFromGUID('708279')
PLAYER_CARDS = DATA_HELPER.getTable('PLAYER_CARD_DATA')
PLAYER_CARD_TOKEN_OFFSETS = DATA_HELPER.getTable('PLAYER_CARD_TOKEN_OFFSETS')
2022-11-16 18:58:59 +01:00
TRASHCAN = getObjectFromGUID(TRASHCAN_GUID)
STAT_TRACKER = getObjectFromGUID(STAT_TRACKER_GUID)
RESOURCE_COUNTER = getObjectFromGUID(RESOURCE_COUNTER_GUID)
2022-11-12 12:43:35 +01:00
for i = 1, 6 do
makeDiscardButton(DISCARD_BUTTON_OFFSETS[i], {-3.85, 3, 10.38}, i)
end
self.createButton({
click_function = "drawEncountercard",
function_owner = self,
2022-11-12 12:43:35 +01:00
position = {-1.84, 0, -0.65},
rotation = {0, 80, 0},
width = 265,
height = 190
})
self.createButton({
click_function = "drawChaostokenButton",
function_owner = self,
2022-11-12 12:43:35 +01:00
position = {1.85, 0, -0.74},
rotation = {0, -45, 0},
width = 135,
height = 135
})
self.createButton({
2022-11-12 12:43:35 +01:00
label = "Upkeep",
click_function = "doUpkeep",
function_owner = self,
2022-11-12 12:43:35 +01:00
position = {1.84, 0.1, -0.44},
scale = {0.12, 0.12, 0.12},
width = 800,
height = 280,
font_size = 180
})
local state = JSON.decode(save_state)
if state ~= nil then
2022-11-12 12:43:35 +01:00
zoneID = state.zoneID
PLAYER_COLOR = state.playerColor
activeInvestigatorId = state.activeInvestigatorId
2022-12-08 12:01:10 +01:00
drawButton = state.drawButton
end
2022-12-08 12:01:10 +01:00
showDrawButton(drawButton)
2022-11-12 12:43:35 +01:00
if getObjectFromGUID(zoneID) == nil then spawnDeckZone() end
COLLISION_ENABLED = true
end
2022-11-12 12:43:35 +01:00
---------------------------------------------------------
-- utility functions
---------------------------------------------------------
function log(message)
if DEBUG then print(message) end
end
2022-11-12 12:43:35 +01:00
-- send messages to player who clicked button if no seated player found
function setMessageColor(color)
2022-11-12 12:43:35 +01:00
messageColor = Player[PLAYER_COLOR].seated and PLAYER_COLOR or color
end
2022-11-12 12:43:35 +01:00
function spawnDeckZone()
spawnObject({
position = self.positionToWorld({-1.4, 0, 0.3 }),
scale = {3, 5, 8 },
type = 'ScriptingTrigger',
callback = function (zone) zoneID = zone.getGUID() end,
callback_owner = self,
rotation = self.getRotation()
})
end
2022-11-12 12:43:35 +01:00
function searchArea(origin, size)
return Physics.cast({
origin = origin,
direction = {0, 1, 0},
orientation = PLAY_ZONE_ROTATION,
type = 3,
size = size,
max_distance = 1,
debug = DEBUG
})
end
2022-11-12 12:43:35 +01:00
function doNotReady(card) return card.getVar("do_not_ready") or false end
2022-11-12 12:43:35 +01:00
---------------------------------------------------------
-- Discard buttons
---------------------------------------------------------
2022-11-12 12:43:35 +01:00
-- builds a function that discards things in searchPosition to discardPosition
-- stuff on the card/deck will be put into the local trashcan
function makeDiscardHandlerFor(searchPosition, discardPosition)
return function ()
for _, hitObj in ipairs(findObjectsAtPosition(searchPosition)) do
local obj = hitObj.hit_object
if obj.tag == "Deck" or obj.tag == "Card" then
if obj.hasTag("PlayerCard") then
obj.setPositionSmooth(DISCARD_PILE_POSITION, false, true)
obj.setRotation(PLAY_ZONE_ROTATION)
else
obj.setPositionSmooth(discardPosition, false, true)
obj.setRotation({0, -90, 0})
end
-- don't touch the table or this playmat itself
elseif obj.guid ~= "4ee1f2" and obj ~= self then
TRASHCAN.putObject(obj)
end
end
2022-11-12 12:43:35 +01:00
end
end
2022-11-12 12:43:35 +01:00
-- build a discard button to discard from searchPosition to discardPosition (number must be unique)
function makeDiscardButton(position, discardPosition, number)
local searchPosition = {-position[1], position[2], position[3] + 0.32}
local handler = makeDiscardHandlerFor(searchPosition, discardPosition)
local handlerName = 'handler' .. number
self.setVar(handlerName, handler)
self.createButton({
label = "Discard",
click_function = handlerName,
function_owner = self,
position = position,
scale = {0.12, 0.12, 0.12},
width = 800,
height = 280,
font_size = 180
})
end
2022-11-12 12:43:35 +01:00
function findObjectsAtPosition(localPos)
return Physics.cast({
origin = self.positionToWorld(localPos),
direction = {0, 1, 0},
orientation = {0, PLAY_ZONE_ROTATION.y + 90, 0},
type = 3,
size = {3.2, 1, 2},
max_distance = 0,
debug = DEBUG
})
end
2022-11-12 12:43:35 +01:00
---------------------------------------------------------
-- Upkeep button
---------------------------------------------------------
2022-11-12 12:43:35 +01:00
function doUpkeep(_, color, alt_click)
setMessageColor(color)
2022-11-12 12:43:35 +01:00
-- right-click binds to new player color
if alt_click then
PLAYER_COLOR = color
printToColor("Upkeep button bound to " .. color, color)
return
end
2022-11-12 12:43:35 +01:00
local forcedLearning = false
-- unexhaust cards in play zone, flip action tokens and find forcedLearning
for _, v in ipairs(searchArea(PLAY_ZONE_POSITION, PLAY_ZONE_SCALE)) do
local obj = v.hit_object
if obj.getDescription() == "Action Token" and obj.is_face_down then
obj.flip()
elseif obj.tag == "Card" and not obj.is_face_down then
local cardMetadata = JSON.decode(obj.getGMNotes()) or {}
if not doNotReady(obj) and cardMetadata.type ~= "Investigator" then
2022-11-12 12:43:35 +01:00
obj.setRotation(PLAY_ZONE_ROTATION)
end
if cardMetadata.id == "08031" then
forcedLearning = true
end
if cardMetadata.uses ~= nil then
2022-11-12 12:43:35 +01:00
-- check for cards with 'replenish' in their metadata
local count
local replenish
-- Uses structure underwent a breaking change in 2.4.0, have to check to see if this is
-- a single entry or an array. TODO: Clean this up when 2.4.0 has been out long
-- enough that saved decks don't have old data
if cardMetadata.uses.count ~= nil then
count = cardMetadata.uses.count
replenish = cardMetadata.uses.replenish
else
count = cardMetadata.uses[1].count
replenish = cardMetadata.uses[1].replenish
end
if count and replenish then
replenishTokens(obj, count, replenish)
end
2022-11-12 12:43:35 +01:00
end
end
2022-11-12 12:43:35 +01:00
end
2022-11-12 12:43:35 +01:00
-- flip investigator mini-card if found
-- flip summoned servitor mini-cards (To-Do: don't flip all of them)
if activeInvestigatorId ~= nil then
local miniId = string.match(activeInvestigatorId, "%d%d%d%d%d") .. "-m"
for _, obj in ipairs(getObjects()) do
2022-11-16 18:58:59 +01:00
if obj.tag == "Card" and obj.is_face_down then
2022-11-12 12:43:35 +01:00
local notes = JSON.decode(obj.getGMNotes())
2022-11-16 18:58:59 +01:00
if notes ~= nil and notes.type == "Minicard" and (notes.id == miniId or notes.id == "09080-m") then
2022-11-12 12:43:35 +01:00
obj.flip()
end
end
end
2022-11-12 12:43:35 +01:00
end
2022-11-12 12:43:35 +01:00
-- gain a resource
2022-11-19 00:33:21 +01:00
RESOURCE_COUNTER.call("addOrSubtract")
2022-11-12 12:43:35 +01:00
-- gain an additional resource for Jenny Barnes
if string.match(activeInvestigatorId, "%d%d%d%d%d") == "02003" then
2022-11-19 00:33:21 +01:00
RESOURCE_COUNTER.call("addOrSubtract")
2022-11-12 12:43:35 +01:00
printToColor("Gaining 2 resources (Jenny)", messageColor)
end
2022-11-12 12:43:35 +01:00
-- draw a card (with handling for Patrice and Forced Learning)
if activeInvestigatorId == "06005" then
2022-11-16 18:58:59 +01:00
if forcedLearning then
printToColor("Wow, did you really take 'Versatile' to play Patrice with 'Forced Learning'? Choose which draw replacement effect takes priority and draw cards accordingly.", messageColor)
else
local handSize = #Player[PLAYER_COLOR].getHandObjects()
if handSize < 5 then
local cardsToDraw = 5 - handSize
printToColor("Drawing " .. cardsToDraw .. " cards (Patrice)", messageColor)
drawCardsWithReshuffle(cardsToDraw)
end
end
2022-11-12 12:43:35 +01:00
elseif forcedLearning then
printToColor("Drawing 2 cards, discard 1 (Forced Learning)", messageColor)
drawCardsWithReshuffle(2)
2022-11-12 12:43:35 +01:00
else
drawCardsWithReshuffle(1)
end
end
2022-11-12 12:43:35 +01:00
-- legacy function for "add draw 1 button"
function doDrawOne(_, color)
setMessageColor(color)
drawCardsWithReshuffle(1)
end
2022-11-12 12:43:35 +01:00
-- draw X cards (shuffle discards if necessary)
function drawCardsWithReshuffle(numCards)
getDrawDiscardDecks()
-- Norman Withers handling
if string.match(activeInvestigatorId, "%d%d%d%d%d") == "08004" then
local harbinger = false
if topCard ~= nil and topCard.getName() == "The Harbinger" then harbinger = true
elseif drawDeck ~= nil and not drawDeck.is_face_down then
local cards = drawDeck.getObjects()
if cards[#cards].name == "The Harbinger" then harbinger = true end
end
if harbinger then
printToColor("The Harbinger is on top of your deck, not drawing cards", messageColor)
return
end
if topCard ~= nil then
topCard.deal(numCards, PLAYER_COLOR)
numCards = numCards - 1
if numCards == 0 then return end
end
end
local deckSize = 1
if drawDeck == nil then
deckSize = 0
elseif drawDeck.tag == "Deck" then
deckSize = #drawDeck.getObjects()
end
2022-11-12 12:43:35 +01:00
if deckSize >= numCards then
drawCards(numCards)
return
end
drawCards(deckSize)
if discardPile ~= nil then
shuffleDiscardIntoDeck()
Wait.time(|| drawCards(numCards - deckSize), 1)
end
printToColor("Take 1 horror (drawing card from empty deck)", messageColor)
end
2022-11-12 12:43:35 +01:00
-- get the draw deck and discard pile objects
function getDrawDiscardDecks()
drawDeck = nil
discardPile = nil
topCard = nil
local zone = getObjectFromGUID(zoneID)
if zone == nil then return end
for _, object in ipairs(zone.getObjects()) do
if object.tag == "Deck" or object.tag == "Card" then
if self.positionToLocal(object.getPosition()).z > 0.5 then
discardPile = object
-- Norman Withers handling
elseif string.match(activeInvestigatorId, "%d%d%d%d%d") == "08004" and object.tag == "Card" and not object.is_face_down then
topCard = object
else
drawDeck = object
end
end
2022-11-12 12:43:35 +01:00
end
end
function drawCards(numCards)
if drawDeck == nil then return end
drawDeck.deal(numCards, PLAYER_COLOR)
end
function shuffleDiscardIntoDeck()
if not discardPile.is_face_down then discardPile.flip() end
discardPile.shuffle()
discardPile.setPositionSmooth(DRAW_DECK_POSITION, false, false)
drawDeck = discardPile
discardPile = nil
end
function spawnTokenOn(object, offsets, tokenType)
local tokenPosition = object.positionToWorld(offsets)
spawnToken(tokenPosition, tokenType)
end
2022-11-24 11:01:03 -08:00
-- Spawn a group of tokens of the given type on the object
-- @param object Object to spawn the tokens on
-- @param tokenType Type of token to be spawned
-- @param tokenCount Number of tokens to spawn
-- @param shiftDown Amount to shift this group down to avoid spawning multiple token groups on
-- top of each other. Negative values are allowed, and will move the group up instead. This is
-- a static value and is unaware of how many tokens were spawned previously; callers should
-- ensure the proper shift.
function spawnTokenGroup(object, tokenType, tokenCount, shiftDown)
2022-11-12 12:43:35 +01:00
if (tokenCount < 1 or tokenCount > 12) then return end
local offsets = PLAYER_CARD_TOKEN_OFFSETS[tokenCount]
if shiftDown ~= nil then
-- Copy the offsets to make sure we don't change the static values
local baseOffsets = offsets
offsets = { }
for i, baseOffset in ipairs(baseOffsets) do
offsets[i] = baseOffset
offsets[i][3] = offsets[i][3] + shiftDown
end
end
2022-11-12 12:43:35 +01:00
if offsets == nil then error("couldn't find offsets for " .. tokenCount .. ' tokens') end
for i = 1, tokenCount do
spawnTokenOn(object, offsets[i], tokenType)
end
end
2022-11-12 12:43:35 +01:00
---------------------------------------------------------
-- playmat token spawning
---------------------------------------------------------
-- replenish Tokens for specific cards (like 'Physical Training (4)')
function replenishTokens(card, count, replenish)
local cardPos = card.getPosition()
-- don't continue for cards on your deck (Norman) or in your discard pile
if self.positionToLocal(cardPos).x < -1 then return end
2022-11-12 12:43:35 +01:00
-- get current amount of resource tokens on the card
local search = searchArea(cardPos, { 2.5, 0.5, 3.5 })
local foundTokens = 0
for _, obj in ipairs(search) do
local obj = obj.hit_object
if obj.getCustomObject().image == "http://cloud-3.steamusercontent.com/ugc/1758068501357192910/11DDDC7EF621320962FDCF3AE3211D5EDC3D1573/" then
foundTokens = foundTokens + math.abs(obj.getQuantity())
obj.destruct()
end
end
-- handling Runic Axe upgrade sheet for additional replenish
if card.getName() == "Runic Axe" then
for _, v in ipairs(searchArea(PLAY_ZONE_POSITION, PLAY_ZONE_SCALE)) do
local obj = v.hit_object
if obj.tag == "Card" then
local notes = JSON.decode(obj.getGMNotes()) or {}
if notes ~= nil and notes.id == "09022-c" then
if obj.getVar("markedBoxes")[7] == 3 then replenish = 2 end
break
end
end
end
end
local newCount = foundTokens + replenish
if newCount > count then newCount = count end
spawnTokenGroup(card, "resource", newCount)
end
function getPlayerCardData(object)
2022-11-12 12:43:35 +01:00
return PLAYER_CARDS[object.getName()..':'..object.getDescription()] or PLAYER_CARDS[object.getName()]
end
function shouldSpawnTokens(object)
2022-11-12 12:43:35 +01:00
-- don't spawn tokens if in doubt, this should only ever happen onLoad and prevents respawns
local spawned = DATA_HELPER.call('getSpawnedPlayerCardGuid', {object.getGUID()})
local hasDataHelperData = getPlayerCardData(object)
local cardMetadata = JSON.decode(object.getGMNotes()) or {}
local hasUses = cardMetadata.uses ~= nil
return not spawned and (hasDataHelperData or hasUses)
end
function markSpawned(object)
local saved = DATA_HELPER.call('setSpawnedPlayerCardGuid', {object.getGUID(), true})
2022-11-12 12:43:35 +01:00
if not saved then error('attempt to mark player card spawned before data loaded') end
end
2022-11-12 12:43:35 +01:00
-- contains position and amount of boxes for the upgradesheets that change uses
-- Alchemical Distillation, Damning Testimony, Living Ink and Hyperphysical Shotcaster
local customizationsTable = {
["09040"] = {5, 2},
["09059"] = {2, 2},
["09079"] = {3, 2},
["09119"] = {6, 4}
}
function spawnTokensFor(object)
local cardMetadata = JSON.decode(object.getGMNotes()) or {}
local type = nil
2022-11-12 12:43:35 +01:00
local token = nil
local tokenCount = 0
2022-11-12 12:43:35 +01:00
if cardMetadata.uses ~= nil then
-- Uses structure underwent a breaking change in 2.4.0, have to check to see if this is
-- a single entry or an array. This is ugly and duplicated, but impossible to replicate the
-- multi-spawn vs. single spawn otherwise. TODO: Clean this up when 2.4.0 has been out long
-- enough that saved decks don't have old data
if cardMetadata.uses.count != nil then
type = cardMetadata.uses.type
token = cardMetadata.uses.token
tokenCount = cardMetadata.uses.count
if activeInvestigatorId == "03004" and type == "Charge" then tokenCount = tokenCount + 1 end
log("Spawning tokens for "..object.getName()..'['..object.getDescription()..']: '..tokenCount.."x "..token)
spawnTokenGroup(object, token, tokenCount)
else
for i, useInfo in ipairs(cardMetadata.uses) do
type = useInfo.type
token = useInfo.token
tokenCount = useInfo.count
-- additional uses for certain customizable cards (by checking the upgradesheets)
if customizationsTable[cardMetadata.id] ~= nil then
for _, obj in ipairs(searchArea(PLAY_ZONE_POSITION, PLAY_ZONE_SCALE)) do
local obj = obj.hit_object
if obj.tag == "Card" then
local notes = JSON.decode(obj.getGMNotes()) or {}
if notes.id == (cardMetadata.id .. "-c") then
local pos = customizationsTable[cardMetadata.id][1]
local boxes = customizationsTable[cardMetadata.id][2]
if obj.getVar("markedBoxes")[pos] == boxes then tokenCount = tokenCount + 2 end
break
end
end
2022-11-12 12:43:35 +01:00
end
end
-- additional charge for Akachi
if activeInvestigatorId == "03004" and type == "Charge" then tokenCount = tokenCount + 1 end
2022-11-12 12:43:35 +01:00
log("Spawning tokens for "..object.getName()..'['..object.getDescription()..']: '..tokenCount.."x "..token)
-- Shift each spawned group after the first down so they don't pile on each other
spawnTokenGroup(object, token, tokenCount, (i - 1) * 0.6)
end
end
else
local data = getPlayerCardData(object)
token = data['tokenType']
tokenCount = data['tokenCount']
log("Spawning tokens for "..object.getName()..'['..object.getDescription()..']: '..tokenCount.."x "..token)
spawnTokenGroup(object, token, tokenCount)
end
markSpawned(object)
end
function resetSpawnState()
2022-11-12 12:43:35 +01:00
local zone = getObjectFromGUID(zoneID)
if zone == nil then return end
for _, object in ipairs(zone.getObjects()) do
if object.tag == "Card" then
unmarkSpawned(object.getGUID(), true)
elseif object.tag == "Deck" then
local cards = object.getObjects()
for _, v in ipairs(cards) do
unmarkSpawned(v.guid)
end
end
2022-11-12 12:43:35 +01:00
end
end
function unmarkSpawned(guid, force)
if not force and getObjectFromGUID(guid) ~= nil then return end
DATA_HELPER.call('setSpawnedPlayerCardGuid', {guid, false})
end
function onCollisionEnter(collision_info)
2022-11-12 12:43:35 +01:00
if not COLLISION_ENABLED then return end
Wait.time(resetSpawnState, 1)
2022-11-12 12:43:35 +01:00
local object = collision_info.collision_object
-- only continue for cards
if object.name ~= "Card" and object.name ~= "CardCustom" then return end
maybeUpdateActiveInvestigator(object)
2022-11-12 12:43:35 +01:00
-- don't spawn tokens for cards in discard pile / threat area
local localpos = self.positionToLocal(object.getPosition())
if localpos.x < -0.7 or localpos.z < -0.3 then
log('Not spawning tokens, relative coordinates are x: ' .. localpos.x .. ' z: ' .. localpos.z)
elseif not object.is_face_down and shouldSpawnTokens(object) then
spawnTokensFor(object)
end
end
2022-11-12 12:43:35 +01:00
---------------------------------------------------------
-- investigator ID grabbing and stat tracker
---------------------------------------------------------
function maybeUpdateActiveInvestigator(card)
2022-11-12 12:43:35 +01:00
local notes = JSON.decode(card.getGMNotes())
if notes ~= nil and notes.type == "Investigator" and notes.id ~= activeInvestigatorId then
activeInvestigatorId = notes.id
STAT_TRACKER.call("updateStats", {notes.willpowerIcons, notes.intellectIcons, notes.combatIcons, notes.agilityIcons})
-- change state of action tokens
local search = searchArea(self.positionToWorld({-1.1, 0.05, -0.27}), {4, 1, 1})
local small_token = nil
local state_table = {
["Guardian"] = 1,
["Seeker"] = 2,
["Rogue"] = 3,
["Mystic"] = 4,
["Survivor"] = 5,
["Neutral"] = 6
}
2022-11-12 12:43:35 +01:00
for _, obj in ipairs(search) do
local obj = obj.hit_object
if obj.getDescription() == "Action Token" and obj.getStateId() > 0 then
2022-11-16 18:58:59 +01:00
if obj.getScale().x < 0.4 then
2022-11-12 12:43:35 +01:00
small_token = obj
else
2022-11-16 18:58:59 +01:00
setObjectState(obj, state_table[notes.class])
2022-11-12 12:43:35 +01:00
end
end
end
-- update the small token with special action for certain investigators
-- Ursula Downs: Investigate action
if activeInvestigatorId == "04002" then
2022-11-16 18:58:59 +01:00
setObjectState(small_token, 8)
2022-11-12 12:43:35 +01:00
-- Daisy Walker (only for normal front, not parallel): Tome action
2022-11-16 18:58:59 +01:00
elseif activeInvestigatorId == "01002" or activeInvestigatorId == "01502" or activeInvestigatorId == "01002-pb" then
setObjectState(small_token, 9)
2022-11-12 12:43:35 +01:00
-- Tony Morgan: Engage/Fight action
elseif activeInvestigatorId == "06003" then
2022-11-16 18:58:59 +01:00
setObjectState(small_token, 10)
2022-11-12 12:43:35 +01:00
-- Finn Edwards: Evade action
elseif activeInvestigatorId == "04003" then
2022-11-16 18:58:59 +01:00
setObjectState(small_token, 11)
2022-11-12 12:43:35 +01:00
-- Bob Jenkins: Play Item action
elseif activeInvestigatorId == "08016" then
2022-11-16 18:58:59 +01:00
setObjectState(small_token, 14)
2022-11-12 12:43:35 +01:00
else
2022-11-16 18:58:59 +01:00
setObjectState(small_token, state_table[notes.class])
2022-11-12 12:43:35 +01:00
end
end
end
2022-11-16 18:58:59 +01:00
function setObjectState(obj, stateId)
2022-11-12 12:43:35 +01:00
if obj.getStateId() ~= stateId then obj.setState(stateId) end
end
2022-11-12 12:43:35 +01:00
---------------------------------------------------------
-- calls to 'Global' / functions for calls from outside
---------------------------------------------------------
function drawChaostokenButton(_, _, isRightClick)
Global.call("drawChaostoken", {self, DRAWN_CHAOS_TOKEN_OFFSET, isRightClick})
end
2022-11-12 12:43:35 +01:00
function drawEncountercard(_, _, isRightClick)
Global.call("drawEncountercard", {self.positionToWorld(DRAWN_ENCOUNTER_CARD_OFFSET), self.getRotation(), isRightClick})
end
function spawnToken(position, tokenType)
2022-11-12 12:43:35 +01:00
Global.call('spawnToken', {position, tokenType, PLAY_ZONE_ROTATION})
end
2022-12-08 12:01:10 +01:00
-- Sets this playermat's draw 1 button to visible
---@param visibleButton Boolean. Whether the draw 1 button should be visible
function showDrawButton(visibleButton)
drawButton = visibleButton
if drawButton then
self.createButton({
label = "Draw 1",
click_function = "doDrawOne",
function_owner = mat,
position = { 1.84, 0.1, -0.36 },
scale = { 0.12, 0.12, 0.12 },
width = 800,
height = 280,
font_size = 180
})
else
-- remove button with index 9 if 10 buttons are present (because index starts at 0)
if #self.getButtons() == 10 then
self.removeButton(9)
end
end
end
2022-12-08 12:46:22 +01:00
-- Spawns / destroys a clickable clue counter for this playmat with the correct amount of clues
2022-12-08 12:01:10 +01:00
---@param clickableCounter Boolean. Whether the clickable clue counter should be present
function clickableClues(clickableCounter)
2022-12-08 12:46:22 +01:00
local CLUE_COUNTER = getObjectFromGUID(CLUE_COUNTER_GUID)
2022-12-08 14:27:55 +01:00
local CLUE_CLICKER = getObjectFromGUID(CLUE_CLICKER_GUID)
local clickerPos = CLUE_CLICKER.getPosition()
2022-12-08 12:46:22 +01:00
local clueCount = 0
if clickableCounter then
-- current clue count
clueCount = CLUE_COUNTER.getVar("exposedValue")
-- remove clues
CLUE_COUNTER.call("removeAllClues")
2022-12-08 14:27:55 +01:00
-- set value for clue clickers
CLUE_CLICKER.call("updateVal", clueCount)
2022-12-08 12:46:22 +01:00
2022-12-08 14:27:55 +01:00
-- move clue counters up
clickerPos.y = 1.52
CLUE_CLICKER.setPosition(clickerPos)
2022-12-08 12:46:22 +01:00
else
-- current clue count
2022-12-08 14:27:55 +01:00
clueCount = CLUE_CLICKER.getVar("val")
2022-12-08 12:46:22 +01:00
2022-12-08 14:27:55 +01:00
-- move clue counters down
clickerPos.y = 1.3
CLUE_CLICKER.setPosition(clickerPos)
2022-12-08 12:46:22 +01:00
-- spawn clues
2022-12-08 14:27:55 +01:00
local pos = self.positionToWorld({x = -1.12, y = 0.05, z = 0.7})
2022-12-08 12:46:22 +01:00
for i = 1, clueCount do
2022-12-08 14:27:55 +01:00
pos.y = pos.y + 0.045 * i
2022-12-08 12:46:22 +01:00
spawnToken(pos, "clue")
end
end
2022-12-08 12:01:10 +01:00
end
-- Sets this playermat's snap points to limit snapping to matching card types or not. If matchTypes
-- is true, the main card slot snap points will only snap assets, while the investigator area point
-- will only snap Investigators. If matchTypes is false, snap points will be reset to snap all
-- cards.
2022-12-08 12:01:10 +01:00
---@param matchTypes Boolean. Whether snap points should only snap for the matching card types.
function setLimitSnapsByType(matchTypes)
local snaps = self.getSnapPoints()
for i, snap in ipairs(snaps) do
local snapPos = snap.position
if inArea(snapPos, MAIN_PLAY_AREA) then
local snapTags = snaps[i].tags
if matchTypes then
if snapTags == nil then
snaps[i].tags = { "Asset" }
else
table.insert(snaps[i].tags, "Asset")
end
else
snaps[i].tags = nil
end
end
if inArea(snapPos, INVESTIGATOR_AREA) then
local snapTags = snaps[i].tags
if matchTypes then
if snapTags == nil then
snaps[i].tags = { "Investigator" }
else
table.insert(snaps[i].tags, "Investigator")
end
else
snaps[i].tags = nil
end
end
end
self.setSnapPoints(snaps)
end
-- Simple method to check if the given point is in a specified area. Local use only,
-- @param point Vector. Point to check, only x and z values are relevant
-- @param bounds Table. Defined area to see if the point is within. See MAIN_PLAY_AREA for sample
-- bounds definition.
-- @return Boolean. True if the point is in the area defined by bounds
function inArea(point, bounds)
return (point.x < bounds.upperLeft.x
and point.x > bounds.lowerRight.x
and point.z < bounds.upperLeft.z
and point.z > bounds.lowerRight.z)
end
2022-11-16 18:58:59 +01:00
-- called by custom data helpers to add player card data
---@param args table Contains only one entry, the GUID of the custom data helper
function updatePlayerCards(args)
2022-11-12 12:43:35 +01:00
local custom_data_helper = getObjectFromGUID(args[1])
data_player_cards = custom_data_helper.getTable("PLAYER_CARD_DATA")
for k, v in pairs(data_player_cards) do
PLAYER_CARDS[k] = v
end
end