diff --git a/src/core/GameKeyHandler.ttslua b/src/core/GameKeyHandler.ttslua index 761d86ed..8b57ce0b 100644 --- a/src/core/GameKeyHandler.ttslua +++ b/src/core/GameKeyHandler.ttslua @@ -76,19 +76,6 @@ function takeCardIntoThreatArea(playerColor, hoveredObject) return end - -- initialize list of objects to move (and store local position + rotation) - local additionalObjects = {} - for _, obj in ipairs(searchLib.onObject(hoveredObject, "isTileOrToken")) do - if not obj.locked then - local data = { - object = obj, - localPos = hoveredObject.positionToLocal(obj.getPosition()), - localRot = obj.getRotation() - cardRot - } - table.insert(additionalObjects, data) - end - end - -- get the rotation of the owning playermat (or Mythos) local cardOwner = guidReferenceApi.getOwnerOfObject(hoveredObject) local ownerRotation = Vector(0, 270, 0) @@ -101,17 +88,7 @@ function takeCardIntoThreatArea(playerColor, hoveredObject) local newCardRot = cardRot:setAt("y", matRotation.y + cardRot.y - ownerRotation.y) -- move the main card to threat area - hoveredObject.setRotation(newCardRot) - hoveredObject.setPosition(threatAreaPos) - - -- move tokens/tiles (to new global position) - for _, data in ipairs(additionalObjects) do - data.object.setPosition(hoveredObject.positionToWorld(data.localPos)) - - local newRot = data.localRot + Vector(0, newCardRot.y, 0) - newRot.y = roundToMultiple(newRot.y, 45) - data.object.setRotation(newRot) - end + GlobalApi.moveCardWithTokens(hoveredObject, threatAreaPos, newCardRot) -- contruct feedback message (hide name when face-down + enabled setting) local cardName = hoveredObject.getName() diff --git a/src/core/Global.ttslua b/src/core/Global.ttslua index a628b72f..81ccf704 100644 --- a/src/core/Global.ttslua +++ b/src/core/Global.ttslua @@ -73,8 +73,8 @@ local RESOURCE_OPTIONS = { local handVisibility = {} local blurseVisibility = {} --- track cards' settings -local cardSetting = {} +-- track cards' tokens +local cardTokens = {} --------------------------------------------------------- -- data for tokens @@ -180,6 +180,12 @@ function getRandomSeed() return math.random(999) end +function onObjectDrop(_, object) + if object.type == "Card" or object.type == "Deck" then + moveCardWithTokens({ card = object }) + end +end + -- Event hook for any object search. When chaos tokens are manipulated while the chaos bag -- container is being searched, a TTS bug can cause tokens to duplicate or vanish. We lock the -- chaos bag during search operations to avoid this. @@ -208,8 +214,6 @@ function tryObjectEnterContainer(container, object) -- stop mini cards from forming decks if object.hasTag("Minicard") and container.hasTag("Minicard") then return false - elseif object.type == "Card" and object.getName() ~= "Atlach-Nacha" then - handleTokenDetaching({ card = object }) end playAreaApi.tryObjectEnterContainer(container, object) @@ -303,19 +307,12 @@ function onPlayerAction(player, action, targets) end return false elseif action == Player.Action.PickUp then - local pickedCards = {} for _, target in ipairs(targets) do - if target.type == "Card" then - table.insert(pickedCards, target) + if target.type == "Card" or target.type == "Deck" then + storeTokenTransform(target) end end - if #pickedCards > 5 then return end - - for _, pickedCard in ipairs(pickedCards) do - handleTokenAttaching({ player = player, card = pickedCard }) - end end - return true end --------------------------------------------------------- @@ -2849,30 +2846,33 @@ function tableContains(thisTable, thisElement) return false end -function handleTokenAttaching(params) +function moveCardWithTokens(params) local card = params.card - local player = params.player - local searchResult = searchLib.onObject(card, "isTileOrToken", 0.95) - if card.is_face_down and next(searchResult) ~= nil then - cardSetting[card] = { - hideFacedown = card.hide_when_face_down, - tooltip = card.tooltip - } - card.hide_when_face_down = false - card.tooltip = false + local position = params.position + local rotation = params.rotation + + if not cardTokens[card] then + storeTokenTransform(card) end - for _, token in ipairs(searchResult) do - if not token.locked then - card.addAttachment(token) - end + if rotation then + card.setRotation(rotation) end + if position then + card.use_hands = false + card.setPositionSmooth(position) + end + + if #cardTokens[card] == 0 then return end + + -- wait for the card to finish moving, update token position/rotation regularly Wait.condition( - function() handleTokenDetaching({ card = card }) end, + function() stopTokenTransformUpdating(card) end, function() - if card ~= nil and player ~= nil and player.seated then - return card.resting and not tableContains(player.getHoldingObjects(), card) + if card ~= nil and cardTokens[card] ~= nil and #cardTokens[card] ~= 0 then + updateTokenTransform(card) + return card.resting and not card.isSmoothMoving() else return true end @@ -2880,35 +2880,49 @@ function handleTokenAttaching(params) ) end -function handleTokenDetaching(params) - local card = params.card - if card == nil or next(card.getAttachments()) == nil then return end - - -- restore card settings - if cardSetting[card] ~= nil then - card.hide_when_face_down = cardSetting[card]["hideFacedown"] - card.tooltip = cardSetting[card]["tooltip"] - cardSetting[card] = nil +function storeTokenTransform(card) + if cardTokens[card] ~= nil then + stopTokenTransformUpdating(card) end - -- remove attachments - local removedTokens = card.removeAttachments() - for _, token in ipairs(removedTokens) do - if token.getPosition().y < card.getPosition().y then - local posY = card.getPosition().y + 0.05 - token.setPosition(token.getPosition():setAt("y", posY)) + cardTokens[card] = {} + local cardRot = card.getRotation() + for _, token in ipairs(searchLib.onObject(card, "isTileOrToken", 0.95)) do + if not token.locked then + -- offset to stop the token from colliding with the card + local tokenPos = token.getPosition() + Vector(0, 0.05, 0) + token.setPosition(tokenPos) + + -- store local transform data + table.insert(cardTokens[card], { + token = token, + localPos = card.positionToLocal(tokenPos), + localRot = token.getRotation() - cardRot + }) end end +end - -- redraw token xml - if card.hasTag("CardThatSeals") then - local func = card.getVar("updateStackSize") -- make sure function exists - if func ~= nil then - card.call("updateStackSize") +function updateTokenTransform(card) + for _, tokenData in ipairs(cardTokens[card] or {}) do + if tokenData.token ~= nil then + tokenData.token.setPosition(card.positionToWorld(tokenData.localPos)) + tokenData.token.setRotation(card.getRotation() + tokenData.localRot) + tokenData.token.locked = true end end end +function stopTokenTransformUpdating(card) + for _, tokenData in ipairs(cardTokens[card] or {}) do + if tokenData.token ~= nil then + tokenData.token.locked = false + end + end + cardTokens[card] = nil + card.use_hands = true +end + -- removes tokens from the provided card/deck function removeTokensFromObject(params) local object = params.object @@ -2921,14 +2935,30 @@ function removeTokensFromObject(params) end end - for _, obj in ipairs(searchLib.onObject(object, "isTileOrToken")) do - if tokenChecker.isChaosToken(obj) then - returnChaosTokenToBag( { token = obj, fromBag = false } ) - elseif obj.getGUID() ~= "4ee1f2" and -- table - obj ~= self and - obj.memo ~= nil and - obj.getLock() == false then - trash.putObject(obj) + if cardTokens[object] then + -- check if this card was moved with tokens on it + for _, tokenData in ipairs(cardTokens[object]) do + if tokenData.token ~= nil then + tokenData.token.locked = false + if tokenChecker.isChaosToken(tokenData.token) then + returnChaosTokenToBag({ token = tokenData.token, fromBag = false }) + else + trash.putObject(tokenData.token) + end + end + end + cardTokens[object] = nil + else + -- search area for tokens + for _, obj in ipairs(searchLib.onObject(object, "isTileOrToken")) do + if tokenChecker.isChaosToken(obj) then + returnChaosTokenToBag({ token = obj, fromBag = false }) + elseif obj.getGUID() ~= "4ee1f2" and -- table + obj ~= self and + obj.memo ~= nil and + obj.getLock() == false then + trash.putObject(obj) + end end end end diff --git a/src/core/GlobalApi.ttslua b/src/core/GlobalApi.ttslua index 8a26fd62..e39a64d9 100644 --- a/src/core/GlobalApi.ttslua +++ b/src/core/GlobalApi.ttslua @@ -53,12 +53,6 @@ do Global.call("handVisibilityToggle", { playerColor = playerColor, handColor = handColor }) end - -- handles token detaching for cards - ---@param card tts__Object Card that should get tokens detached - function GlobalApi.handleTokenDetaching(card) - Global.call("handleTokenDetaching", { card = card }) - end - -- discards tokens from an object ---@param object tts__Object Object that should get tokens removed ---@param owner string Owner of this object (for discarding) @@ -66,6 +60,14 @@ do Global.call("removeTokensFromObject", { object = object, owner = owner }) end + -- moves a card with tokens on it + ---@param card tts__Object Card that should get moved + ---@param position? tts__Vector Target position + ---@param rotation? tts__Vector Target rotation + function GlobalApi.moveCardWithTokens(card, position, rotation) + Global.call("moveCardWithTokens", { card = card, position = position, rotation = rotation }) + end + -- loads saved options ---@param options table Set a new state for the option table function GlobalApi.loadOptionPanelSettings(options) diff --git a/src/core/MythosArea.ttslua b/src/core/MythosArea.ttslua index 3c0a1c0a..2b04d705 100644 --- a/src/core/MythosArea.ttslua +++ b/src/core/MythosArea.ttslua @@ -105,7 +105,6 @@ function onCollisionEnter(collisionInfo) if inArea(localPos, ENCOUNTER_DECK_AREA) or inArea(localPos, ENCOUNTER_DISCARD_AREA) then -- reset spawned tokens and remove tokens from cards in encounter deck / discard area Wait.frames(function() tokenSpawnTrackerApi.resetTokensSpawned(object) end, 1) - GlobalApi.handleTokenDetaching(object) GlobalApi.removeTokensFromObject(object, "Mythos") elseif inArea(localPos, SCENARIO_REFERENCE_AREA) then diff --git a/src/playermat/Playermat.ttslua b/src/playermat/Playermat.ttslua index 0247a7db..84c7c3dd 100644 --- a/src/playermat/Playermat.ttslua +++ b/src/playermat/Playermat.ttslua @@ -359,23 +359,7 @@ function discardOrMove(_, playerColor, isRightClick) for _, obj in ipairs(searchResult) do if obj.type == "Card" or obj.type == "Deck" then - -- initialize list of objects to move (and store local position + rotation) - local additionalObjects = {} - for _, tokenOnCard in ipairs(searchLib.onObject(obj, "isTileOrToken")) do - if not tokenOnCard.locked then - local data = { - object = tokenOnCard, - localPos = obj.positionToLocal(tokenOnCard.getPosition()), - } - table.insert(additionalObjects, data) - end - end - - obj.setPosition(threatAreaPos) - -- move tokens/tiles (to new global position) - for _, data in ipairs(additionalObjects) do - data.object.setPosition(obj.positionToWorld(data.localPos)) - end + GlobalApi.moveCardWithTokens(obj, threatAreaPos) end end else @@ -1297,7 +1281,6 @@ function onCollisionEnter(collisionInfo) spawnTokensOrShowHelper(object) end elseif inArea(localCardPos, DECK_DISCARD_AREA) then - GlobalApi.handleTokenDetaching(object) tokenSpawnTrackerApi.resetTokensSpawned(object) GlobalApi.removeTokensFromObject(object, matColor) elseif object.is_face_down == false then