Merge branch 'main' into playermat-xml
This commit is contained in:
commit
757e818450
@ -27,6 +27,8 @@
|
||||
"HandTrigger.be2f17",
|
||||
"HandTrigger.0285cc",
|
||||
"HandTrigger.a70eee",
|
||||
"ScriptingTrigger.a2f932",
|
||||
"FogOfWarTrigger.3aab97",
|
||||
"TableLegBottomRight.afc863",
|
||||
"TableLegBottomLeft.c8edca",
|
||||
"TableLegTopLeft.393bf7",
|
||||
@ -97,7 +99,6 @@
|
||||
"Damagetokens.480bda",
|
||||
"Resourcetokens.9fadf9",
|
||||
"Connectionmarkers.170f10",
|
||||
"FogOfWarTrigger.3aab97",
|
||||
"ChaosTokenReserve.106418",
|
||||
"ClueCounter.37be78",
|
||||
"ClueCounter.1769ed",
|
||||
@ -127,7 +128,6 @@
|
||||
"ChaosBag.fea079",
|
||||
"DataHelper.708279",
|
||||
"BlessCurseManager.5933fb",
|
||||
"ScriptingTrigger.a2f932",
|
||||
"EdgeoftheEarth.895eaa",
|
||||
"TheDream-Eaters.a16a1a",
|
||||
"TheFeastofHemlockVale.c740af",
|
||||
|
@ -30,12 +30,12 @@
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2026086584372569912/5CB461AEAE2E59D3064D90A776EB86C46081EC78/"
|
||||
},
|
||||
{
|
||||
"Name": "option-on",
|
||||
"Name": "option_on",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2462982115668997008/2178787B67B3C96F3419EDBAB8420E39893756BC/"
|
||||
},
|
||||
{
|
||||
"Name": "option-off",
|
||||
"Name": "option_off",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2462982115668996901/D6438ECBB11DECC6DB9987589FF526FBAD4D2368/"
|
||||
},
|
||||
@ -69,16 +69,6 @@
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2280574378889753733/F67B7B37FF7AA253B6D697E577DF54A3E76030C2/"
|
||||
},
|
||||
{
|
||||
"Name": "option_on",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2024962321889555728/22ABD35CBB49A001F3A5318E4AFCFB22D24FEA39/"
|
||||
},
|
||||
{
|
||||
"Name": "option_off",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2024962321889555661/6643E5CC9160FF4624672C255D0DF7B313DA00A5/"
|
||||
},
|
||||
{
|
||||
"Name": "SpeechBubble",
|
||||
"Type": 0,
|
||||
@ -244,6 +234,11 @@
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2510267932886739653/CB7AA2D73777EF5938A6E6CD664B2ABA52B6E20A/"
|
||||
},
|
||||
{
|
||||
"Name": "token-eldersign",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2540675016035917168/C0D6F531A85FD94C2F54825DFC50781B5B499A1D/"
|
||||
},
|
||||
{
|
||||
"Name": "token-custom-token",
|
||||
"Type": 0,
|
||||
|
@ -33,7 +33,7 @@
|
||||
"IgnoreFoW": false,
|
||||
"LayoutGroupSortIndex": 0,
|
||||
"Locked": false,
|
||||
"LuaScript": "",
|
||||
"LuaScript": "require(\"playercards/cards/KohakuNarukami\")",
|
||||
"LuaScriptState": "",
|
||||
"MeasureMovement": false,
|
||||
"Name": "Card",
|
||||
@ -58,5 +58,5 @@
|
||||
"scaleZ": 1.15
|
||||
},
|
||||
"Value": 0,
|
||||
"XmlUI": ""
|
||||
"XmlUI": "\u003cInclude src=\"playercards/KohakuNarukami.xml\"/\u003e"
|
||||
}
|
||||
|
@ -31,12 +31,12 @@
|
||||
{
|
||||
"Name": "option_on",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2024962321889555728/22ABD35CBB49A001F3A5318E4AFCFB22D24FEA39/"
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2462982115668997008/2178787B67B3C96F3419EDBAB8420E39893756BC/"
|
||||
},
|
||||
{
|
||||
"Name": "option_off",
|
||||
"Type": 0,
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2024962321889555661/6643E5CC9160FF4624672C255D0DF7B313DA00A5/"
|
||||
"URL": "http://cloud-3.steamusercontent.com/ugc/2462982115668996901/D6438ECBB11DECC6DB9987589FF526FBAD4D2368/"
|
||||
}
|
||||
],
|
||||
"Description": "Cleans up the table for the next scenario during campaign play.\n\nThis includes moving cards and tokens into the trashcans, resetting counters and removing bless/curse tokens from the chaos bag.",
|
||||
|
@ -369,8 +369,8 @@ function tidyPlayerMatCoroutine()
|
||||
if tekeliliHelper then
|
||||
tekeliliHelper.call("spawnStoredTekelili", color)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
-- mythos area cleanup
|
||||
|
@ -42,10 +42,11 @@ function onLoad(savedData)
|
||||
color = { r = 0, g = 0, b = 0, a = 0 }
|
||||
})
|
||||
|
||||
self.addContextMenuItem("toggle broadcasting", updateBroadcast)
|
||||
self.addContextMenuItem("Toggle Broadcasting", updateBroadcast)
|
||||
end
|
||||
|
||||
function updateBroadcast()
|
||||
function updateBroadcast(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
for _, tracker in ipairs(getObjectsWithTag("LinkedPhaseTracker")) do
|
||||
tracker.setVar("broadcastChange", not broadcastChange)
|
||||
end
|
||||
|
@ -124,22 +124,17 @@ function startSearch(messageColor, number)
|
||||
end
|
||||
|
||||
-- get position and rotation for set aside cards
|
||||
local handData = Player[handColor].getHandTransform()
|
||||
local handCards = Player[handColor].getHandObjects()
|
||||
setAsidePosition = handData.position + offset * handData.right
|
||||
setAsideRotation = { handData.rotation.x, handData.rotation.y + 180, 180 }
|
||||
local handData = Player[handColor].getHandTransform()
|
||||
local handCards = Player[handColor].getHandObjects()
|
||||
setAsidePosition = (handData.position + offset * handData.right):setAt("y", 1.5)
|
||||
setAsideRotation = { handData.rotation.x, handData.rotation.y + 180, 180 }
|
||||
|
||||
-- set y-value
|
||||
setAsidePosition.y = 1.5
|
||||
|
||||
for i = #handCards, 1, -1 do
|
||||
handCards[i].setPosition(setAsidePosition + Vector(0, (#handCards - i) * 0.1, 0))
|
||||
handCards[i].setRotation(setAsideRotation)
|
||||
end
|
||||
-- place hand cards set aside
|
||||
deckLib.placeOrMergeIntoDeck(handCards, setAsidePosition, setAsideRotation)
|
||||
|
||||
-- handling for Norman Withers
|
||||
if deckAreaObjects.topCard then
|
||||
deckAreaObjects.topCard.flip()
|
||||
deckAreaObjects.topCard.setRotation(setAsideRotation)
|
||||
topCardDetected = true
|
||||
end
|
||||
|
||||
@ -155,35 +150,36 @@ end
|
||||
function endSearch(_, _, isRightClick)
|
||||
local handCards = Player[handColor].getHandObjects()
|
||||
|
||||
local j = 0
|
||||
for i = #handCards, 1, -1 do
|
||||
j = j + 1
|
||||
Wait.time(function() deckLib.placeOrMergeIntoDeck(handCards[i], drawDeckPosition, setAsideRotation) end, j * 0.1)
|
||||
end
|
||||
-- place cards on deck
|
||||
deckLib.placeOrMergeIntoDeck(handCards, drawDeckPosition, setAsideRotation)
|
||||
|
||||
-- draw set aside cards (from the ground!)
|
||||
Wait.time(drawSetAsideCards, 0.5 + #handCards * 0.1)
|
||||
|
||||
normalView()
|
||||
|
||||
Wait.time(function()
|
||||
-- maybe shuffle deck
|
||||
if not isRightClick then
|
||||
local deckAreaObjects = playermatApi.getDeckAreaObjects(matColor)
|
||||
if deckAreaObjects.draw then
|
||||
deckAreaObjects.draw.shuffle()
|
||||
end
|
||||
end
|
||||
|
||||
-- Norman Withers handling
|
||||
if topCardDetected then
|
||||
playermatApi.flipTopCardFromDeck(matColor)
|
||||
end
|
||||
end, 1 + #handCards * 0.1)
|
||||
end
|
||||
|
||||
function drawSetAsideCards()
|
||||
for _, obj in ipairs(searchLib.atPosition(setAsidePosition, "isCardOrDeck")) do
|
||||
local count = 1
|
||||
if obj.type == "Deck" then
|
||||
count = #obj.getObjects()
|
||||
end
|
||||
Wait.time(function() obj.deal(count, handColor) end, 1)
|
||||
end
|
||||
|
||||
normalView()
|
||||
|
||||
-- delay is to wait for cards to enter deck
|
||||
if not isRightClick then
|
||||
Wait.time(function()
|
||||
local deckAreaObjects = playermatApi.getDeckAreaObjects(matColor)
|
||||
if deckAreaObjects.draw then
|
||||
deckAreaObjects.draw.shuffle()
|
||||
end
|
||||
end, #handCards * 0.3 + 0.5)
|
||||
end
|
||||
|
||||
-- Norman Withers handling
|
||||
if topCardDetected then
|
||||
Wait.time(function() playermatApi.flipTopCardFromDeck(matColor) end, #handCards * 0.3 + 0.75)
|
||||
obj.deal(count, handColor)
|
||||
end
|
||||
end
|
||||
|
@ -32,7 +32,7 @@ function onload(savedData)
|
||||
isSetup = false
|
||||
movingCards = false
|
||||
|
||||
self.addContextMenuItem('Reset helper', resetHelper)
|
||||
self.addContextMenuItem('Reset Helper', resetHelper)
|
||||
|
||||
if savedData and savedData ~= '' then
|
||||
local loaded_data = JSON.decode(savedData)
|
||||
@ -242,7 +242,8 @@ function finish()
|
||||
0.75)
|
||||
end
|
||||
|
||||
function resetHelper()
|
||||
function resetHelper(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
for i, card in ipairs(self.getObjects()) do
|
||||
self.takeObject({
|
||||
index = 0,
|
||||
@ -269,7 +270,5 @@ function resetHelper()
|
||||
isSetup = false
|
||||
movingCards = false
|
||||
|
||||
self.reset()
|
||||
|
||||
print('Underworld Market Helper: Helper has been reset.')
|
||||
end
|
||||
|
@ -266,6 +266,19 @@ function updateDisplayAndBroadcast(type)
|
||||
end
|
||||
end
|
||||
|
||||
function getBlessCurseInBag()
|
||||
local numInBag = { Bless = 0, Curse = 0 }
|
||||
local chaosBag = chaosBagApi.findChaosBag()
|
||||
|
||||
for _, v in ipairs(chaosBag.getObjects()) do
|
||||
if v.name == "Bless" or v.name == "Curse" then
|
||||
numInBag[v.name] = numInBag[v.name] + 1
|
||||
end
|
||||
end
|
||||
|
||||
return numInBag
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- main functions: add and remove
|
||||
---------------------------------------------------------
|
||||
|
@ -67,5 +67,9 @@ do
|
||||
getManager().call("removeToken", type)
|
||||
end
|
||||
|
||||
BlessCurseManagerApi.getBlessCurseInBag = function()
|
||||
return getManager().call("getBlessCurseInBag", {})
|
||||
end
|
||||
|
||||
return BlessCurseManagerApi
|
||||
end
|
||||
|
@ -1,5 +1,6 @@
|
||||
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
|
||||
local guidReferenceApi = require("core/GUIDReferenceApi")
|
||||
local mythosAreaApi = require("core/MythosAreaApi")
|
||||
local navigationOverlayApi = require("core/NavigationOverlayApi")
|
||||
local optionPanelApi = require("core/OptionPanelApi")
|
||||
local playermatApi = require("playermat/PlayermatApi")
|
||||
@ -15,6 +16,7 @@ function onLoad()
|
||||
addHotkey("Move card to Victory Display", moveCardToVictoryDisplay)
|
||||
addHotkey("Place card into threat area", takeCardIntoThreatArea)
|
||||
addHotkey("Remove a use", removeOneUse)
|
||||
addHotkey("Reshuffle encounter deck", mythosAreaApi.reshuffleEncounterDeck)
|
||||
addHotkey("Switch seat clockwise", switchSeatClockwise)
|
||||
addHotkey("Switch seat counter-clockwise", switchSeatCounterClockwise)
|
||||
addHotkey("Take clue from location", takeClueFromLocation)
|
||||
@ -122,10 +124,15 @@ function takeCardIntoThreatArea(playerColor, hoveredObject)
|
||||
end
|
||||
end
|
||||
|
||||
-- discard the hovered object to the respective trashcan and discard tokens on it if it was a card
|
||||
-- discard the hovered or selected objects to the respective trashcan and discard tokens on it if it was a card
|
||||
function discardObject(playerColor, hoveredObject)
|
||||
-- if more than one object is selected, discard them all, one at a time
|
||||
local selectedObjects = Player[playerColor].getSelectedObjects()
|
||||
if #selectedObjects > 0 then
|
||||
discardGroup(playerColor, selectedObjects)
|
||||
return
|
||||
-- only continue if an unlocked card, deck or tile was hovered
|
||||
if hoveredObject == nil
|
||||
elseif hoveredObject == nil
|
||||
or (hoveredObject.type ~= "Card" and hoveredObject.type ~= "Deck" and hoveredObject.type ~= "Tile")
|
||||
or hoveredObject.locked then
|
||||
broadcastToColor("Hover a token/tile or a card/deck and try again.", playerColor, "Yellow")
|
||||
@ -133,11 +140,15 @@ function discardObject(playerColor, hoveredObject)
|
||||
end
|
||||
|
||||
-- These should probably not be discarded normally. Ask player for confirmation.
|
||||
if hoveredObject.type == "Deck" or hoveredObject.hasTag("Location") then
|
||||
local suspect = (hoveredObject.type == "Deck") and "Deck" or "Location"
|
||||
Player[playerColor].showConfirmDialog("Discard " .. suspect .. "?",
|
||||
function() performDiscard(playerColor, hoveredObject) end)
|
||||
return
|
||||
local tokenData = mythosAreaApi.returnTokenData()
|
||||
local scenarioName = tokenData.currentScenario
|
||||
if scenarioName ~= "Lost in Time and Space" and scenarioName ~= "The Secret Name" then
|
||||
if hoveredObject.type == "Deck" or hoveredObject.hasTag("Location") then
|
||||
local suspect = (hoveredObject.type == "Deck") and "Deck" or "Location"
|
||||
Player[playerColor].showConfirmDialog("Discard " .. suspect .. "?",
|
||||
function() performDiscard(playerColor, hoveredObject) end)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
performDiscard(playerColor, hoveredObject)
|
||||
@ -159,6 +170,18 @@ function performDiscard(playerColor, hoveredObject)
|
||||
playermatApi.discardListOfObjects(discardForMatColor, discardTheseObjects)
|
||||
end
|
||||
|
||||
function discardGroup(playerColor, selectedObjects)
|
||||
local count = #selectedObjects
|
||||
-- discarding one at a time avoids an error with cards in the discard pile losing the 'hands' toggle and uses multiple mats
|
||||
for i = count, 1, -1 do
|
||||
Wait.time(function()
|
||||
if (selectedObjects[i].type == "Card" or selectedObjects[i].type ~= "Deck" or selectedObjects[i].type == "Tile") then
|
||||
performDiscard(playerColor, selectedObjects[i])
|
||||
end
|
||||
end, (count - i + 1) * 0.1)
|
||||
end
|
||||
end
|
||||
|
||||
-- discard the top card of hovered deck, calling discardObject function
|
||||
function discardTopDeck(playerColor, hoveredObject)
|
||||
-- only continue if an unlocked card or deck was hovered
|
||||
@ -282,8 +305,7 @@ function removeOneUse(playerColor, hoveredObject)
|
||||
end
|
||||
end
|
||||
|
||||
local playerName = Player[playerColor].steam_name
|
||||
broadcastToAll(playerName .. " removed a token: " .. tokenName, playerColor)
|
||||
broadcastToAll(getColoredName(playerColor) .. " removed a token: " .. tokenName, playerColor)
|
||||
|
||||
local discardForMatColor = getColorToDiscardFor(hoveredObject, playerColor)
|
||||
playermatApi.discardListOfObjects(discardForMatColor, { targetObject })
|
||||
@ -404,12 +426,10 @@ function takeClueFromLocation(playerColor, hoveredObject)
|
||||
local clickableClues = optionPanelApi.getOptions()["useClueClickers"]
|
||||
|
||||
-- handling for calling this for a specific mat via hotkey
|
||||
local playerName, matColor, pos
|
||||
local matColor, pos
|
||||
if Player[playerColor] and Player[playerColor].seated then
|
||||
playerName = Player[playerColor].steam_name
|
||||
matColor = playermatApi.getMatColor(playerColor)
|
||||
else
|
||||
playerName = playerColor
|
||||
matColor = playerColor
|
||||
end
|
||||
|
||||
@ -431,9 +451,9 @@ function takeClueFromLocation(playerColor, hoveredObject)
|
||||
end
|
||||
|
||||
if cardName then
|
||||
broadcastToAll(playerName .. " took one clue from " .. cardName .. ".", "White")
|
||||
broadcastToAll(getColoredName(playerColor) .. " took one clue from " .. cardName .. ".", "White")
|
||||
else
|
||||
broadcastToAll(playerName .. " took one clue.", "White")
|
||||
broadcastToAll(getColoredName(playerColor) .. " took one clue.", "White")
|
||||
end
|
||||
|
||||
victoryDisplayApi.update()
|
||||
@ -472,3 +492,14 @@ function getFirstSeatedPlayer()
|
||||
return color
|
||||
end
|
||||
end
|
||||
|
||||
-- returns the colored steam name or color
|
||||
function getColoredName(playerColor)
|
||||
local displayName = playerColor
|
||||
if Player[playerColor].steam_name then
|
||||
displayName = Player[playerColor].steam_name
|
||||
end
|
||||
|
||||
-- add bb-code
|
||||
return "[" .. Color.fromString(playerColor):toHex() .. "]" .. displayName .. "[-]"
|
||||
end
|
||||
|
@ -199,14 +199,20 @@ function onObjectEnterZone(zone, object)
|
||||
local matcolor = playermatApi.getMatColorByPosition(object.getPosition())
|
||||
local trash = guidReferenceApi.getObjectByOwnerAndType(matcolor, "Trash")
|
||||
trash.putObject(object)
|
||||
elseif zone.type == "Hand" and object.hasTag("CardWithHelper") then
|
||||
object.clearContextMenu()
|
||||
object.call("shutOff")
|
||||
elseif zone.type == "Hand" and object.type == "Card" then
|
||||
if object.is_face_down then
|
||||
object.flip()
|
||||
end
|
||||
if object.hasTag("CardWithHelper") then
|
||||
object.clearContextMenu()
|
||||
object.call("shutOff")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- TTS event for objects that leave zones
|
||||
function onObjectLeaveZone(zone, object)
|
||||
if zone.isDestroyed() or object.isDestroyed() then return end
|
||||
if zone.type == "Hand" and object.hasTag("CardWithHelper") then
|
||||
object.call("updateDisplay")
|
||||
end
|
||||
@ -238,6 +244,23 @@ function onObjectNumberTyped(hoveredObject, playerColor, number)
|
||||
end
|
||||
end
|
||||
|
||||
-- TTS event, used to redraw the playermat slot symbols after a small delay to account for the custom font loading
|
||||
function onPlayerConnect()
|
||||
Wait.time(function() playermatApi.redrawSlotSymbols("All") end, 0.2)
|
||||
end
|
||||
|
||||
function onPlayerAction(player, action, targets)
|
||||
if action == Player.Action.Delete and player.admin == false then
|
||||
for _, target in ipairs(targets) do
|
||||
local matColor = playermatApi.getMatColorByPosition(target.getPosition())
|
||||
local trash = guidReferenceApi.getObjectByOwnerAndType(matColor, "Trash")
|
||||
trash.putObject(target)
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---------------------------------------------------------
|
||||
-- chaos token drawing
|
||||
---------------------------------------------------------
|
||||
@ -1443,7 +1466,7 @@ function onClick_toggleOption(_, _, id)
|
||||
local currentState = optionPanel[id]
|
||||
local newState = not currentState
|
||||
applyOptionPanelChange(id, newState)
|
||||
UI.setAttribute(id, "image", newState and "option-on" or "option-off")
|
||||
UI.setAttribute(id, "image", newState and "option_on" or "option_off")
|
||||
end
|
||||
|
||||
-- color selection for playArea
|
||||
@ -1537,9 +1560,9 @@ function updateOptionPanelState()
|
||||
elseif (type(optionValue) == "boolean" and optionValue)
|
||||
or (type(optionValue) == "string" and optionValue)
|
||||
or (type(optionValue) == "table" and #optionValue ~= 0) then
|
||||
UI.setAttribute(id, "image", "option-on")
|
||||
UI.setAttribute(id, "image", "option_on")
|
||||
else
|
||||
UI.setAttribute(id, "image", "option-off")
|
||||
UI.setAttribute(id, "image", "option_off")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -78,7 +78,7 @@ function onCollisionEnter(collisionInfo)
|
||||
|
||||
local localPos = self.positionToLocal(object.getPosition())
|
||||
if inArea(localPos, ENCOUNTER_DECK_AREA) or inArea(localPos, ENCOUNTER_DISCARD_AREA) then
|
||||
Wait.frames(function() tokenSpawnTrackerApi.resetTokensSpawned(object.getGUID()) end, 1)
|
||||
Wait.frames(function() tokenSpawnTrackerApi.resetTokensSpawned(object) end, 1)
|
||||
removeTokensFromObject(object)
|
||||
end
|
||||
end
|
||||
@ -100,7 +100,7 @@ end
|
||||
function onObjectEnterContainer(container, object)
|
||||
local localPos = self.positionToLocal(container.getPosition())
|
||||
if inArea(localPos, ENCOUNTER_DECK_AREA) or inArea(localPos, ENCOUNTER_DISCARD_AREA) then
|
||||
tokenSpawnTrackerApi.resetTokensSpawned(object.getGUID())
|
||||
tokenSpawnTrackerApi.resetTokensSpawned(object)
|
||||
removeTokensFromObject(object)
|
||||
end
|
||||
end
|
||||
|
@ -202,6 +202,7 @@ end
|
||||
|
||||
function addContextMenu()
|
||||
self.addContextMenuItem("Change color", function(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local currentClassDisplayName = listOfClassDisplayNames[getIndexOfValue(listOfClasses, class)]
|
||||
Player[playerColor].showOptionsDialog("Choose color", listOfClassDisplayNames, currentClassDisplayName,
|
||||
function(_, selectedIndex)
|
||||
@ -210,6 +211,7 @@ function addContextMenu()
|
||||
end)
|
||||
|
||||
self.addContextMenuItem("Change 1st symbol", function(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local symbolList = getSymbolList()
|
||||
Player[playerColor].showOptionsDialog("Choose symbol", listOfSymbols, symbolList[1], function(newSymbol)
|
||||
if newSymbol == "None" then
|
||||
@ -229,6 +231,7 @@ function addContextMenu()
|
||||
end)
|
||||
|
||||
self.addContextMenuItem("Change 2nd symbol", function(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local symbolList = getSymbolList()
|
||||
Player[playerColor].showOptionsDialog("Choose 2nd symbol", listOfSymbols, symbolList[2] or symbolList[1],
|
||||
function(newSymbol)
|
||||
|
@ -1,7 +1,8 @@
|
||||
local searchLib = require("util/SearchLib")
|
||||
local chaosBagApi = require("chaosbag/ChaosBagApi")
|
||||
local deckLib = require("util/DeckLib")
|
||||
local guidReferenceApi = require("core/GUIDReferenceApi")
|
||||
local playAreaApi = require("core/PlayAreaApi")
|
||||
local searchLib = require("util/SearchLib")
|
||||
local tokenChecker = require("core/token/TokenChecker")
|
||||
|
||||
local pendingCall = false
|
||||
@ -234,28 +235,30 @@ end
|
||||
|
||||
-- places the provided card in the first empty spot
|
||||
function placeCard(card)
|
||||
local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash")
|
||||
local name = card.getName() or "Unnamed card"
|
||||
|
||||
-- check snap point states
|
||||
-- get sorted list of snap points to check slots
|
||||
local snaps = self.getSnapPoints()
|
||||
table.sort(snaps, function(a, b) return a.position.x > b.position.x end)
|
||||
table.sort(snaps, function(a, b) return a.position.z < b.position.z end)
|
||||
|
||||
-- get first empty slot
|
||||
local fullSlots = {}
|
||||
local positions = {}
|
||||
local emptyIndex, emptyPos
|
||||
for i, snap in ipairs(snaps) do
|
||||
positions[i] = self.positionToWorld(snap.position)
|
||||
local searchResult = searchLib.atPosition(positions[i], "isCardOrDeck")
|
||||
fullSlots[i] = #searchResult > 0
|
||||
local snapPos = self.positionToWorld(snap.position)
|
||||
local searchResult = searchLib.atPosition(snapPos, "isCardOrDeck")
|
||||
if #searchResult == 0 then
|
||||
emptyPos = snapPos
|
||||
emptyIndex = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- remove tokens from the card
|
||||
for _, obj in ipairs(searchLib.onObject(card)) do
|
||||
-- don't touch decks / cards
|
||||
if obj.type == "Deck" or obj.type == "Card" then
|
||||
local trash = guidReferenceApi.getObjectByOwnerAndType("Mythos", "Trash")
|
||||
for _, obj in ipairs(searchLib.onObject(card, "isTileOrToken")) do
|
||||
if tokenChecker.isChaosToken(obj) then
|
||||
-- put chaos tokens back into bag
|
||||
elseif tokenChecker.isChaosToken(obj) then
|
||||
local chaosBag = chaosBagApi.findChaosBag()
|
||||
chaosBag.putObject(obj)
|
||||
elseif obj.memo ~= nil and obj.getLock() == false then
|
||||
@ -263,18 +266,15 @@ function placeCard(card)
|
||||
end
|
||||
end
|
||||
|
||||
-- place the card
|
||||
local name = card.getName() or "Unnamed card"
|
||||
for i = 1, 10 do
|
||||
if fullSlots[i] ~= true then
|
||||
local rot = { 0, 270, card.getRotation().z }
|
||||
card.setPositionSmooth(positions[i], false, true)
|
||||
card.setRotation(rot)
|
||||
broadcastToAll("Victory Display: " .. name .. " placed into slot " .. i .. ".", "Green")
|
||||
return
|
||||
end
|
||||
end
|
||||
-- use the first snap position in case all slots are full
|
||||
local pos = emptyPos or self.positionToWorld(snaps[1].position)
|
||||
local rot = card.getRotation():setAt("x", 0):setAt("y", 270)
|
||||
deckLib.placeOrMergeIntoDeck(card, pos, rot)
|
||||
|
||||
broadcastToAll("Victory Display is full! " .. name .. " placed into slot 1.", "Orange")
|
||||
card.setPositionSmooth(positions[1], false, true)
|
||||
-- give a feedback message
|
||||
if emptyPos then
|
||||
broadcastToAll("Victory Display: " .. name .. " placed into slot " .. emptyIndex .. ".", "Green")
|
||||
else
|
||||
broadcastToAll("Victory Display is full! " .. name .. " placed into slot 1.", "Orange")
|
||||
end
|
||||
end
|
||||
|
@ -211,10 +211,11 @@ do
|
||||
if tokenType == "clue" then
|
||||
offsets = internal.buildClueOffsets(card, tokenCount)
|
||||
else
|
||||
-- only up to 12 offset tables defined (TODO: stack more than 12 tokens and generate offsets dynamically)
|
||||
-- only up to 12 offset tables defined
|
||||
if tokenCount > 12 then
|
||||
printToAll("Spawning maximum of 12 tokens.")
|
||||
tokenCount = 12
|
||||
printToAll("Attempting to spawn " .. tokenCount .. " tokens. Spawning clickable counter instead.")
|
||||
TokenManager.spawnResourceCounterToken(card, tokenCount)
|
||||
return
|
||||
end
|
||||
for i = 1, tokenCount do
|
||||
offsets[i] = card.positionToWorld(PLAYER_CARD_TOKEN_OFFSETS[tokenCount][i])
|
||||
@ -297,20 +298,13 @@ do
|
||||
-- Checks a card for metadata to maybe replenish it
|
||||
---@param card tts__Object Card object to be replenished
|
||||
---@param uses table The already decoded metadata.uses (to avoid decoding again)
|
||||
---@param mat tts__Object The playermat the card is placed on (for rotation and casting)
|
||||
TokenManager.maybeReplenishCard = function(card, uses, mat)
|
||||
TokenManager.maybeReplenishCard = function(card, uses)
|
||||
-- TODO: support for cards with multiple uses AND replenish (as of yet, no official card needs that)
|
||||
if uses[1].count and uses[1].replenish then
|
||||
internal.replenishTokens(card, uses, mat)
|
||||
internal.replenishTokens(card, uses)
|
||||
end
|
||||
end
|
||||
|
||||
-- Delegate function to the token spawn tracker. Exists to avoid circular dependencies in some callers
|
||||
---@param card tts__Object Card object to reset the tokens for
|
||||
TokenManager.resetTokensSpawned = function(card)
|
||||
tokenSpawnTrackerApi.resetTokensSpawned(card.getGUID())
|
||||
end
|
||||
|
||||
-- Pushes new player card data into the local copy of the Data Helper player data.
|
||||
---@param dataTable table Key/Value pairs following the DataHelper style
|
||||
TokenManager.addPlayerCardData = function(dataTable)
|
||||
@ -483,16 +477,16 @@ do
|
||||
|
||||
---@param card tts__Object Card object to be replenished
|
||||
---@param uses table The already decoded metadata.uses (to avoid decoding again)
|
||||
---@param mat tts__Object The playermat the card is placed on (for rotation and casting)
|
||||
internal.replenishTokens = function(card, uses, mat)
|
||||
-- get current amount of resource tokens on the card
|
||||
internal.replenishTokens = function(card, uses)
|
||||
-- get current amount of matching resource tokens on the card
|
||||
local clickableResourceCounter = nil
|
||||
local foundTokens = 0
|
||||
local searchType = string.lower(uses[1].type)
|
||||
|
||||
for _, obj in ipairs(searchLib.onObject(card, "isTileOrToken")) do
|
||||
local memo = obj.getMemo()
|
||||
|
||||
if (stateTable[memo] or 0) > 0 then
|
||||
if searchType == memo then
|
||||
foundTokens = foundTokens + math.abs(obj.getQuantity())
|
||||
obj.destruct()
|
||||
elseif memo == "resourceCounter" then
|
||||
|
@ -22,8 +22,8 @@ function markTokensSpawned(cardGuid)
|
||||
spawnedCardGuids[cardGuid] = true
|
||||
end
|
||||
|
||||
function resetTokensSpawned(cardGuid)
|
||||
spawnedCardGuids[cardGuid] = nil
|
||||
function resetTokensSpawned(card)
|
||||
spawnedCardGuids[card.getGUID()] = nil
|
||||
end
|
||||
|
||||
function resetAll() spawnedCardGuids = {} end
|
||||
@ -52,6 +52,6 @@ end
|
||||
-- Listener to reset card token spawns when they enter a hand.
|
||||
function onObjectEnterZone(zone, enterObject)
|
||||
if zone.type == "Hand" and enterObject.type == "Card" then
|
||||
resetTokensSpawned(enterObject.getGUID())
|
||||
resetTokensSpawned(enterObject)
|
||||
end
|
||||
end
|
||||
|
@ -14,8 +14,8 @@ do
|
||||
return getSpawnTracker().call("markTokensSpawned", cardGuid)
|
||||
end
|
||||
|
||||
TokenSpawnTracker.resetTokensSpawned = function(cardGuid)
|
||||
return getSpawnTracker().call("resetTokensSpawned", cardGuid)
|
||||
TokenSpawnTracker.resetTokensSpawned = function(card)
|
||||
return getSpawnTracker().call("resetTokensSpawned", card)
|
||||
end
|
||||
|
||||
TokenSpawnTracker.resetAllAssetAndEvents = function()
|
||||
|
@ -63,9 +63,8 @@ function onLoad(savedData)
|
||||
local loadedData = JSON.decode(savedData)
|
||||
isHelperEnabled = loadedData.isHelperEnabled
|
||||
end
|
||||
checkOptionPanel()
|
||||
createHelperXML()
|
||||
updateDisplay()
|
||||
checkOptionPanel()
|
||||
end
|
||||
|
||||
function createHelperXML()
|
||||
|
@ -5,6 +5,8 @@ function checkOptionPanel()
|
||||
local options = optionPanelApi.getOptions()
|
||||
if options.enableCardHelpers then
|
||||
setHelperState(true)
|
||||
else
|
||||
updateDisplay()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -5,7 +5,8 @@ function onLoad()
|
||||
end
|
||||
|
||||
-- rotates the alt_view_angle
|
||||
function rotatePreview()
|
||||
function rotatePreview(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local angle = self.alt_view_angle
|
||||
if angle.y == 0 then
|
||||
angle.y = 180
|
||||
@ -16,7 +17,7 @@ function rotatePreview()
|
||||
end
|
||||
|
||||
-- rotates this card and the preview
|
||||
function rotateSelfAndPreview()
|
||||
function rotateSelfAndPreview(playerColor)
|
||||
self.setRotationSmooth(self.getRotation() + Vector(0, 180, 0))
|
||||
rotatePreview()
|
||||
rotatePreview(playerColor)
|
||||
end
|
||||
|
@ -1,9 +1,11 @@
|
||||
require("playercards/CardsWithHelper")
|
||||
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
|
||||
local chaosBagApi = require("chaosbag/ChaosBagApi")
|
||||
local guidReferenceApi = require("core/GUIDReferenceApi")
|
||||
local playermatApi = require("playermat/PlayermatApi")
|
||||
|
||||
local isHelperEnabled = false
|
||||
local updated
|
||||
|
||||
function updateSave()
|
||||
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
|
||||
@ -16,7 +18,6 @@ function onLoad(savedData)
|
||||
isHelperEnabled = loadedData.isHelperEnabled
|
||||
end
|
||||
checkOptionPanel()
|
||||
updateDisplay()
|
||||
end
|
||||
|
||||
-- hide buttons and stop monitoring
|
||||
@ -35,6 +36,7 @@ function initialize()
|
||||
end
|
||||
|
||||
function resolveToken(player, _, tokenType)
|
||||
if not updated then return end
|
||||
local matColor
|
||||
if player.color == "Black" then
|
||||
matColor = playermatApi.getMatColorByPosition(self.getPosition())
|
||||
@ -44,11 +46,13 @@ function resolveToken(player, _, tokenType)
|
||||
|
||||
local mat = guidReferenceApi.getObjectByOwnerAndType(matColor, "Playermat")
|
||||
chaosBagApi.drawChaosToken(mat, true, tokenType)
|
||||
updated = false
|
||||
Wait.frames(maybeUpdateButtonState, 2)
|
||||
end
|
||||
|
||||
-- count tokens in the bag and show appropriate buttons
|
||||
function maybeUpdateButtonState()
|
||||
local numInBag = getBlessCurseInBag()
|
||||
local numInBag = blessCurseManagerApi.getBlessCurseInBag()
|
||||
local state = { Bless = false, Curse = false }
|
||||
|
||||
if numInBag.Bless >= numInBag.Curse and numInBag.Bless > 0 then
|
||||
@ -60,19 +64,7 @@ function maybeUpdateButtonState()
|
||||
end
|
||||
|
||||
setUiState(state)
|
||||
end
|
||||
|
||||
function getBlessCurseInBag()
|
||||
local numInBag = { Bless = 0, Curse = 0 }
|
||||
local chaosBag = chaosBagApi.findChaosBag()
|
||||
|
||||
for _, v in ipairs(chaosBag.getObjects()) do
|
||||
if v.name == "Bless" or v.name == "Curse" then
|
||||
numInBag[v.name] = numInBag[v.name] + 1
|
||||
end
|
||||
end
|
||||
|
||||
return numInBag
|
||||
updated = true
|
||||
end
|
||||
|
||||
function setUiState(params)
|
||||
@ -88,7 +80,7 @@ function setUiState(params)
|
||||
end
|
||||
|
||||
function errorMessage()
|
||||
local numInBag = getBlessCurseInBag()
|
||||
local numInBag = blessCurseManagerApi.getBlessCurseInBag()
|
||||
|
||||
if numInBag.Bless == 0 and numInBag.Curse == 0 then
|
||||
broadcastToAll("There are no Bless or Curse tokens in the chaos bag.", "Red")
|
||||
|
@ -46,7 +46,6 @@ function onLoad(savedData)
|
||||
activeButtonIndex = loadedData.activeButtonIndex
|
||||
end
|
||||
checkOptionPanel()
|
||||
updateDisplay()
|
||||
|
||||
if activeButtonIndex > 0 then
|
||||
selectButton(activeButtonIndex)
|
||||
@ -89,6 +88,7 @@ function createButtons()
|
||||
local upgradeSheet = findUpgradeSheet()
|
||||
if upgradeSheet then
|
||||
for i = 1, 4 do
|
||||
log(4)
|
||||
if upgradeSheet.call("isUpgradeActive", i) then
|
||||
table.insert(hypothesisList, customizableList[i])
|
||||
end
|
||||
@ -110,7 +110,7 @@ end
|
||||
|
||||
function findUpgradeSheet()
|
||||
local matColor = playermatApi.getMatColorByPosition(self.getPosition())
|
||||
local result = playermatApi.searchAroundPlaymat(matColor, "isCard")
|
||||
local result = playermatApi.searchAroundPlayermat(matColor, "isCard")
|
||||
for j, card in ipairs(result) do
|
||||
local metadata = JSON.decode(card.getGMNotes()) or {}
|
||||
if metadata.id == "09041-c" then
|
||||
|
@ -6,9 +6,21 @@ local clickableResourceCounter = nil
|
||||
local foundTokens = 0
|
||||
|
||||
function onLoad()
|
||||
self.addContextMenuItem("Add 4 resources", function(playerColor) add4(playerColor) end)
|
||||
self.addContextMenuItem("Take all resources", function(playerColor) takeAll(playerColor) end)
|
||||
self.addContextMenuItem("Discard all resources", function(playerColor) loseAll(playerColor) end)
|
||||
self.addContextMenuItem("Add 4 resources",
|
||||
function(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
add4(playerColor)
|
||||
end)
|
||||
self.addContextMenuItem("Take all resources",
|
||||
function(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
takeAll(playerColor)
|
||||
end)
|
||||
self.addContextMenuItem("Discard all resources",
|
||||
function(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
loseAll(playerColor)
|
||||
end)
|
||||
end
|
||||
|
||||
function searchSelf()
|
||||
@ -35,12 +47,7 @@ function add4(playerColor)
|
||||
if clickableResourceCounter then
|
||||
clickableResourceCounter.call("updateVal", newCount)
|
||||
else
|
||||
if newCount > 12 then
|
||||
printToColor("Count increased to " .. newCount .. " resources. Spawning clickable counter instead.", playerColor)
|
||||
tokenManager.spawnResourceCounterToken(self, newCount)
|
||||
else
|
||||
tokenManager.spawnTokenGroup(self, "resource", newCount)
|
||||
end
|
||||
tokenManager.spawnTokenGroup(self, "resource", newCount)
|
||||
end
|
||||
end
|
||||
|
||||
|
147
src/playercards/cards/KohakuNarukami.ttslua
Normal file
147
src/playercards/cards/KohakuNarukami.ttslua
Normal file
@ -0,0 +1,147 @@
|
||||
require("playercards/CardsWithHelper")
|
||||
local blessCurseManagerApi = require("chaosbag/BlessCurseManagerApi")
|
||||
local guidReferenceApi = require("core/GUIDReferenceApi")
|
||||
local playermatApi = require("playermat/PlayermatApi")
|
||||
local searchLib = require("util/SearchLib")
|
||||
local tokenManager = require("core/token/TokenManager")
|
||||
|
||||
local isHelperEnabled = false
|
||||
local updated
|
||||
|
||||
local xmlData = {
|
||||
Action = { color = "#6D202CE6", onClick = "removeAndExtraAction" },
|
||||
Bless = { color = "#9D702CE6", onClick = "addTokenToBag" },
|
||||
Curse = { color = "#633A84E6", onClick = "addTokenToBag" },
|
||||
ElderSign = { color = "#50A8CEE6", onClick = "elderSignAbility" }
|
||||
}
|
||||
|
||||
function updateSave()
|
||||
self.script_state = JSON.encode({ isHelperEnabled = isHelperEnabled })
|
||||
end
|
||||
|
||||
function onLoad(savedData)
|
||||
self.addTag("CardWithHelper")
|
||||
if savedData and savedData ~= "" then
|
||||
local loadedData = JSON.decode(savedData)
|
||||
isHelperEnabled = loadedData.isHelperEnabled
|
||||
end
|
||||
checkOptionPanel()
|
||||
end
|
||||
|
||||
-- hide buttons and stop monitoring
|
||||
function shutOff()
|
||||
self.UI.hide("Helper")
|
||||
Wait.stopAll()
|
||||
updateSave()
|
||||
end
|
||||
|
||||
-- show buttons and begin monitoring chaos bag for curse and bless tokens
|
||||
function initialize()
|
||||
self.UI.show("Helper")
|
||||
maybeUpdateButtonState()
|
||||
Wait.time(maybeUpdateButtonState, 1, -1)
|
||||
updateSave()
|
||||
end
|
||||
|
||||
function addTokenToBag(_, _, tokenType)
|
||||
if not updated then return end
|
||||
blessCurseManagerApi.addToken(tokenType)
|
||||
updated = false
|
||||
Wait.frames(maybeUpdateButtonState, 2)
|
||||
end
|
||||
|
||||
function removeAndExtraAction()
|
||||
if not updated then return end
|
||||
blessCurseManagerApi.removeToken("Bless")
|
||||
blessCurseManagerApi.removeToken("Bless")
|
||||
blessCurseManagerApi.removeToken("Curse")
|
||||
blessCurseManagerApi.removeToken("Curse")
|
||||
updated = false
|
||||
Wait.frames(maybeUpdateButtonState, 2)
|
||||
|
||||
local position = self.getPosition()
|
||||
local matColor = playermatApi.getMatColorByPosition(position)
|
||||
local mat = guidReferenceApi.getObjectByOwnerAndType(matColor, "Playermat")
|
||||
local rotation = mat.getRotation()
|
||||
|
||||
-- find empty action token slots by checking snap points
|
||||
local snaps = mat.getSnapPoints()
|
||||
|
||||
-- get first empty slot in the top row (so the second empty slot because of the ability token)
|
||||
local emptyPos = position -- default to card position
|
||||
for i, snap in ipairs(snaps) do
|
||||
if i > 1 then
|
||||
if snap.tags[1] == "UniversalToken" then
|
||||
local snapPos = mat.positionToWorld(snap.position)
|
||||
local searchResult = searchLib.atPosition(snapPos, "isUniversalToken")
|
||||
if #searchResult == 0 then
|
||||
emptyPos = snapPos
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
callback = function(spawned)
|
||||
spawned.call("updateClassAndSymbol", { class = "Mystic", symbol = "Mystic" })
|
||||
spawned.addTag("Temporary")
|
||||
end
|
||||
tokenManager.spawnToken(emptyPos + Vector(0, 0.7, 0), "universalActionAbility", rotation, callback)
|
||||
end
|
||||
|
||||
function elderSignAbility()
|
||||
blessCurseManagerApi.addToken("Bless")
|
||||
blessCurseManagerApi.addToken("Curse")
|
||||
updated = false
|
||||
Wait.frames(maybeUpdateButtonState, 2)
|
||||
end
|
||||
|
||||
-- count tokens in the bag and show appropriate buttons
|
||||
function maybeUpdateButtonState()
|
||||
local numInBag = blessCurseManagerApi.getBlessCurseInBag()
|
||||
local state = { Bless = false, Curse = false, Action = false, ElderSign = false }
|
||||
|
||||
if numInBag.Bless <= numInBag.Curse and numInBag.Bless < 10 then
|
||||
state.Bless = true
|
||||
state.ElderSign = true
|
||||
end
|
||||
|
||||
if numInBag.Curse <= numInBag.Bless and numInBag.Curse < 10 then
|
||||
state.Curse = true
|
||||
state.ElderSign = true
|
||||
end
|
||||
|
||||
if numInBag.Curse >= 2 and numInBag.Bless >= 2 then
|
||||
state.Action = true
|
||||
end
|
||||
|
||||
setUiState(state)
|
||||
updated = true
|
||||
end
|
||||
|
||||
function setUiState(params)
|
||||
for buttonId, state in pairs(params) do
|
||||
if state then
|
||||
self.UI.setAttribute(buttonId, "color", xmlData[buttonId].color)
|
||||
self.UI.setAttribute(buttonId, "onClick", xmlData[buttonId].onClick)
|
||||
self.UI.setAttribute(buttonId, "textColor", "white")
|
||||
else
|
||||
self.UI.setAttribute(buttonId, "color", "#353535E6")
|
||||
self.UI.setAttribute(buttonId, "onClick", "errorMessage")
|
||||
self.UI.setAttribute(buttonId, "textColor", "#A0A0A0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function errorMessage(_, _, triggeringButton)
|
||||
local numInBag = blessCurseManagerApi.getBlessCurseInBag()
|
||||
if triggeringButton == "Action" then
|
||||
broadcastToAll("There are not enough Blesses and/or Curses in the chaos bag.", "Red")
|
||||
elseif numInBag.Bless == 10 and numInBag.Curse == 10 then
|
||||
broadcastToAll("No more tokens can be added to the chaos bag.", "Red")
|
||||
elseif numInBag.Bless < numInBag.Curse then
|
||||
broadcastToAll("There are more Bless tokens than Curse tokens in the chaos bag.", "Red")
|
||||
else
|
||||
broadcastToAll("There are more Curse tokens than Bless tokens in the chaos bag.", "Red")
|
||||
end
|
||||
end
|
@ -61,6 +61,7 @@ end
|
||||
|
||||
-- Create dialog window to choose sigil and create sigil-drawing button
|
||||
function chooseSigil(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
self.clearContextMenu()
|
||||
self.addContextMenuItem("Clear Helper", deleteButtons)
|
||||
|
||||
@ -80,7 +81,8 @@ function chooseSigil(playerColor)
|
||||
end
|
||||
|
||||
-- Delete button and remove sigil
|
||||
function deleteButtons()
|
||||
function deleteButtons(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
self.clearContextMenu()
|
||||
self.addContextMenuItem("Enable Helper", chooseSigil)
|
||||
self.UI.setXml("")
|
||||
|
@ -17,6 +17,7 @@ function onLoad()
|
||||
end
|
||||
|
||||
function contextFunc(playerColor, amount)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
deckData = {}
|
||||
local options = {}
|
||||
|
||||
|
@ -5,7 +5,8 @@ function onLoad()
|
||||
end
|
||||
|
||||
-- called by context menu entry
|
||||
function shortSupply(color)
|
||||
function shortSupply(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local matColor = playermatApi.getMatColorByPosition(self.getPosition())
|
||||
|
||||
-- get draw deck and discard position
|
||||
@ -15,20 +16,20 @@ function shortSupply(color)
|
||||
|
||||
-- error handling
|
||||
if discardPos == nil then
|
||||
broadcastToColor("Couldn't retrieve discard position from playermat!", color, "Red")
|
||||
broadcastToColor("Couldn't retrieve discard position from playermat!", playerColor, "Red")
|
||||
return
|
||||
end
|
||||
|
||||
if drawDeck == nil then
|
||||
broadcastToColor("Deck not found!", color, "Yellow")
|
||||
broadcastToColor("Deck not found!", playerColor, "Yellow")
|
||||
return
|
||||
elseif drawDeck.type ~= "Deck" then
|
||||
broadcastToColor("Deck only contains a single card!", color, "Yellow")
|
||||
broadcastToColor("Deck only contains a single card!", playerColor, "Yellow")
|
||||
return
|
||||
end
|
||||
|
||||
-- discard cards, waiting 0.7 seconds between each discard to give players visiblity of the cards
|
||||
broadcastToColor("Discarding top 10 cards for player color '" .. matColor .. "'.", color, "White")
|
||||
broadcastToColor("Discarding top 10 cards for player color '" .. matColor .. "'.", playerColor, "White")
|
||||
for i = 1, 10 do
|
||||
Wait.time(function() drawDeck.takeObject({ flip = true, position = { discardPos.x, 2 + 0.075 * i, discardPos.z } }) end, .7 * (i - 1))
|
||||
end
|
||||
|
@ -7,7 +7,8 @@ function onLoad()
|
||||
end
|
||||
|
||||
-- uses the tekeli-li helper to place this card at the bottom of the deck
|
||||
function returnSelf()
|
||||
function returnSelf(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local helper = getTekeliliHelper()
|
||||
if helper == nil then
|
||||
printToAll("Couldn't find Tekeli-li Helper!")
|
||||
@ -18,6 +19,7 @@ end
|
||||
|
||||
-- places this card below the deck of the player that triggered it
|
||||
function placeBelowDeck(playerColor)
|
||||
Player[playerColor].clearSelectedObjects()
|
||||
local matColor = playermatApi.getMatColor(playerColor)
|
||||
local deckPos = playermatApi.getDrawPosition(matColor)
|
||||
local deckRot = playermatApi.returnRotation(matColor)
|
||||
|
@ -6,6 +6,7 @@ local navigationOverlayApi = require("core/NavigationOverlayApi")
|
||||
local searchLib = require("util/SearchLib")
|
||||
local tokenChecker = require("core/token/TokenChecker")
|
||||
local tokenManager = require("core/token/TokenManager")
|
||||
local tokenSpawnTrackerApi = require("core/token/TokenSpawnTrackerApi")
|
||||
|
||||
-- option panel data
|
||||
local availableOptions = {
|
||||
@ -75,12 +76,12 @@ local buttonParameters = {
|
||||
|
||||
-- table of texture URLs
|
||||
local nameToTexture = {
|
||||
Guardian = "http://cloud-3.steamusercontent.com/ugc/2444972799638881117/169F4520A94FB186B54E0F2BF4BAC809844C923E/",
|
||||
Mystic = "http://cloud-3.steamusercontent.com/ugc/2444972799638880413/B59966123EA41649EDCBD614167E590C8C105582/",
|
||||
Guardian = "http://cloud-3.steamusercontent.com/ugc/2501268517203536128/853B9CD08FC14A8B2A08C73D8ED77E0FE235CCCB/",
|
||||
Mystic = "http://cloud-3.steamusercontent.com/ugc/2501268517203536470/11C99488B9CA9236059A5F02E4A852A7C77B42A6/",
|
||||
Neutral = "http://cloud-3.steamusercontent.com/ugc/2462982115659543571/5D778EA4BC682DAE97E8F59A991BCF8CB3979B04/",
|
||||
Rogue = "http://cloud-3.steamusercontent.com/ugc/2444972799638880905/CFC02BF5A6140B9B4B92312AD6DC74D8DD61180B/",
|
||||
Seeker = "http://cloud-3.steamusercontent.com/ugc/2444972799638881117/169F4520A94FB186B54E0F2BF4BAC809844C923E/",
|
||||
Survivor = "http://cloud-3.steamusercontent.com/ugc/2444972799638880687/EEDF8F8BC3266069FECB09775845BE2501310C17/"
|
||||
Rogue = "http://cloud-3.steamusercontent.com/ugc/2501268517203536767/587791B327255DB8F953B27BB9E4DE602FA32B64/",
|
||||
Seeker = "http://cloud-3.steamusercontent.com/ugc/2501268517203537098/EFD9FC4CCDB105EFFDFF7A57C915CD984865760D/",
|
||||
Survivor = "http://cloud-3.steamusercontent.com/ugc/2501268517203537426/14EF780606D9A449F31A007226CB48D05AA820FF/"
|
||||
}
|
||||
|
||||
-- translation table for slot names to characters for special font
|
||||
@ -325,7 +326,9 @@ function doUpkeep(_, clickedByColor, isRightClick)
|
||||
local forcedLearning = false
|
||||
local rot = self.getRotation()
|
||||
for _, obj in ipairs(searchAroundSelf()) do
|
||||
if obj.hasTag("UniversalToken") == true and obj.is_face_down then
|
||||
if obj.hasTag("Temporary") == true then
|
||||
discardListOfObjects({ obj })
|
||||
elseif obj.hasTag("UniversalToken") == true and obj.is_face_down then
|
||||
obj.flip()
|
||||
elseif obj.type == "Card" and not inArea(self.positionToLocal(obj.getPosition()), INVESTIGATOR_AREA) then
|
||||
local cardMetadata = JSON.decode(obj.getGMNotes()) or {}
|
||||
@ -355,7 +358,7 @@ function doUpkeep(_, clickedByColor, isRightClick)
|
||||
end
|
||||
|
||||
-- maybe replenish uses on certain cards (don't continue for cards on the deck (Norman) or in the discard pile)
|
||||
if cardMetadata.uses ~= nil and self.positionToLocal(obj.getPosition()).x < -1 then
|
||||
if cardMetadata.uses ~= nil and self.positionToLocal(obj.getPosition()).x > -1 then
|
||||
tokenManager.maybeReplenishCard(obj, cardMetadata.uses, self)
|
||||
end
|
||||
elseif obj.type == "Deck" and forcedLearning == false then
|
||||
@ -399,34 +402,29 @@ function doUpkeep(_, clickedByColor, isRightClick)
|
||||
local handCards = Player[playerColor].getHandObjects()
|
||||
local cardsToDiscard = {}
|
||||
|
||||
for i = 1, #handCards do
|
||||
local metadata = JSON.decode(handCards[i].getGMNotes())
|
||||
if metadata ~= nil and (not metadata.weakness and not metadata.hidden) then
|
||||
table.insert(cardsToDiscard, handCards[i])
|
||||
for _, card in ipairs(handCards) do
|
||||
local md = JSON.decode(card.getGMNotes())
|
||||
if md ~= nil and (not md.weakness and not md.hidden and md.type ~= "Enemy") then
|
||||
table.insert(cardsToDiscard, card)
|
||||
end
|
||||
end
|
||||
|
||||
-- perform discarding 1 by 1
|
||||
local pos = returnGlobalDiscardPosition()
|
||||
local count = #cardsToDiscard
|
||||
for i = count, 1, -1 do
|
||||
Wait.time(function() deckLib.placeOrMergeIntoDeck(cardsToDiscard[i], pos, rot) end, (count - i + 1) * 0.1)
|
||||
end
|
||||
deckLib.placeOrMergeIntoDeck(cardsToDiscard, pos, self.getRotation())
|
||||
|
||||
-- add some time if there are any cards to discard, if not, draw up to 5 immediately
|
||||
local k = 0
|
||||
if count > 0 then
|
||||
k = 0.7 + (count * 0.1)
|
||||
end
|
||||
-- draw up to 5 cards
|
||||
local cardsToDraw = 5 - #handCards + #cardsToDiscard
|
||||
if cardsToDraw > 0 then
|
||||
printToColor("Discarding " .. #cardsToDiscard .. " and drawing " .. cardsToDraw .. " card(s). (Patrice)", messageColor)
|
||||
|
||||
Wait.time(function()
|
||||
local handSize = #Player[playerColor].getHandObjects()
|
||||
if handSize < 5 then
|
||||
local cardsToDraw = 5 - handSize
|
||||
printToColor("Drawing " .. cardsToDraw .. " cards (Patrice)", messageColor)
|
||||
drawCardsWithReshuffle(cardsToDraw)
|
||||
-- add some time if there are any cards to discard
|
||||
local k = 0
|
||||
if #cardsToDiscard > 0 then
|
||||
k = 0.8 + (#cardsToDiscard * 0.1)
|
||||
end
|
||||
end, k)
|
||||
Wait.time(function() drawCardsWithReshuffle(cardsToDraw) end, k)
|
||||
end
|
||||
end
|
||||
elseif forcedLearning then
|
||||
printToColor("Drawing 2 cards, discard 1 (Forced Learning)", messageColor)
|
||||
@ -585,9 +583,8 @@ function doDiscardOne()
|
||||
-- get a random non-hidden card (from the "choices" table)
|
||||
local num = math.random(1, #choices)
|
||||
deckLib.placeOrMergeIntoDeck(hand[choices[num]], returnGlobalDiscardPosition(), self.getRotation())
|
||||
|
||||
local playerName = Player[playerColor].steam_name or playerColor
|
||||
broadcastToAll(playerName .. " randomly discarded card " .. choices[num] .. "/" .. #hand .. ".", "White")
|
||||
broadcastToAll(getColoredName(playerColor) .. " randomly discarded card "
|
||||
.. choices[num] .. "/" .. #hand .. ".", "White")
|
||||
end
|
||||
end
|
||||
|
||||
@ -974,7 +971,7 @@ function onCollisionEnter(collisionInfo)
|
||||
|
||||
local localCardPos = self.positionToLocal(object.getPosition())
|
||||
if inArea(localCardPos, DECK_DISCARD_AREA) then
|
||||
tokenManager.resetTokensSpawned(object)
|
||||
tokenSpawnTrackerApi.resetTokensSpawned(object)
|
||||
removeTokensFromObject(object)
|
||||
elseif shouldSpawnTokens(object) then
|
||||
spawnTokensFor(object)
|
||||
@ -1018,7 +1015,7 @@ function onObjectEnterContainer(container, object)
|
||||
|
||||
local localCardPos = self.positionToLocal(object.getPosition())
|
||||
if inArea(localCardPos, DECK_DISCARD_AREA) then
|
||||
tokenManager.resetTokensSpawned(object)
|
||||
tokenSpawnTrackerApi.resetTokensSpawned(object)
|
||||
removeTokensFromObject(object)
|
||||
end
|
||||
end
|
||||
@ -1397,3 +1394,14 @@ function updatePlayerCards(args)
|
||||
local playerCardData = customDataHelper.getTable("PLAYER_CARD_DATA")
|
||||
tokenManager.addPlayerCardData(playerCardData)
|
||||
end
|
||||
|
||||
-- returns the colored steam name or color
|
||||
function getColoredName(playerColor)
|
||||
local displayName = playerColor
|
||||
if Player[playerColor].steam_name then
|
||||
displayName = Player[playerColor].steam_name
|
||||
end
|
||||
|
||||
-- add bb-code
|
||||
return "[" .. Color.fromString(playerColor):toHex() .. "]" .. displayName .. "[-]"
|
||||
end
|
||||
|
@ -2,23 +2,33 @@ do
|
||||
local DeckLib = {}
|
||||
local searchLib = require("util/SearchLib")
|
||||
|
||||
-- places a card/deck at a position or merges into an existing deck
|
||||
---@param obj tts__Object Object to move
|
||||
-- places a card/deck at a position or merges into an existing deck below
|
||||
---@param objOrTable tts__Object|table Object or table of objects to move
|
||||
---@param pos table New position for the object
|
||||
---@param rot? table New rotation for the object
|
||||
---@param below? boolean Should the object be placed below an existing deck?
|
||||
DeckLib.placeOrMergeIntoDeck = function(obj, pos, rot, below)
|
||||
if obj == nil or pos == nil then return end
|
||||
DeckLib.placeOrMergeIntoDeck = function(objOrTable, pos, rot, below)
|
||||
if objOrTable == nil or pos == nil then return end
|
||||
|
||||
-- handle 'objOrTable' parameter
|
||||
local objects = {}
|
||||
if type(objOrTable) == "table" then
|
||||
objects = objOrTable
|
||||
else
|
||||
table.insert(objects, objOrTable)
|
||||
end
|
||||
|
||||
-- search the new position for existing card/deck
|
||||
local searchResult = searchLib.atPosition(pos, "isCardOrDeck")
|
||||
local targetObj
|
||||
|
||||
-- get new position
|
||||
local offset = 0.5
|
||||
local newPos = Vector(pos) + Vector(0, offset, 0)
|
||||
|
||||
if #searchResult == 1 then
|
||||
local bounds = searchResult[1].getBounds()
|
||||
targetObj = searchResult[1]
|
||||
local bounds = targetObj.getBounds()
|
||||
if below then
|
||||
newPos = Vector(pos):setAt("y", bounds.center.y - bounds.size.y / 2)
|
||||
else
|
||||
@ -26,25 +36,39 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
-- allow moving the objects smoothly out of the hand
|
||||
obj.use_hands = false
|
||||
-- process objects in reverse order
|
||||
for i = #objects, 1, -1 do
|
||||
local obj = objects[i]
|
||||
-- add a 0.1 delay for each object (for animation purposes)
|
||||
Wait.time(function()
|
||||
-- allow moving smoothly out of hand and temporarily lock it
|
||||
obj.setLock(true)
|
||||
obj.use_hands = false
|
||||
|
||||
if rot then
|
||||
obj.setRotationSmooth(rot, false, true)
|
||||
if rot then
|
||||
obj.setRotationSmooth(rot, false, true)
|
||||
end
|
||||
obj.setPositionSmooth(newPos, false, true)
|
||||
|
||||
-- wait for object to finish movement (or 2 seconds)
|
||||
Wait.condition(
|
||||
function()
|
||||
-- revert toggles
|
||||
obj.setLock(false)
|
||||
obj.use_hands = true
|
||||
|
||||
-- use putObject to avoid a TTS bug that merges unrelated cards that are not resting
|
||||
if #searchResult == 1 and targetObj ~= obj and not targetObj.isDestroyed() and not obj.isDestroyed() then
|
||||
targetObj = targetObj.putObject(obj)
|
||||
else
|
||||
targetObj = obj
|
||||
end
|
||||
end,
|
||||
-- check state of the object (make sure it's not moving)
|
||||
function() return obj.isDestroyed() or not obj.isSmoothMoving() end,
|
||||
2)
|
||||
end, (#objects- i) * 0.1)
|
||||
end
|
||||
obj.setPositionSmooth(newPos, false, true)
|
||||
|
||||
-- continue if the card stops smooth moving
|
||||
Wait.condition(
|
||||
function()
|
||||
obj.use_hands = true
|
||||
-- this avoids a TTS bug that merges unrelated cards that are not resting
|
||||
if #searchResult == 1 and searchResult[1] ~= obj then
|
||||
-- call this with avoiding errors (physics is sometimes too fast so the object doesn't exist for the put)
|
||||
pcall(function() searchResult[1].putObject(obj) end)
|
||||
end
|
||||
end,
|
||||
function() return not obj.isSmoothMoving() end, 3)
|
||||
end
|
||||
|
||||
return DeckLib
|
||||
|
@ -62,7 +62,7 @@
|
||||
<Panel class="doubleColumn-wrapper"
|
||||
padding="0 17 3 3"/>
|
||||
<Button class="optionToggle"
|
||||
image="option-off"
|
||||
image="option_off"
|
||||
rectAlignment="MiddleRight"
|
||||
offsetXY="-30 0"
|
||||
colors="#FFFFFF|#dfdfdf"
|
||||
|
@ -1,25 +1,54 @@
|
||||
<!-- Default formatting -->
|
||||
<Defaults>
|
||||
<Text color="black" alignment="MiddleLeft"/>
|
||||
<Text class="h1" fontSize="160" font="font_teutonic-arkham"/>
|
||||
<Text class="h2" fontSize="120" font="font_teutonic-arkham"/>
|
||||
<Text class="p" fontSize="60" alignment="UpperLeft"/>
|
||||
<Text color="black"
|
||||
alignment="MiddleLeft"/>
|
||||
<Text class="h1"
|
||||
fontSize="160"
|
||||
font="font_teutonic-arkham"/>
|
||||
<Text class="h2"
|
||||
fontSize="120"
|
||||
font="font_teutonic-arkham"/>
|
||||
<Text class="p"
|
||||
fontSize="60"
|
||||
alignment="UpperLeft"/>
|
||||
|
||||
<Panel rotation="0 0 180"/>
|
||||
<Panel class="window" width="1500" height="1500" color="white" outline="white" outlineSize="10 10"/>
|
||||
<Panel class="window"
|
||||
width="1500"
|
||||
height="1500"
|
||||
color="white"
|
||||
outline="white"
|
||||
outlineSize="10 10"/>
|
||||
|
||||
<Row dontUseTableRowBackground="true"/>
|
||||
<Row class="header" color="#707070"/>
|
||||
<Row class="option" preferredHeight="200" color="#9e9e9e"/>
|
||||
<Row class="header"
|
||||
color="#707070"/>
|
||||
<Row class="option"
|
||||
preferredHeight="200"
|
||||
color="#9e9e9e"/>
|
||||
|
||||
<!-- row heights: 70 x lines + 50 -->
|
||||
<Row class="description" color="#cfcfcf"/>
|
||||
<Row class="description"
|
||||
color="#cfcfcf"/>
|
||||
|
||||
<Button class="optionToggle" rectAlignment="MiddleRight" offsetXY="-30 0" colors="#FFFFFF|#dfdfdf" height="160" width="288" ignoreLayout="True" fontSize="60"/>
|
||||
<Button class="optionToggle"
|
||||
rectAlignment="MiddleRight"
|
||||
offsetXY="-30 0"
|
||||
colors="#FFFFFF|#dfdfdf"
|
||||
height="160"
|
||||
width="288"
|
||||
ignoreLayout="True"
|
||||
fontSize="60"/>
|
||||
</Defaults>
|
||||
|
||||
<!-- Option window -->
|
||||
<Panel id="options" class="window" offsetXY="-580 200" scale="0.5 0.5" active="false" showAnimation="FadeIn" hideAnimation="FadeOut">
|
||||
<Panel id="options"
|
||||
class="window"
|
||||
offsetXY="-580 200"
|
||||
scale="0.5 0.5"
|
||||
active="false"
|
||||
showAnimation="FadeIn"
|
||||
hideAnimation="FadeOut">
|
||||
<TableLayout cellPadding="25 25 15 15">
|
||||
<!-- Header -->
|
||||
<Row class="header">
|
||||
@ -32,10 +61,14 @@
|
||||
<Row class="option">
|
||||
<Cell>
|
||||
<Text class="h2">Import trauma</Text>
|
||||
<Button class="optionToggle" id="importTrauma" onClick="optionButtonClick(importTrauma)" image="option_on"/>
|
||||
<Button class="optionToggle"
|
||||
id="importTrauma"
|
||||
onClick="optionButtonClick(importTrauma)"
|
||||
image="option_on"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
<Row class="description" preferredHeight="330">
|
||||
<Row class="description"
|
||||
preferredHeight="330">
|
||||
<Cell>
|
||||
<Text class="p">Enables importing trauma values from the campaign log (custom content might give wrong values!).
Enter players in the campaign log in this order:
White, Orange, Green, Red.</Text>
|
||||
</Cell>
|
||||
@ -45,10 +78,14 @@
|
||||
<Row class="option">
|
||||
<Cell>
|
||||
<Text class="h2">Tidy playermats</Text>
|
||||
<Button class="optionToggle" id="tidyPlayermats" onClick="optionButtonClick(tidyPlayermats)" image="option_on"/>
|
||||
<Button class="optionToggle"
|
||||
id="tidyPlayermats"
|
||||
onClick="optionButtonClick(tidyPlayermats)"
|
||||
image="option_on"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
<Row class="description" preferredHeight="190">
|
||||
<Row class="description"
|
||||
preferredHeight="190">
|
||||
<Cell>
|
||||
<Text class="p">Controls whether the playermats should get tidied (removal of all cards and tokens).</Text>
|
||||
</Cell>
|
||||
@ -58,10 +95,14 @@
|
||||
<Row class="option">
|
||||
<Cell>
|
||||
<Text class="h2">Remove drawn lines</Text>
|
||||
<Button class="optionToggle" id="removeDrawnLines" onClick="optionButtonClick(removeDrawnLines)" image="option_off"/>
|
||||
<Button class="optionToggle"
|
||||
id="removeDrawnLines"
|
||||
onClick="optionButtonClick(removeDrawnLines)"
|
||||
image="option_off"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
<Row class="description" preferredHeight="120">
|
||||
<Row class="description"
|
||||
preferredHeight="120">
|
||||
<Cell>
|
||||
<Text class="p">Controls whether all drawn lines should be removed.</Text>
|
||||
</Cell>
|
||||
|
42
xml/playercards/KohakuNarukami.xml
Normal file
42
xml/playercards/KohakuNarukami.xml
Normal file
@ -0,0 +1,42 @@
|
||||
<Defaults>
|
||||
<Button padding="50 50 50 50"
|
||||
font="font_teutonic-arkham"
|
||||
fontSize="200"
|
||||
iconWidth="300"
|
||||
iconAlignment="Right"/>
|
||||
<TableLayout position="0 188 -22"
|
||||
rotation="0 0 90"
|
||||
height="1800"
|
||||
width="700"
|
||||
scale="0.1 0.1 1"
|
||||
cellSpacing="80"
|
||||
cellBackgroundColor="rgba(1,1,1,0)"/>
|
||||
</Defaults>
|
||||
|
||||
<TableLayout id="Helper"
|
||||
active="false">
|
||||
<Row>
|
||||
<Cell>
|
||||
<Button id="Bless"
|
||||
icon="token-bless"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell>
|
||||
<Button id="Curse"
|
||||
icon="token-curse"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell>
|
||||
<Button id="Action"
|
||||
text="Remove tokens"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
<Row>
|
||||
<Cell>
|
||||
<Button id="ElderSign"
|
||||
icon="token-eldersign"/>
|
||||
</Cell>
|
||||
</Row>
|
||||
</TableLayout>
|
Loading…
Reference in New Issue
Block a user