diff --git a/objects/AllPlayerCards.15bb07/StellaClark.00e18e.gmnotes b/objects/AllPlayerCards.15bb07/StellaClark.00e18e.gmnotes
index 54a5e010..ede7ce7d 100644
--- a/objects/AllPlayerCards.15bb07/StellaClark.00e18e.gmnotes
+++ b/objects/AllPlayerCards.15bb07/StellaClark.00e18e.gmnotes
@@ -8,7 +8,6 @@
"combatIcons": 3,
"agilityIcons": 4,
"cycle": "Investigator Packs",
- "extraToken": "Survivor",
"signatures": [
{
"60502": 3,
diff --git a/objects/AllPlayerCards.15bb07/StellaClark.00e18e.json b/objects/AllPlayerCards.15bb07/StellaClark.00e18e.json
index 782945dc..80d677fe 100644
--- a/objects/AllPlayerCards.15bb07/StellaClark.00e18e.json
+++ b/objects/AllPlayerCards.15bb07/StellaClark.00e18e.json
@@ -33,7 +33,7 @@
"IgnoreFoW": false,
"LayoutGroupSortIndex": 0,
"Locked": false,
- "LuaScript": "",
+ "LuaScript": "require(\"playercards/cards/StellaClark\")",
"LuaScriptState": "",
"MeasureMovement": false,
"Name": "Card",
@@ -58,5 +58,5 @@
"scaleZ": 1.15
},
"Value": 0,
- "XmlUI": ""
+ "XmlUI": "\u003cInclude src=\"playercards/StellaClark.xml\"/\u003e"
}
diff --git a/src/playercards/cards/StellaClark.ttslua b/src/playercards/cards/StellaClark.ttslua
new file mode 100644
index 00000000..b6692bc4
--- /dev/null
+++ b/src/playercards/cards/StellaClark.ttslua
@@ -0,0 +1,118 @@
+require("playercards/CardsWithHelper")
+local guidReferenceApi = require("core/GUIDReferenceApi")
+local playermatApi = require("playermat/PlayermatApi")
+local searchLib = require("util/SearchLib")
+local tokenManagerApi = require("core/token/TokenManagerApi")
+
+-- intentionally global
+hasXML = true
+isHelperEnabled = false
+local updated
+local state
+
+local xmlData = {
+ Action = { color = "#6D202CE6", onClick = "addExtraAction" },
+ ElderSign = { color = "#50A8CEE6", onClick = "elderSignAbility" }
+}
+
+function updateSave()
+ self.script_state = JSON.encode({
+ isHelperEnabled = isHelperEnabled,
+ state = state
+ })
+end
+
+function onLoad(savedData)
+ self.addTag("DoInUpkeep")
+ if savedData and savedData ~= "" then
+ local loadedData = JSON.decode(savedData)
+ isHelperEnabled = loadedData.isHelperEnabled
+ state = loadedData.state
+ else
+ state = { Action = true, ElderSign = true }
+ end
+ syncDisplayWithOptionPanel()
+end
+
+function initialize()
+ setUiState(state)
+ updated = true
+end
+
+function addExtraAction()
+ if not updated then return end
+ updated = false
+
+ 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
+
+ local callbackName = "updateUniversalActionAbilityToken"
+ local callbackParams = { class = "Survivor", symbol = "Survivor", addTag = "Temporary"}
+ tokenManagerApi.spawnToken(emptyPos + Vector(0, 0.7, 0), "universalActionAbility", rotation, callbackName, callbackParams)
+ state = { Action = false, ElderSign = true }
+ setUiState(state)
+ updated = true
+ updateSave()
+end
+
+function elderSignAbility()
+ local matColor = playermatApi.getMatColorByPosition(self.getPosition())
+ playermatApi.updateCounter(matColor, "DamageCounter", _, -1)
+ playermatApi.updateCounter(matColor, "HorrorCounter", _, -1)
+
+ -- check if reaction ability has already been used
+ local actionColor = self.UI.getAttribute("Action", "color")
+ if actionColor ~= "#353535E6" then
+ addExtraAction()
+ broadcastToAll("Healing 1 damage and 1 horror from Stella and triggering her reaction.", "White")
+ else
+ broadcastToAll("Healing 1 damage and 1 horror from Stella.", "White")
+ end
+end
+
+function setUiState(params)
+ for buttonId, onState in pairs(params) do
+ if onState 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)
+ if triggeringButton == "Action" then
+ broadcastToAll("You have already triggered this once this round.", "Red")
+ end
+end
+
+function doInUpkeep()
+ state = { Action = true, ElderSign = true }
+ setUiState(state)
+ updated = true
+ updateSave()
+end
\ No newline at end of file
diff --git a/src/playermat/Playermat.ttslua b/src/playermat/Playermat.ttslua
index 67abe0a2..622a1456 100644
--- a/src/playermat/Playermat.ttslua
+++ b/src/playermat/Playermat.ttslua
@@ -345,8 +345,17 @@ function doUpkeep(_, clickedByColor, isRightClick)
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
+ elseif obj.type == "Card" then
local cardMetadata = JSON.decode(obj.getGMNotes()) or {}
+
+ if obj.hasTag("DoInUpkeep") then
+ obj.call("doInUpkeep")
+ end
+ -- do not rotate, replenish, etc. on cards in investigator card area
+ if inArea(self.positionToLocal(obj.getPosition()), INVESTIGATOR_AREA) then
+ return
+ end
+
if not (obj.getVar("do_not_ready") or obj.hasTag("DoNotReady")) then
local cardRotation = obj.getRotation()
local yRotDiff = round(cardRotation.y, 0) - rot.y
diff --git a/xml/playercards/StellaClark.xml b/xml/playercards/StellaClark.xml
new file mode 100644
index 00000000..68f3e43d
--- /dev/null
+++ b/xml/playercards/StellaClark.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+