2020-11-28 13:23:58 -05:00
|
|
|
-- set true to enable debug logging
|
|
|
|
DEBUG = false
|
|
|
|
-- we use this to turn off collision handling (for clue spawning)
|
|
|
|
-- until after load is complete (probably a better way to do this)
|
|
|
|
COLLISION_ENABLED = false
|
|
|
|
-- position offsets, adjust these to reposition things relative to mat [x,y,z]
|
2022-10-19 19:07:47 -04:00
|
|
|
DRAWN_ENCOUNTER_CARD_OFFSET = {1.365, 0.5, -0.635}
|
|
|
|
DRAWN_CHAOS_TOKEN_OFFSET = {-1.55, 0.5, -0.58}
|
2020-11-28 13:23:58 -05:00
|
|
|
DISCARD_BUTTON_OFFSETS = {
|
2020-11-29 00:11:43 -05:00
|
|
|
{-0.98, 0.2, -0.35},
|
|
|
|
{-0.525, 0.2, -0.35},
|
|
|
|
{-0.07, 0.2, -0.35},
|
|
|
|
{0.39, 0.2, -0.35},
|
|
|
|
{0.84, 0.2, -0.35},
|
2020-11-28 13:23:58 -05:00
|
|
|
}
|
|
|
|
-- draw deck and discard zone
|
|
|
|
DECK_POSITION = { x=-1.4, y=0, z=0.3 }
|
|
|
|
DECK_ZONE_SCALE = { x=3, y=5, z=8 }
|
2021-02-14 13:17:53 -05:00
|
|
|
DRAW_DECK_POSITION = { x=-26.46, y=1.67, z=-39.15 }
|
2020-11-28 13:23:58 -05:00
|
|
|
|
|
|
|
-- play zone
|
|
|
|
PLAYER_COLOR = "Red"
|
|
|
|
PLAY_ZONE_POSITION = { x=-25, y=4, z=-27 }
|
2021-02-14 13:17:53 -05:00
|
|
|
PLAY_ZONE_ROTATION = { x=0, y=270, z=0 }
|
2020-11-28 13:23:58 -05:00
|
|
|
PLAY_ZONE_SCALE = { x=30, y=5, z=15 }
|
|
|
|
|
|
|
|
RESOURCE_COUNTER_GUID = "a4b60d"
|
|
|
|
|
|
|
|
-- the position of the global discard pile
|
|
|
|
-- TODO: delegate to global for any auto discard actions
|
|
|
|
DISCARD_POSITION = {-3.85, 3, 10.38}
|
2022-10-19 19:07:47 -04:00
|
|
|
-- DISCARD PILE POSITION
|
|
|
|
DISCARD_PILE_POSITION = { -18.79, 4, -30.84 }
|
|
|
|
|
|
|
|
local activeInvestigatorId = nil
|
|
|
|
local willpowerTokenGuid = "8b6743"
|
|
|
|
local intellectTokenGuid = "b4ef12"
|
|
|
|
local combatTokenGuid = "93a48b"
|
|
|
|
local agilityTokenGuid = "2c3f8d"
|
2020-11-28 13:23:58 -05:00
|
|
|
|
|
|
|
function log(message)
|
|
|
|
if DEBUG then
|
|
|
|
print(message)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- builds a function that discards things in searchPostion to discardPostition
|
|
|
|
function makeDiscardHandlerFor(searchPosition, discardPosition)
|
|
|
|
return function (_)
|
|
|
|
local discardItemList = findObjectsAtPosition(searchPosition)
|
|
|
|
for _, obj in ipairs(discardItemList) do
|
|
|
|
obj.setPositionSmooth(discardPosition, false, true)
|
|
|
|
obj.setRotation({0, -90, 0})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- build a discard button at position to discard from searchPosition to discardPosition
|
|
|
|
-- number must be unique
|
|
|
|
function makeDiscardButton(position, searchPosition, discardPosition, number)
|
|
|
|
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
|
|
|
|
|
|
|
|
function onload(save_state)
|
2022-10-19 19:07:47 -04:00
|
|
|
|
2020-11-28 13:23:58 -05: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')
|
|
|
|
|
|
|
|
-- positions of encounter card slots
|
|
|
|
local encounterSlots = {
|
2022-10-19 19:07:47 -04:00
|
|
|
{1.365, 0, -0.7},
|
|
|
|
{0.91, 0, -0.7},
|
|
|
|
{0.455, 0, -0.7},
|
|
|
|
{0, 0, -0.7},
|
|
|
|
{-0.455, 0, -0.7},
|
|
|
|
{-0.91, 0, -0.7},
|
2020-11-28 13:23:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
local i = 1
|
2022-10-19 19:07:47 -04:00
|
|
|
while i <= 6 do
|
2022-11-26 20:35:57 -05:00
|
|
|
position = Vector(encounterSlots[i]) * Vector(-1, 1, 1) + Vector(0, 0.2, 0.35)
|
|
|
|
makeDiscardButton(position, encounterSlots[i], DISCARD_POSITION, i)
|
2020-11-28 13:23:58 -05:00
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
|
|
|
|
self.createButton({
|
|
|
|
label = " ",
|
|
|
|
click_function = "drawEncountercard",
|
|
|
|
function_owner = self,
|
2022-10-19 19:07:47 -04:00
|
|
|
position = {-1.88,0,-0.7},
|
2020-11-28 13:23:58 -05:00
|
|
|
rotation = {0,-15,0},
|
|
|
|
width = 170,
|
|
|
|
height = 255,
|
|
|
|
font_size = 50
|
|
|
|
})
|
|
|
|
|
|
|
|
self.createButton({
|
|
|
|
label=" ",
|
|
|
|
click_function = "drawChaostokenButton",
|
|
|
|
function_owner = self,
|
2022-10-19 19:07:47 -04:00
|
|
|
position = {1.84,0.0,-0.74},
|
2020-11-28 13:23:58 -05:00
|
|
|
rotation = {0,-45,0},
|
|
|
|
width = 125,
|
|
|
|
height = 125,
|
|
|
|
font_size = 50
|
|
|
|
})
|
|
|
|
|
|
|
|
self.createButton({
|
2021-09-03 13:52:12 -04:00
|
|
|
label="Upkeep",
|
|
|
|
click_function = "doUpkeep",
|
|
|
|
function_owner = self,
|
2022-10-19 19:07:47 -04:00
|
|
|
position = {1.84,0.1,-0.44},
|
2021-09-03 13:52:12 -04:00
|
|
|
scale = {0.12, 0.12, 0.12},
|
|
|
|
width = 800,
|
|
|
|
height = 280,
|
|
|
|
font_size = 180
|
|
|
|
})
|
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
local state = JSON.decode(save_state)
|
2021-02-13 12:12:29 -05:00
|
|
|
if state ~= nil then
|
|
|
|
if state.playerColor ~= nil then
|
|
|
|
PLAYER_COLOR = state.playerColor
|
|
|
|
end
|
|
|
|
if state.zoneID ~= nil then
|
|
|
|
zoneID = state.zoneID
|
|
|
|
Wait.time(checkDeckZoneExists, 30)
|
|
|
|
else
|
|
|
|
spawnDeckZone()
|
|
|
|
end
|
2020-11-28 13:23:58 -05:00
|
|
|
else
|
|
|
|
spawnDeckZone()
|
|
|
|
end
|
|
|
|
|
|
|
|
COLLISION_ENABLED = true
|
|
|
|
end
|
|
|
|
|
|
|
|
function onSave()
|
2021-02-13 12:12:29 -05:00
|
|
|
return JSON.encode({ zoneID=zoneID, playerColor=PLAYER_COLOR })
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
|
2021-09-03 13:52:12 -04:00
|
|
|
function setMessageColor(color)
|
|
|
|
-- send messages to player who clicked button if no seated player found
|
|
|
|
messageColor = Player[PLAYER_COLOR].seated and PLAYER_COLOR or color
|
|
|
|
end
|
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
-- get the draw deck and discard pile objects
|
|
|
|
function getDrawDiscardDecks()
|
2021-09-03 13:52:12 -04:00
|
|
|
drawDeck = nil
|
|
|
|
discardPile = nil
|
2022-03-27 10:12:31 -04:00
|
|
|
topCard = nil
|
2021-11-16 23:41:43 -05:00
|
|
|
|
|
|
|
local zone = getObjectFromGUID(zoneID)
|
|
|
|
if zone == nil then return end
|
|
|
|
|
2021-09-03 13:52:12 -04:00
|
|
|
for i,object in ipairs(zone.getObjects()) do
|
|
|
|
if object.tag == "Deck" or object.tag == "Card" then
|
2022-03-27 10:12:31 -04:00
|
|
|
local relativePos = self.positionToLocal(object.getPosition())
|
|
|
|
if relativePos.z > 0.5 then
|
2021-09-03 13:52:12 -04:00
|
|
|
discardPile = object
|
2022-03-27 10:12:31 -04:00
|
|
|
else
|
|
|
|
if investigator == "Norman Withers" and object.tag == "Card" and not object.is_face_down then
|
|
|
|
topCard = object
|
|
|
|
else
|
|
|
|
drawDeck = object
|
|
|
|
end
|
2021-09-03 13:52:12 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
function doUpkeep(obj, color, alt_click)
|
|
|
|
-- right-click binds to new player color
|
|
|
|
if alt_click then
|
|
|
|
PLAYER_COLOR = color
|
|
|
|
printToColor("Upkeep button bound to " .. color, color)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2021-09-03 13:52:12 -04:00
|
|
|
setMessageColor(color)
|
2021-04-10 00:44:08 -04:00
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
-- unexhaust cards in play zone
|
|
|
|
local objs = Physics.cast({
|
|
|
|
origin = PLAY_ZONE_POSITION,
|
|
|
|
direction = { x=0, y=1, z=0 },
|
|
|
|
type = 3,
|
|
|
|
size = PLAY_ZONE_SCALE,
|
|
|
|
orientation = PLAY_ZONE_ROTATION
|
|
|
|
})
|
|
|
|
|
|
|
|
local y = PLAY_ZONE_ROTATION.y
|
|
|
|
|
2022-03-27 10:12:31 -04:00
|
|
|
investigator = nil
|
2021-11-16 23:41:43 -05:00
|
|
|
local miniId = nil
|
|
|
|
local forcedLearning = false
|
2020-11-28 13:23:58 -05:00
|
|
|
for i,v in ipairs(objs) do
|
|
|
|
local obj = v.hit_object
|
2021-11-16 23:41:43 -05:00
|
|
|
local props = obj.getCustomObject() or {}
|
2021-04-10 00:44:08 -04:00
|
|
|
if obj.tag == "Card" and not obj.is_face_down and not doNotReady(obj) then
|
2021-11-16 23:41:43 -05:00
|
|
|
local notes = JSON.decode(obj.getGMNotes()) or {}
|
|
|
|
local name = obj.getName()
|
|
|
|
if notes.type == "Investigator" and notes.id ~= nil then
|
2022-10-19 19:07:47 -04:00
|
|
|
miniId = string.match(notes.id, "%d%d%d%d%d%d-") .. "-m"
|
2021-11-16 23:41:43 -05:00
|
|
|
end
|
|
|
|
if notes.type == "Investigator" or props.unique_back then
|
2020-11-28 13:23:58 -05:00
|
|
|
if string.match(name, "Jenny Barnes") ~= nil then
|
|
|
|
investigator = "Jenny Barnes"
|
|
|
|
elseif name == "Patrice Hathaway" then
|
|
|
|
investigator = name
|
2022-03-27 10:12:31 -04:00
|
|
|
elseif string.match(name, "Norman Withers") ~= nil then
|
|
|
|
investigator = "Norman Withers"
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
2021-11-16 23:41:43 -05:00
|
|
|
elseif name == "Forced Learning" then
|
|
|
|
forcedLearning = true
|
2020-11-28 13:23:58 -05:00
|
|
|
else
|
|
|
|
local r = obj.getRotation()
|
|
|
|
if (r.y - y > 10) or (y - r.y > 10) then
|
2021-02-13 12:12:29 -05:00
|
|
|
obj.setRotation(PLAY_ZONE_ROTATION)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
end
|
2022-10-19 19:07:47 -04:00
|
|
|
elseif obj.getDescription() == "Action Token" then
|
2020-12-06 09:42:32 -05:00
|
|
|
if obj.is_face_down then obj.flip() end
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
-- flip investigator mini-card if found
|
|
|
|
if miniId ~= nil then
|
|
|
|
objs = getObjects()
|
2022-10-19 19:07:47 -04:00
|
|
|
for i, obj in ipairs(objs) do
|
2021-11-16 23:41:43 -05:00
|
|
|
if obj.tag == "Card" then
|
|
|
|
local notes = JSON.decode(obj.getGMNotes())
|
|
|
|
if notes ~= nil and notes.type == "Minicard" and notes.id == miniId then
|
|
|
|
if obj.is_face_down then
|
|
|
|
obj.flip()
|
|
|
|
end
|
|
|
|
goto done
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
::done::
|
|
|
|
|
2022-10-19 19:07:47 -04:00
|
|
|
-- flip summoned servitor mini-cards (To-Do: don't flip all of them)
|
|
|
|
for i, obj in ipairs(getObjects()) do
|
|
|
|
if obj.tag == "Card" then
|
|
|
|
local notes = JSON.decode(obj.getGMNotes())
|
|
|
|
if notes ~= nil and notes.type == "Minicard" and notes.id == "09080-m" then
|
|
|
|
if obj.is_face_down then
|
|
|
|
obj.flip()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
-- gain resource
|
|
|
|
getObjectFromGUID(RESOURCE_COUNTER_GUID).call("add_subtract")
|
|
|
|
if investigator == "Jenny Barnes" then
|
|
|
|
getObjectFromGUID(RESOURCE_COUNTER_GUID).call("add_subtract")
|
2022-10-19 19:07:47 -04:00
|
|
|
printToColor("Gaining 2 resources (Jenny)", messageColor)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
|
2021-09-03 13:52:12 -04:00
|
|
|
-- special draw for Patrice Hathaway (shuffle discards if necessary)
|
2020-11-28 13:23:58 -05:00
|
|
|
if investigator == "Patrice Hathaway" then
|
|
|
|
patriceDraw()
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
-- special draw for Forced Learning
|
|
|
|
if forcedLearning then
|
|
|
|
forcedLearningDraw()
|
2021-09-03 13:52:12 -04:00
|
|
|
return
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
2021-09-03 13:52:12 -04:00
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
drawCardsWithReshuffle(1)
|
|
|
|
end
|
2021-09-03 13:52:12 -04:00
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
function doDrawOne(obj, color)
|
|
|
|
setMessageColor(color)
|
|
|
|
drawCardsWithReshuffle(1)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
|
2021-04-10 00:44:08 -04:00
|
|
|
function doNotReady(card)
|
|
|
|
if card.getVar("do_not_ready") == true then
|
|
|
|
return true
|
|
|
|
else
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
-- draw X cards (shuffle discards if necessary)
|
|
|
|
function drawCardsWithReshuffle(numCards)
|
|
|
|
if type(numCards) ~= "number" then numCards = 1 end
|
2020-11-28 13:23:58 -05:00
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
getDrawDiscardDecks()
|
2020-11-28 13:23:58 -05:00
|
|
|
|
2022-03-27 10:12:31 -04:00
|
|
|
if investigator == "Norman Withers" then
|
|
|
|
local harbinger = false
|
|
|
|
if topCard ~= nil and topCard.getName() == "The Harbinger" then
|
|
|
|
harbinger = true
|
|
|
|
else
|
|
|
|
if drawDeck ~= nil and not drawDeck.is_face_down then
|
|
|
|
local cards = drawDeck.getObjects()
|
|
|
|
local bottomCard = cards[#cards]
|
|
|
|
if bottomCard.name == "The Harbinger" then
|
|
|
|
harbinger = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if harbinger then
|
|
|
|
printToColor("The Harbinger is on top of your deck, not drawing cards", messageColor)
|
|
|
|
return -1
|
|
|
|
end
|
|
|
|
|
|
|
|
if topCard ~= nil then
|
|
|
|
topCard.deal(numCards, PLAYER_COLOR)
|
|
|
|
numCards = numCards - 1
|
|
|
|
if numCards == 0 then return end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
local deckSize
|
|
|
|
if drawDeck == nil then
|
|
|
|
deckSize = 0
|
|
|
|
elseif drawDeck.tag == "Deck" then
|
|
|
|
deckSize = #drawDeck.getObjects()
|
|
|
|
else
|
|
|
|
deckSize = 1
|
|
|
|
end
|
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
if deckSize >= numCards then
|
|
|
|
drawCards(numCards)
|
2020-11-28 13:23:58 -05:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
drawCards(deckSize)
|
|
|
|
if discardPile ~= nil then
|
|
|
|
shuffleDiscardIntoDeck()
|
2021-11-16 23:41:43 -05:00
|
|
|
Wait.time(|| drawCards(numCards - deckSize), 1)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
2021-04-10 00:44:08 -04:00
|
|
|
printToColor("Take 1 horror (drawing card from empty deck)", messageColor)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
|
2021-11-16 23:41:43 -05:00
|
|
|
function drawCards(numCards)
|
|
|
|
if drawDeck == nil then return end
|
|
|
|
drawDeck.deal(numCards, PLAYER_COLOR)
|
|
|
|
end
|
|
|
|
|
|
|
|
function shuffleDiscardIntoDeck()
|
|
|
|
discardPile.flip()
|
|
|
|
discardPile.shuffle()
|
|
|
|
discardPile.setPositionSmooth(DRAW_DECK_POSITION, false, false)
|
|
|
|
drawDeck = discardPile
|
|
|
|
discardPile = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
function patriceDraw()
|
|
|
|
local handSize = #Player[PLAYER_COLOR].getHandObjects()
|
|
|
|
if handSize >= 5 then return end
|
|
|
|
local cardsToDraw = 5 - handSize
|
|
|
|
printToColor("Drawing " .. cardsToDraw .. " cards (Patrice)", messageColor)
|
|
|
|
drawCardsWithReshuffle(cardsToDraw)
|
|
|
|
end
|
|
|
|
|
|
|
|
function forcedLearningDraw()
|
|
|
|
printToColor("Drawing 2 cards, discard 1 (Forced Learning)", messageColor)
|
|
|
|
drawCardsWithReshuffle(2)
|
|
|
|
end
|
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
function checkDeckZoneExists()
|
|
|
|
if getObjectFromGUID(zoneID) ~= nil then return end
|
|
|
|
spawnDeckZone()
|
|
|
|
end
|
|
|
|
|
|
|
|
function spawnDeckZone()
|
|
|
|
local pos = self.positionToWorld(DECK_POSITION)
|
|
|
|
local zoneProps = {
|
|
|
|
position = pos,
|
|
|
|
scale = DECK_ZONE_SCALE,
|
|
|
|
type = 'ScriptingTrigger',
|
|
|
|
callback = 'zoneCallback',
|
|
|
|
callback_owner = self,
|
|
|
|
rotation = self.getRotation()
|
|
|
|
}
|
|
|
|
spawnObject(zoneProps)
|
|
|
|
end
|
|
|
|
|
|
|
|
function zoneCallback(zone)
|
|
|
|
zoneID = zone.getGUID()
|
|
|
|
end
|
|
|
|
|
|
|
|
function findObjectsAtPosition(localPos)
|
|
|
|
local globalPos = self.positionToWorld(localPos)
|
|
|
|
local objList = Physics.cast({
|
|
|
|
origin=globalPos, --Where the cast takes place
|
|
|
|
direction={0,1,0}, --Which direction it moves (up is shown)
|
|
|
|
type=2, --Type. 2 is "sphere"
|
|
|
|
size={2,2,2}, --How large that sphere is
|
|
|
|
max_distance=1, --How far it moves. Just a little bit
|
|
|
|
debug=false --If it displays the sphere when casting.
|
|
|
|
})
|
|
|
|
local decksAndCards = {}
|
|
|
|
for _, obj in ipairs(objList) do
|
|
|
|
if obj.hit_object.tag == "Deck" or obj.hit_object.tag == "Card" then
|
|
|
|
table.insert(decksAndCards, obj.hit_object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return decksAndCards
|
|
|
|
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-10-19 19:07:47 -04:00
|
|
|
if (tokenCount < 1 or tokenCount > 12) then
|
|
|
|
return
|
|
|
|
end
|
2020-11-28 13:23:58 -05:00
|
|
|
local offsets = PLAYER_CARD_TOKEN_OFFSETS[tokenCount]
|
|
|
|
if offsets == nil then
|
|
|
|
error("couldn't find offsets for " .. tokenCount .. ' tokens')
|
|
|
|
end
|
|
|
|
local i = 0
|
|
|
|
while i < tokenCount do
|
|
|
|
local offset = offsets[i + 1]
|
|
|
|
spawnTokenOn(object, offset, tokenType)
|
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function buildPlayerCardKey(object)
|
|
|
|
return object.getName() .. ':' .. object.getDescription()
|
|
|
|
end
|
|
|
|
|
|
|
|
function getPlayerCardData(object)
|
|
|
|
return PLAYER_CARDS[buildPlayerCardKey(object)] or PLAYER_CARDS[object.getName()]
|
|
|
|
end
|
|
|
|
|
|
|
|
function shouldSpawnTokens(object)
|
|
|
|
-- we assume we shouldn't spawn tokens if in doubt, this should
|
|
|
|
-- only ever happen on load and in that case prevents respawns
|
|
|
|
local spawned = DATA_HELPER.call('getSpawnedPlayerCardGuid', {object.getGUID()})
|
2022-10-19 19:07:47 -04:00
|
|
|
local hasDataHelperData = getPlayerCardData(object)
|
|
|
|
local cardMetadata = JSON.decode(object.getGMNotes()) or {}
|
|
|
|
local hasUses = cardMetadata.uses ~= nil
|
|
|
|
return not spawned and (hasDataHelperData or hasUses)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
function markSpawned(object)
|
|
|
|
local saved = DATA_HELPER.call('setSpawnedPlayerCardGuid', {object.getGUID(), true})
|
|
|
|
if not saved then
|
|
|
|
error('attempt to mark player card spawned before data loaded')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function spawnTokensFor(object)
|
2022-10-19 19:07:47 -04:00
|
|
|
local cardMetadata = JSON.decode(object.getGMNotes()) or {}
|
|
|
|
local token = nil
|
|
|
|
local type = nil
|
|
|
|
local tokenCount = 0
|
|
|
|
if (cardMetadata.uses ~= nil) then
|
|
|
|
for i, useInfo in ipairs(cardMetadata.uses) do
|
|
|
|
token = useInfo.token
|
|
|
|
type = useInfo.type
|
|
|
|
tokenCount = useInfo.count
|
|
|
|
if (activeInvestigatorId == "03004" and useInfo.type == "Charge") then
|
|
|
|
tokenCount = tokenCount + 1
|
|
|
|
end
|
|
|
|
log("Spawning tokens for "..object.getName()..'['..object.getDescription()..']: '..tokenCount.."x "..token)
|
|
|
|
spawnTokenGroup(object, token, tokenCount)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
local data = getPlayerCardData(object)
|
|
|
|
if data == nil then
|
|
|
|
error('attempt to spawn tokens for ' .. object.getName() .. ': no token data')
|
|
|
|
end
|
|
|
|
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)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
markSpawned(object)
|
|
|
|
end
|
|
|
|
|
|
|
|
function resetSpawnState()
|
|
|
|
local zone = getObjectFromGUID(zoneID)
|
|
|
|
if zone == nil then return end
|
|
|
|
|
|
|
|
for i,object in ipairs(zone.getObjects()) do
|
|
|
|
if object.tag == "Card" then
|
|
|
|
local guid = object.getGUID()
|
|
|
|
if guid ~= nil then unmarkSpawned(guid, true) end
|
|
|
|
elseif object.tag == "Deck" then
|
|
|
|
local cards = object.getObjects()
|
|
|
|
if (cards ~= nil) then
|
|
|
|
for i,v in ipairs(cards) do
|
|
|
|
if v.guid ~= nil then unmarkSpawned(v.guid) end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
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-10-19 19:07:47 -04:00
|
|
|
if (collision_info.collision_object.name == "Card") then
|
|
|
|
maybeUpdateActiveInvestigator(collision_info.collision_object)
|
|
|
|
end
|
2020-11-28 13:23:58 -05:00
|
|
|
if not COLLISION_ENABLED then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local object = collision_info.collision_object
|
|
|
|
Wait.time(resetSpawnState, 1)
|
|
|
|
-- anything to the left of this is legal to spawn
|
|
|
|
local discardSpawnBoundary = self.positionToWorld({-1.2, 0, 0})
|
|
|
|
local boundaryLocalToCard = object.positionToLocal(discardSpawnBoundary)
|
|
|
|
if boundaryLocalToCard.x > 0 then
|
|
|
|
log('not checking for token spawn, boundary relative is ' .. boundaryLocalToCard.x)
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if not object.is_face_down and shouldSpawnTokens(object) then
|
|
|
|
spawnTokensFor(object)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-10-19 19:07:47 -04:00
|
|
|
function maybeUpdateActiveInvestigator(card)
|
|
|
|
local cardMetadata = JSON.decode(card.getGMNotes()) or {}
|
|
|
|
if (cardMetadata.type == "Investigator") then
|
|
|
|
activeInvestigatorId = cardMetadata.id
|
|
|
|
updateStatToken(willpowerTokenGuid, cardMetadata.willpowerIcons)
|
|
|
|
updateStatToken(intellectTokenGuid, cardMetadata.intellectIcons)
|
|
|
|
updateStatToken(combatTokenGuid, cardMetadata.combatIcons)
|
|
|
|
updateStatToken(agilityTokenGuid, cardMetadata.agilityIcons)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function updateStatToken(tokenGuid, val)
|
|
|
|
local statToken = getObjectFromGUID(tokenGuid)
|
|
|
|
if (statToken == nil) then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
statToken.call("reset_val")
|
|
|
|
for i = 1, val do
|
|
|
|
statToken.call("add_subtract", { alt_click = false })
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-28 13:23:58 -05:00
|
|
|
-- functions delegated to Global
|
|
|
|
function drawChaostokenButton(object, player, isRightClick)
|
|
|
|
-- local toPosition = self.positionToWorld(DRAWN_CHAOS_TOKEN_OFFSET)
|
|
|
|
Global.call("drawChaostoken", {self, DRAWN_CHAOS_TOKEN_OFFSET, isRightClick})
|
|
|
|
end
|
|
|
|
|
|
|
|
function drawEncountercard(object, player, isRightClick)
|
2020-11-29 00:06:51 -05:00
|
|
|
Global.call("drawEncountercard", PLAYER_COLOR)
|
2020-11-28 13:23:58 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
function spawnToken(position, tokenType)
|
|
|
|
Global.call('spawnToken', {position, tokenType})
|
2021-01-02 22:49:38 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
function updatePlayerCards(args)
|
|
|
|
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
|
2021-04-10 00:44:08 -04:00
|
|
|
end
|