-- 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] DRAWN_ENCOUNTER_CARD_OFFSET = {0.98, 0.5, -0.635} DRAWN_CHAOS_TOKEN_OFFSET = {-1.2, 0.5, -0.45} DISCARD_BUTTON_OFFSETS = { {-0.98, 0.2, -0.935}, {-0.525, 0.2, -0.935}, {-0.07, 0.2, -0.935}, {0.39, 0.2, -0.935}, {0.84, 0.2, -0.935}, } -- the position of the global discard pile -- TODO: delegate to global for any auto discard actions DISCARD_POSITION = {-3.85, 3, 10.38} 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() 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 = { {1, 0, -0.7}, {0.55, 0, -0.7}, {0.1, 0, -0.7}, {-0.35, 0, -0.7}, {-0.8, 0, -0.7} } local i = 1 while i <= 5 do makeDiscardButton(DISCARD_BUTTON_OFFSETS[i], encounterSlots[i], DISCARD_POSITION, i) i = i + 1 end self.createButton({ label = " ", click_function = "drawEncountercard", function_owner = self, position = {-1.45,0,-0.7}, rotation = {0,-15,0}, width = 170, height = 255, font_size = 50 }) self.createButton({ label=" ", click_function = "drawChaostokenButton", function_owner = self, position = {1.48,0.0,-0.74}, rotation = {0,-45,0}, width = 125, height = 125, font_size = 50 }) COLLISION_ENABLED = true 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) 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()}) local canSpawn = getPlayerCardData(object) return not spawned and canSpawn 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) local data = getPlayerCardData(object) if data == nil then error('attempt to spawn tokens for ' .. object.getName() .. ': no token data') end log(object.getName() .. '[' .. object.getDescription() .. ']' .. ' : ' .. data['tokenType'] .. ' : ' .. data['tokenCount']) spawnTokenGroup(object, data['tokenType'], data['tokenCount']) markSpawned(object) end function onCollisionEnter(collision_info) if not COLLISION_ENABLED then return end local object = collision_info.collision_object -- 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 -- 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) local toPosition = self.positionToWorld(DRAWN_ENCOUNTER_CARD_OFFSET) Global.call("drawEncountercard", {toPosition, self.getRotation(), isRightClick}) end function spawnToken(position, tokenType) Global.call('spawnToken', {position, tokenType}) end