diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..7b0c0d0
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,39 @@
+{
+ "manifestVersion": 1,
+ "name": "Daggerheart Dice Roller",
+ "summary": "A Symbiote to roll duality dice with advantage or disadvantage",
+ "entryPoint": "/roll.html",
+ "descriptionFilePath": "/readme.md",
+ "version": "0.1",
+"about": {
+ "website": "https://example.com",
+
+
+ "authors": [
+
+
+ "Jane Doe",
+
+
+ "John Doe"
+ ]
+ }, "license": "GPL-3.0",
+ "api": {
+ "version": "0.1",
+ "subscriptions": {
+ "dice": {
+ "onRollResults": "handleRollResult"
+ }
+ }
+ },
+ "environment": {
+ "webViewBackgroundColor": "#000000",
+ "capabilities": [
+ "runInBackground"
+ ],
+ "extras": [
+ "fonts",
+ "colorStyles"
+ ]
+ }
+}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..bb5172d
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,9 @@
+This is a very simple Duality Dice Roller for Daggerheart
+
+Just add your modifiers (if any, can be positive or negative),
+and if it applies select Advantage/Disatvantage.
+
+As soon as you click the "Roll Duality Dice" Button the roll gets added to the tray,
+from which you can roll the dice like any other roll.
+
+The result then gets shown int the chat with colored formatting.
\ No newline at end of file
diff --git a/roll.css b/roll.css
new file mode 100644
index 0000000..1dd6155
--- /dev/null
+++ b/roll.css
@@ -0,0 +1,86 @@
+/* roll.css */
+
+body {
+ margin: 0;
+ padding: 2rem;
+ background-color: #121212;
+ color: #e7c74b;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+}
+
+h1 {
+ font-size: 2rem;
+ margin-bottom: 2rem;
+ color: #e7c74b;
+ text-align: center;
+}
+
+.content-row {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ margin-bottom: 1.5rem;
+ max-width: 300px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.field-title {
+ font-size: 1rem;
+ color: #e7c74b;
+}
+
+input[type="number"],
+select {
+ font-size: 1.25rem;
+ padding: 0.75rem 1rem;
+ border-radius: 8px;
+ border: 1px solid #333;
+ background-color: #1e1e1e;
+ color: #ffffff;
+ outline: none;
+ transition: border-color 0.3s;
+}
+
+input[type="number"]:focus,
+select:focus {
+ border-color: #5dade2;
+}
+
+button {
+ padding: 0.75rem 1.5rem;
+ font-size: 1.25rem;
+ background-color: #5dade2;
+ color: #121212;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background-color 0.3s;
+ align-self: center;
+}
+
+button:hover {
+ background-color: #3498db;
+}
+
+.spacer {
+ height: 1rem;
+}
+
+/* Prevent scrollbar on dropdown */
+select {
+ appearance: none;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ background-image: url('data:image/svg+xml;utf8,');
+ background-repeat: no-repeat;
+ background-position: right 0.75rem center;
+ background-size: 1rem;
+ padding-right: 2rem;
+}
+
+/* Optional: hide scrollbar in Firefox */
+select:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #ffffff;
+}
diff --git a/roll.html b/roll.html
new file mode 100644
index 0000000..c24ce5f
--- /dev/null
+++ b/roll.html
@@ -0,0 +1,28 @@
+
+
+
+ Daggerheart Dice Roller
+
+
+
+
+
+ Duality Dice Roll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/roll.js b/roll.js
new file mode 100644
index 0000000..973c42a
--- /dev/null
+++ b/roll.js
@@ -0,0 +1,116 @@
+let trackedIds = {};
+let isGM = false;
+let me;
+
+function roll(type) {
+ let name = document.getElementById("roll-name").value || "Check";
+ let dice = document.getElementById("roll-content").value || "1d20";
+ let typeStr = type == "advantage" ? " (Adv)" : " (Disadv)";
+ TS.dice.putDiceInTray([{ name: name + typeStr, roll: dice }, { name: name + typeStr, roll: dice }], true).then((diceSetResponse) => {
+ trackedIds[diceSetResponse] = type;
+ });
+}
+
+async function rollDualityDice() {
+ const modifier = parseInt(document.getElementById("modifier").value) || 0;
+ const advDisadv = document.getElementById("adv-disadv").value;
+
+ // Define the dice groups
+ const diceGroups = [
+ { name: "Hope", roll: "1d12", color: "yellow" },
+ { name: "Fear", roll: "1d12", color: "purple" }
+ ];
+
+ // Add advantage/disadvantage if applicable
+ if (advDisadv === "advantage") {
+ diceGroups.push({ name: "Advantage", roll: "1d6" });
+ } else if (advDisadv === "disadvantage") {
+ diceGroups.push({ name: "Disadvantage", roll: "1d6" });
+ }
+
+ // Roll the dice
+ const diceSetResponse = await TS.dice.putDiceInTray(diceGroups, true);
+ trackedIds[diceSetResponse] = { modifier, advDisadv };
+}
+
+async function handleRollResult(rollEvent) {
+ if (!trackedIds[rollEvent.payload.rollId]) {
+ return;
+ }
+
+ const trackedData = trackedIds[rollEvent.payload.rollId];
+ const { modifier, advDisadv } = trackedData;
+
+ if (rollEvent.kind === "rollResults") {
+ const roll = rollEvent.payload;
+ let hopeResult = 0;
+ let fearResult = 0;
+ let advDisadvResult = 0;
+
+ for (const group of roll.resultsGroups) {
+ const groupSum = await TS.dice.evaluateDiceResultsGroup(group);
+
+ if (group.name === "Hope") {
+ hopeResult = groupSum;
+ } else if (group.name === "Fear") {
+ fearResult = groupSum;
+ } else if (group.name === "Advantage" || group.name === "Disadvantage") {
+ advDisadvResult = groupSum;
+ }
+ }
+
+ // Calculate the final result
+ let totalResult = hopeResult + fearResult + modifier;
+ if (advDisadv === "advantage") {
+ totalResult += advDisadvResult;
+ } else if (advDisadv === "disadvantage") {
+ totalResult -= advDisadvResult;
+ }
+
+ // Determine the outcome
+ let outcome = "";
+ if (hopeResult === fearResult) {
+ outcome = "Critical Success!";
+ } else if (hopeResult > fearResult) {
+ outcome = "With Hope";
+ } else {
+ outcome = "With Fear";
+ }
+
+ // Display the result in the in-game chat message
+ let outcomeColor = "#FFFFFF";
+ if (outcome === "Critical Success!") {
+ outcomeColor = "#00FF00"; // Green
+ } else if (outcome === "With Hope") {
+ outcomeColor = "#4FC3F7"; // Light Blue
+ } else if (outcome === "With Fear") {
+ outcomeColor = "#E57373"; // Red
+ }
+
+ const chatMessage = {
+ content:
+ `Roll Result: ` +
+ `Hope ${hopeResult} + ` +
+ `Fear ${fearResult}` +
+ `${modifier !== 0 ? ` + Modifier ${modifier}` : ""}` +
+ `${advDisadvResult !== 0 ? ` + ${advDisadv === "advantage" ? "Adv" : "Dis"} ${advDisadvResult}` : ""} = ` +
+ `
${totalResult} ` +
+ ` ${outcome}`,
+ rollId: roll.rollId,
+ expanded: true
+ };
+
+ TS.chat.send(chatMessage.content, "board").catch((response) => console.error("error in sending chat message", response));
+
+ // Display the result in the console
+ console.log(`Total Result: ${totalResult} (${outcome})`);
+
+ displayResult(roll.resultsGroups, roll.rollId);
+ } else if (rollEvent.kind === "rollRemoved") {
+ delete trackedIds[rollEvent.payload.rollId];
+ }
+}
+
+async function displayResult(resultGroup, rollId) {
+ TS.dice.sendDiceResult(resultGroup, rollId).catch((response) => console.error("error in sending dice result", response));
+}
\ No newline at end of file