Created
February 6, 2026 06:29
-
-
Save MathewDominic/66c4f4ce10cfaed943178c8fe664584f to your computer and use it in GitHub Desktop.
A companion tracker for playing Scoundrel (single player card game). It helps you win by tracking which cards are still in the dungeon.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <title>Scoundrel Tracker</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| background: #1e1e2f; | |
| color: white; | |
| text-align: center; | |
| margin: 0; | |
| } | |
| h1 { margin-top: 20px; } | |
| h2 { margin-top: 30px; } | |
| .section { margin: 30px 0; } | |
| .cards { | |
| display: flex; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| gap: 8px; | |
| margin-top: 10px; | |
| } | |
| .row { | |
| display: flex; | |
| justify-content: center; | |
| gap: 8px; | |
| margin-top: 8px; | |
| } | |
| .card { | |
| width: 50px; | |
| height: 70px; | |
| border-radius: 6px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: bold; | |
| cursor: pointer; | |
| user-select: none; | |
| transition: 0.2s; | |
| } | |
| .weapon { background: #555; } | |
| .potion { background: #a33; } | |
| .monster { background: #333; } | |
| .used { | |
| opacity: 0.3; | |
| text-decoration: line-through; | |
| } | |
| .counter { | |
| margin-top: 8px; | |
| font-size: 14px; | |
| } | |
| #totalCards { | |
| margin-top: 10px; | |
| font-size: 18px; | |
| font-weight: bold; | |
| } | |
| button { | |
| padding: 6px 12px; | |
| margin-top: 20px; | |
| cursor: pointer; | |
| } | |
| /* Help Button */ | |
| #helpBtn { | |
| position: absolute; | |
| top: 15px; | |
| right: 15px; | |
| width: 35px; | |
| height: 35px; | |
| border-radius: 50%; | |
| background: #444; | |
| color: white; | |
| font-weight: bold; | |
| border: none; | |
| cursor: pointer; | |
| } | |
| /* Modal */ | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| inset: 0; | |
| background: rgba(0,0,0,0.6); | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| .modal-content { | |
| background: #2b2b3d; | |
| padding: 20px; | |
| width: 300px; | |
| border-radius: 8px; | |
| text-align: left; | |
| } | |
| .close-btn { | |
| margin-top: 15px; | |
| width: 100%; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <button id="helpBtn">?</button> | |
| <h1>Scoundrel Tracker</h1> | |
| <div id="totalCards"></div> | |
| <div id="app"></div> | |
| <button onclick="resetAll()">Reset All</button> | |
| <!-- Modal --> | |
| <!-- Modal --> | |
| <div class="modal" id="helpModal"> | |
| <div class="modal-content"> | |
| <p> | |
| This is a companion tracker for playing <b>Scoundrel</b>. | |
| It helps you track what cards are still in the dungeon. | |
| <br><br> | |
| <b>How to use while playing:</b><br><br> | |
| • At the start of the game, all cards are available.<br><br> | |
| • Whenever a monster, weapon, or potion is permanently discarded, | |
| tap that card here to mark it as used.<br><br> | |
| • Use the Remaining Strength to understand how dangerous | |
| the dungeon still is.<br><br> | |
| • Use Total Cards Remaining to estimate deck size | |
| and future draw probability. | |
| <br><br> | |
| New to Scoundrel? | |
| <br> | |
| <a href="https://www.youtube.com/watch?v=Gt2tYzM93h4" target="_blank" style="color:#66b3ff;"> | |
| Watch how to play | |
| </a> | |
| </p> | |
| <button class="close-btn" onclick="closeHelp()">Close</button> | |
| </div> | |
| </div> | |
| <script> | |
| const app = document.getElementById("app"); | |
| const totalCardsEl = document.getElementById("totalCards"); | |
| const helpBtn = document.getElementById("helpBtn"); | |
| const helpModal = document.getElementById("helpModal"); | |
| helpBtn.onclick = () => helpModal.style.display = "flex"; | |
| helpModal.onclick = (e) => { | |
| if (e.target === helpModal) closeHelp(); | |
| }; | |
| function closeHelp() { | |
| helpModal.style.display = "none"; | |
| } | |
| const sections = [ | |
| { | |
| title: "Weapons (2–10)", | |
| type: "weapon", | |
| values: [2,3,4,5,6,7,8,9,10] | |
| }, | |
| { | |
| title: "Potions (2–10)", | |
| type: "potion", | |
| values: [2,3,4,5,6,7,8,9,10] | |
| }, | |
| { | |
| title: "Monsters (2–14 ×2)", | |
| type: "monster", | |
| values: [ | |
| [2,3,4,5,6,7,8,9,10,11,12,13,14], | |
| [2,3,4,5,6,7,8,9,10,11,12,13,14] | |
| ] | |
| } | |
| ]; | |
| function render() { | |
| app.innerHTML = ""; | |
| sections.forEach((section, index) => { | |
| const wrapper = document.createElement("div"); | |
| wrapper.className = "section"; | |
| const title = document.createElement("h2"); | |
| title.innerText = section.title; | |
| wrapper.appendChild(title); | |
| if (Array.isArray(section.values[0])) { | |
| section.values.forEach(rowValues => { | |
| const rowDiv = document.createElement("div"); | |
| rowDiv.className = "row"; | |
| rowValues.forEach(val => { | |
| rowDiv.appendChild(createCard(val, section.type)); | |
| }); | |
| wrapper.appendChild(rowDiv); | |
| }); | |
| } else { | |
| const cardsDiv = document.createElement("div"); | |
| cardsDiv.className = "cards"; | |
| section.values.forEach(val => { | |
| cardsDiv.appendChild(createCard(val, section.type)); | |
| }); | |
| wrapper.appendChild(cardsDiv); | |
| } | |
| const counter = document.createElement("div"); | |
| counter.className = "counter"; | |
| counter.id = "counter-" + index; | |
| wrapper.appendChild(counter); | |
| app.appendChild(wrapper); | |
| }); | |
| updateStats(); | |
| } | |
| function createCard(val, type) { | |
| const card = document.createElement("div"); | |
| card.className = `card ${type}`; | |
| card.innerText = val; | |
| card.dataset.value = val; | |
| card.onclick = () => { | |
| card.classList.toggle("used"); | |
| updateStats(); | |
| }; | |
| return card; | |
| } | |
| function updateStats() { | |
| let totalCardsLeft = 0; | |
| document.querySelectorAll(".section").forEach((sectionEl, index) => { | |
| let sectionStrength = 0; | |
| let sectionCardsLeft = 0; | |
| sectionEl.querySelectorAll(".card").forEach(card => { | |
| if (!card.classList.contains("used")) { | |
| sectionStrength += parseInt(card.dataset.value); | |
| sectionCardsLeft++; | |
| } | |
| }); | |
| totalCardsLeft += sectionCardsLeft; | |
| document.getElementById("counter-" + index).innerText = | |
| `Remaining Strength: ${sectionStrength} | Cards Left: ${sectionCardsLeft}`; | |
| }); | |
| totalCardsEl.innerText = | |
| `Total Cards Remaining: ${totalCardsLeft} / 44`; | |
| } | |
| function resetAll() { | |
| document.querySelectorAll(".card").forEach(card => { | |
| card.classList.remove("used"); | |
| }); | |
| updateStats(); | |
| } | |
| render(); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment