2022-11-12 12:43:35 +01:00
|
|
|
---------------------------------------------------------
|
|
|
|
-- general setup (identical for each playmat)
|
|
|
|
---------------------------------------------------------
|
|
|
|
|
|
|
|
-- set true to enable debug logging and show Physics.cast()
|
|
|
|
local DEBUG = false
|
|
|
|
|
|
|
|
-- 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 PLAY_ZONE_ROTATION = self.getRotation()
|
|
|
|
activeInvestigatorId = "00000"
|
|
|
|
|
|
|
|
---------------------------------------------------------
|
|
|
|
-- general code (identical for each playmat)
|
|
|
|
---------------------------------------------------------
|
|
|
|
|
|
|
|
function onSave() return JSON.encode({zoneID = zoneID, playerColor = PLAYER_COLOR, activeInvestigatorId = activeInvestigatorId}) end
|
|
|
|
|
|
|
|
function onLoad(save_state)
|
2021-10-06 20:37:31 -07:00
|
|
|
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-12 12:43:35 +01:00
|
|
|
for i = 1, 6 do
|
|
|
|
makeDiscardButton(DISCARD_BUTTON_OFFSETS[i], {-3.85, 3, 10.38}, i)
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
self.createButton({
|
2022-11-12 12:43:35 +01:00
|
|
|
label = "Upkeep",
|
2021-10-06 20:37:31 -07:00
|
|
|
click_function = "doUpkeep",
|
|
|
|
function_owner = self,
|
2022-11-12 12:43:35 +01:00
|
|
|
position = {1.84, 0.1, -0.44},
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
if getObjectFromGUID(zoneID) == nil then spawnDeckZone() end
|
2021-10-06 20:37:31 -07:00
|
|
|
COLLISION_ENABLED = true
|
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
---------------------------------------------------------
|
|
|
|
-- utility functions
|
|
|
|
---------------------------------------------------------
|
|
|
|
|
|
|
|
function log(message)
|
|
|
|
if DEBUG then print(message) end
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
-- send messages to player who clicked button if no seated player found
|
2021-10-06 20:37:31 -07:00
|
|
|
function setMessageColor(color)
|
2022-11-12 12:43:35 +01:00
|
|
|
messageColor = Player[PLAYER_COLOR].seated and PLAYER_COLOR or color
|
2021-10-06 20:37:31 -07:00
|
|
|
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()
|
|
|
|
})
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
function doNotReady(card) return card.getVar("do_not_ready") or false end
|
2021-10-06 20:37:31 -07:00
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
---------------------------------------------------------
|
|
|
|
-- Discard buttons
|
|
|
|
---------------------------------------------------------
|
2022-10-25 00:36:45 -07:00
|
|
|
|
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)
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
|
|
|
end
|
2022-11-12 12:43:35 +01:00
|
|
|
end
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
2021-10-06 20:37:31 -07:00
|
|
|
|
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
|
|
|
|
})
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
|
|
|
})
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
---------------------------------------------------------
|
|
|
|
-- Upkeep button
|
|
|
|
---------------------------------------------------------
|
2022-10-25 00:36:45 -07:00
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
function doUpkeep(_, color, alt_click)
|
|
|
|
setMessageColor(color)
|
2022-10-25 00:36:45 -07:00
|
|
|
|
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
|
|
|
|
|
|
|
|
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.tag == "Card" and not obj.is_face_down and not doNotReady(obj) then
|
|
|
|
local notes = JSON.decode(obj.getGMNotes()) or {}
|
|
|
|
if notes.id == "08031" then
|
|
|
|
forcedLearning = true
|
|
|
|
elseif notes.type ~= "Investigator" then
|
|
|
|
obj.setRotation(PLAY_ZONE_ROTATION)
|
|
|
|
|
|
|
|
-- check for cards with 'replenish' in their metadata
|
|
|
|
if notes.uses ~= nil then
|
|
|
|
local count = notes.uses[1].count
|
|
|
|
local replenish = notes.uses[1].replenish
|
|
|
|
if count and replenish then replenishTokens(obj, count, replenish) end
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
2022-11-12 12:43:35 +01:00
|
|
|
end
|
|
|
|
elseif obj.getDescription() == "Action Token" and obj.is_face_down then
|
|
|
|
obj.flip()
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
2022-11-12 12:43:35 +01:00
|
|
|
end
|
2021-10-06 20:37:31 -07:00
|
|
|
|
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
|
|
|
|
if obj.tag == "Card" then
|
|
|
|
local notes = JSON.decode(obj.getGMNotes())
|
|
|
|
if obj.is_face_down and notes ~= nil and notes.type == "Minicard" and (notes.id == miniId or notes.id == "09080-m") then
|
|
|
|
obj.flip()
|
|
|
|
end
|
|
|
|
end
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
2022-11-12 12:43:35 +01:00
|
|
|
end
|
2021-10-06 20:37:31 -07:00
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
-- gain a resource
|
|
|
|
RESOURCE_COUNTER.call("add_subtract")
|
2021-10-06 20:37:31 -07:00
|
|
|
|
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
|
|
|
|
RESOURCE_COUNTER.call("add_subtract")
|
|
|
|
printToColor("Gaining 2 resources (Jenny)", messageColor)
|
|
|
|
end
|
2022-10-25 00:36:45 -07:00
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
-- draw a card (with handling for Patrice and Forced Learning)
|
|
|
|
if activeInvestigatorId == "06005" then
|
2022-10-25 00:36:45 -07:00
|
|
|
local handSize = #Player[PLAYER_COLOR].getHandObjects()
|
|
|
|
if handSize >= 5 then return end
|
|
|
|
local cardsToDraw = 5 - handSize
|
|
|
|
printToColor("Drawing " .. cardsToDraw .. " cards (Patrice)", messageColor)
|
|
|
|
drawCardsWithReshuffle(cardsToDraw)
|
2022-11-12 12:43:35 +01:00
|
|
|
elseif forcedLearning then
|
2022-10-25 00:36:45 -07:00
|
|
|
printToColor("Drawing 2 cards, discard 1 (Forced Learning)", messageColor)
|
|
|
|
drawCardsWithReshuffle(2)
|
2022-11-12 12:43:35 +01:00
|
|
|
else
|
|
|
|
drawCardsWithReshuffle(1)
|
|
|
|
end
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
-- legacy function for "add draw 1 button"
|
|
|
|
function doDrawOne(_, color)
|
|
|
|
setMessageColor(color)
|
|
|
|
drawCardsWithReshuffle(1)
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
-- draw X cards (shuffle discards if necessary)
|
|
|
|
function drawCardsWithReshuffle(numCards)
|
|
|
|
if type(numCards) ~= "number" then numCards = 1 end
|
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
|
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)
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
function spawnTokenOn(object, offsets, tokenType)
|
|
|
|
local tokenPosition = object.positionToWorld(offsets)
|
|
|
|
spawnToken(tokenPosition, tokenType)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- spawn a group of tokens of the given type on the object
|
|
|
|
function spawnTokenGroup(object, tokenType, tokenCount)
|
2022-11-12 12:43:35 +01:00
|
|
|
if (tokenCount < 1 or tokenCount > 12) then return end
|
2021-10-06 20:37:31 -07:00
|
|
|
local offsets = PLAYER_CARD_TOKEN_OFFSETS[tokenCount]
|
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)
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
|
|
|
|
|
|
|
-- 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)
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
function getPlayerCardData(object)
|
2022-11-12 12:43:35 +01:00
|
|
|
return PLAYER_CARDS[object.getName()..':'..object.getDescription()] or PLAYER_CARDS[object.getName()]
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
local spawned = DATA_HELPER.call('getSpawnedPlayerCardGuid', {object.getGUID()})
|
2022-10-25 00:36:45 -07:00
|
|
|
local hasDataHelperData = getPlayerCardData(object)
|
|
|
|
local cardMetadata = JSON.decode(object.getGMNotes()) or {}
|
|
|
|
local hasUses = cardMetadata.uses ~= nil
|
|
|
|
return not spawned and (hasDataHelperData or hasUses)
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
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}
|
|
|
|
}
|
|
|
|
|
2021-10-06 20:37:31 -07:00
|
|
|
function spawnTokensFor(object)
|
2022-10-25 00:36:45 -07:00
|
|
|
local cardMetadata = JSON.decode(object.getGMNotes()) or {}
|
|
|
|
local type = nil
|
2022-11-12 12:43:35 +01:00
|
|
|
local token = nil
|
2022-10-25 00:36:45 -07:00
|
|
|
local tokenCount = 0
|
2022-11-12 12:43:35 +01:00
|
|
|
if cardMetadata.uses ~= nil then
|
|
|
|
for _, useInfo in ipairs(cardMetadata.uses) do
|
2022-10-25 00:36:45 -07:00
|
|
|
type = useInfo.type
|
2022-11-12 12:43:35 +01:00
|
|
|
token = useInfo.token
|
2022-10-25 00:36:45 -07:00
|
|
|
tokenCount = useInfo.count
|
2022-11-12 12:43:35 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
-- 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-10-25 00:36:45 -07:00
|
|
|
end
|
|
|
|
end
|
2022-11-12 12:43:35 +01:00
|
|
|
|
|
|
|
-- additional charge for Akachi
|
|
|
|
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)
|
2022-10-25 00:36:45 -07:00
|
|
|
else
|
|
|
|
local data = getPlayerCardData(object)
|
2022-11-12 12:43:35 +01:00
|
|
|
if data == nil then error('attempt to spawn tokens for ' .. object.getName() .. ': no token data') end
|
2022-10-25 00:36:45 -07:00
|
|
|
token = data['tokenType']
|
|
|
|
tokenCount = data['tokenCount']
|
|
|
|
log(object.getName() .. '[' .. object.getDescription() .. ']' .. ' : ' .. data['tokenType'] .. ' : ' .. data['tokenCount'])
|
|
|
|
log("Spawning tokens for "..object.getName()..'['..object.getDescription()..']: '..tokenCount.."x "..token)
|
|
|
|
spawnTokenGroup(object, token, tokenCount)
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
2022-11-12 12:43:35 +01:00
|
|
|
end
|
2021-10-06 20:37:31 -07:00
|
|
|
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
|
2021-10-06 20:37:31 -07:00
|
|
|
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)
|
|
|
|
|
|
|
|
-- 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
|
2021-10-06 20:37:31 -07:00
|
|
|
spawnTokensFor(object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
---------------------------------------------------------
|
|
|
|
-- investigator ID grabbing and stat tracker
|
|
|
|
---------------------------------------------------------
|
|
|
|
|
2022-10-25 00:36:45 -07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, obj in ipairs(search) do
|
|
|
|
local obj = obj.hit_object
|
|
|
|
if obj.getDescription() == "Action Token" and obj.getStateId() > 0 then
|
|
|
|
if self.positionToLocal(obj.getPosition()).x > -0.95 then
|
|
|
|
small_token = obj
|
|
|
|
else
|
|
|
|
objState(obj, state_table[notes.class])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- update the small token with special action for certain investigators
|
|
|
|
-- Ursula Downs: Investigate action
|
|
|
|
if activeInvestigatorId == "04002" then
|
|
|
|
objState(small_token, 8)
|
|
|
|
-- Daisy Walker (only for normal front, not parallel): Tome action
|
|
|
|
elseif activeInvestigatorId == "01002" or activeInvestigatorId == "01002-r" or activeInvestigatorId == "01002-pb" then
|
|
|
|
objState(small_token, 9)
|
|
|
|
-- Tony Morgan: Engage/Fight action
|
|
|
|
elseif activeInvestigatorId == "06003" then
|
|
|
|
objState(small_token, 10)
|
|
|
|
-- Finn Edwards: Evade action
|
|
|
|
elseif activeInvestigatorId == "04003" then
|
|
|
|
objState(small_token, 11)
|
|
|
|
-- Bob Jenkins: Play Item action
|
|
|
|
elseif activeInvestigatorId == "08016" then
|
|
|
|
objState(small_token, 14)
|
|
|
|
else
|
|
|
|
objState(small_token, state_table[notes.class])
|
|
|
|
end
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
function objState(obj, stateId)
|
|
|
|
if obj.getStateId() ~= stateId then obj.setState(stateId) end
|
2022-10-25 00:36:45 -07:00
|
|
|
end
|
|
|
|
|
2022-11-12 12:43:35 +01:00
|
|
|
---------------------------------------------------------
|
|
|
|
-- calls to 'Global' / functions for calls from outside
|
|
|
|
---------------------------------------------------------
|
|
|
|
|
|
|
|
function drawChaostokenButton(_, _, isRightClick)
|
2021-10-06 20:37:31 -07:00
|
|
|
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})
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
function spawnToken(position, tokenType)
|
2022-11-12 12:43:35 +01:00
|
|
|
Global.call('spawnToken', {position, tokenType, PLAY_ZONE_ROTATION})
|
2021-10-06 20:37:31 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
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
|