diff --git a/modsettings/CustomUIAssets.json b/modsettings/CustomUIAssets.json
index 46ca3769..dc84a999 100644
--- a/modsettings/CustomUIAssets.json
+++ b/modsettings/CustomUIAssets.json
@@ -234,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,
diff --git a/objects/AllPlayerCards.15bb07/KhakuNarukami.54eaa7.json b/objects/AllPlayerCards.15bb07/KhakuNarukami.54eaa7.json
index 7c317a95..3cc17a73 100644
--- a/objects/AllPlayerCards.15bb07/KhakuNarukami.54eaa7.json
+++ b/objects/AllPlayerCards.15bb07/KhakuNarukami.54eaa7.json
@@ -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"
}
diff --git a/src/chaosbag/BlessCurseManager.ttslua b/src/chaosbag/BlessCurseManager.ttslua
index 0d8f50eb..945123e0 100644
--- a/src/chaosbag/BlessCurseManager.ttslua
+++ b/src/chaosbag/BlessCurseManager.ttslua
@@ -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
---------------------------------------------------------
diff --git a/src/chaosbag/BlessCurseManagerApi.ttslua b/src/chaosbag/BlessCurseManagerApi.ttslua
index 00290284..896a6c96 100644
--- a/src/chaosbag/BlessCurseManagerApi.ttslua
+++ b/src/chaosbag/BlessCurseManagerApi.ttslua
@@ -67,5 +67,9 @@ do
getManager().call("removeToken", type)
end
+ BlessCurseManagerApi.getBlessCurseInBag = function()
+ return getManager().call("getBlessCurseInBag", {})
+ end
+
return BlessCurseManagerApi
end
diff --git a/src/playercards/cards/BookofLivingMyths.ttslua b/src/playercards/cards/BookofLivingMyths.ttslua
index b30f04d6..776c9d8b 100644
--- a/src/playercards/cards/BookofLivingMyths.ttslua
+++ b/src/playercards/cards/BookofLivingMyths.ttslua
@@ -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 })
@@ -34,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())
@@ -43,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
@@ -59,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)
@@ -87,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")
diff --git a/src/playercards/cards/KohakuNarukami.ttslua b/src/playercards/cards/KohakuNarukami.ttslua
new file mode 100644
index 00000000..1d4c20d9
--- /dev/null
+++ b/src/playercards/cards/KohakuNarukami.ttslua
@@ -0,0 +1,139 @@
+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
+
+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
+ -- check snap point states
+ local snaps = mat.getSnapPoints()
+
+ -- get first empty slot
+ local fullSlots = {}
+ local positions = {}
+ for _, snap in ipairs(snaps) do
+ if snap.tags[1] == "UniversalToken" then
+ table.insert(positions, mat.positionToWorld(snap.position))
+ local searchResult = searchLib.atPosition(positions[#positions], "isUniversalToken")
+ table.insert(fullSlots, #searchResult > 0)
+ end
+ end
+
+ for i = 2, 6 do -- look at all 5 slots above investigator card
+ if fullSlots[i] ~= true then
+ callback = function(spawned) spawned.call("updateClassAndSymbol", { class = "Mystic", symbol = "Mystic" }) spawned.addTag("Temporary") end
+ tokenManager.spawnToken(positions[i] + Vector(0, 0.7, 0), "universalActionAbility", rotation, callback)
+ return
+ end
+ end
+
+ -- if all slots are full
+ callback = function(spawned) spawned.call("updateClassAndSymbol", { class = "Mystic", symbol = "Mystic" }) spawned.addTag("Temporary") end
+ tokenManager.spawnToken(position + Vector(0, 0.7, 0), "universalActionAbility", rotation, callback)
+end
+
+function elderSignAbility()
+ blessCurseManagerApi.addToken("Bless")
+ blessCurseManagerApi.addToken("Curse")
+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
+ end
+
+ if numInBag.Curse <= numInBag.Bless and numInBag.Curse < 10 then
+ state.Curse = true
+ end
+
+ if numInBag.Curse >= 2 and numInBag.Bless >= 2 then
+ state.Action = true
+ end
+
+ state.ElderSign = true
+
+ setUiState(state)
+ updated = true
+end
+
+function setUiState(params)
+ for _, tokenName in ipairs({ "Bless", "Curse", "Action", "ElderSign" }) do
+ if params[tokenName] then
+ self.UI.show(tokenName)
+ self.UI.hide("inactive" .. tokenName)
+ else
+ self.UI.show("inactive" .. tokenName)
+ self.UI.hide(tokenName)
+ end
+ end
+end
+
+function errorMessage(_, _, triggeringButton)
+ local numInBag = blessCurseManagerApi.getBlessCurseInBag()
+ if triggeringButton == "inactiveAction" 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
diff --git a/src/playermat/Playermat.ttslua b/src/playermat/Playermat.ttslua
index 9f221632..cecce54b 100644
--- a/src/playermat/Playermat.ttslua
+++ b/src/playermat/Playermat.ttslua
@@ -313,7 +313,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 {}
diff --git a/xml/playercards/KohakuNarukami.xml b/xml/playercards/KohakuNarukami.xml
new file mode 100644
index 00000000..277dfcd8
--- /dev/null
+++ b/xml/playercards/KohakuNarukami.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+