Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>๐ฒ D20 Dungeon Crawler ๐ฐ</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| min-height: 100vh; | |
| margin: 0; | |
| background-color: #2c3e50; | |
| color: #ecf0f1; | |
| } | |
| #game-container { | |
| background-color: #34495e; | |
| padding: 20px; | |
| border-radius: 10px; | |
| box-shadow: 0 0 20px rgba(0,0,0,0.3); | |
| max-width: 800px; | |
| width: 100%; | |
| } | |
| button { | |
| margin: 5px; | |
| padding: 10px 15px; | |
| background-color: #3498db; | |
| color: white; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| transition: background-color 0.3s; | |
| } | |
| button:hover { | |
| background-color: #2980b9; | |
| } | |
| select { | |
| margin: 5px; | |
| padding: 5px 10px; | |
| } | |
| #dungeon-image { | |
| width: 100%; | |
| height: 200px; | |
| background-color: #2c3e50; | |
| margin-bottom: 20px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| font-size: 24px; | |
| text-align: center; | |
| } | |
| #choices { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 10px; | |
| } | |
| #combat-log { | |
| max-height: 200px; | |
| overflow-y: auto; | |
| background-color: #2c3e50; | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin-top: 20px; | |
| } | |
| #monster-trophies { | |
| display: flex; | |
| flex-wrap: wrap; | |
| margin-top: 10px; | |
| } | |
| .monster-trophy { | |
| font-size: 24px; | |
| margin-right: 5px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="game-container"> | |
| <h1>๐ฒ D20 Dungeon Crawler ๐ฐ</h1> | |
| <div id="character-creation"> | |
| <h2>๐ค Character Creation</h2> | |
| <select id="race"> | |
| <option value="human">๐จ Human</option> | |
| <option value="elf">๐งโโ๏ธ Elf</option> | |
| <option value="dwarf">๐ง Dwarf</option> | |
| <option value="halfling">๐ง Halfling</option> | |
| </select> | |
| <select id="class"> | |
| <option value="warrior">โ๏ธ Warrior</option> | |
| <option value="mage">๐งโโ๏ธ Mage</option> | |
| <option value="rogue">๐ก๏ธ Rogue</option> | |
| <option value="cleric">โจ Cleric</option> | |
| </select> | |
| <button onclick="createCharacter()">๐ญ Create Character</button> | |
| </div> | |
| <div id="game-area" style="display: none;"> | |
| <div id="dungeon-image"></div> | |
| <div id="encounter-description"></div> | |
| <div id="choices"></div> | |
| <div id="player-stats"></div> | |
| <div id="combat-log"></div> | |
| <div id="monster-trophies"></div> | |
| </div> | |
| </div> | |
| <svg id="d20" width="100" height="100" viewBox="0 0 100 100" style="display: none;"> | |
| <polygon points="50,5 95,75 5,75" fill="#f3f3f3" stroke="#000" stroke-width="2"/> | |
| <text x="50" y="55" font-size="24" text-anchor="middle" id="roll-result"></text> | |
| </svg> | |
| <script> | |
| let player, currentEncounter, score = 0, monsterTrophies = []; | |
| const settingsData = ` | |
| ๐๏ธ๐ฎ,Ancient Temple,A crumbling temple with flickering torches and moss-covered statues | |
| ๐ณ๐,Enchanted Forest,A mystical forest filled with glowing mushrooms and whispering trees | |
| ๐๏ธโ๏ธ,Frozen Peaks,Treacherous icy mountains with howling winds and hidden crevasses | |
| ๐๏ธ๐ซ,Desert Ruins,Sun-baked ruins half-buried in shifting sands | |
| ๐๐ฅ,Volcanic Cavern,A scorching cave system with rivers of lava and sulfurous air | |
| ๐๐,Sunken City,The remains of an ancient city submerged beneath crystal-clear waters | |
| ๐ฏ๏ธ๐,Arcane Library,Towering bookshelves filled with magical tomes and floating candles | |
| โฐ๏ธ๐,Haunted Crypt,A chilling burial chamber echoing with unseen whispers | |
| ๐ฟ๐ฆ,Fairy Glade,A serene clearing shimmering with magical butterflies and flower petals | |
| ๐ฐ๏ธโ๏ธ,Clockwork Maze,An intricate labyrinth of gears, pistons, and ticking mechanisms | |
| ๐๐บ,Shadowy Woods,Dark and foreboding forest where eyes seem to watch from every shadow | |
| ๐ฎ๐ญ,Illusory Palace,A grand castle where nothing is as it seems, constantly shifting | |
| ๐ณ๏ธ๐ฆ,Abyssal Chasm,A deep, dark pit with strange echoes and unseen terrors | |
| ๐๐ฆ,Crystal Cavern,A dazzling cave system filled with luminous, multi-colored crystals | |
| ๐ช๐ญ,Carnival of Souls,An eerie, abandoned fairground with a sinister undercurrent | |
| ๐ฟ๐ด,Primal Jungle,A dense, primordial jungle teeming with ancient creatures and ruins | |
| ๐๐,Astral Plane,A surreal realm of floating islands and swirling cosmic energies | |
| ๐ดโโ ๏ธ๐ฆ,Ghost Ship,A decrepit vessel sailing through mists, crewed by spectral pirates | |
| ๐๐ถ,Harmonic Sanctuary,A tranquil temple where magical music fills the air | |
| ๐ญ๐พ,Alien Hive,A bizarre, organic structure pulsing with otherworldly life | |
| `; | |
| const monstersData = ` | |
| ๐น๐ก๏ธ,Goblin,A small, green-skinned creature with a wicked grin and rusty dagger | |
| ๐๐น,Skeleton Archer,A reanimated skeleton wielding a creaky bow with surprising accuracy | |
| ๐บ๐,Dire Wolf,A massive wolf with glowing eyes and razor-sharp fangs | |
| ๐ท๏ธ๐ธ๏ธ,Giant Spider,A hairy arachnid the size of a horse, with venom dripping from its fangs | |
| ๐งโโ๏ธ๐,Zombie,A shambling corpse with rotting flesh, driven by an insatiable hunger | |
| ๐งโโ๏ธ๐ฎ,Evil Witch,A cackling hag with gnarled fingers and a bubbling cauldron of curses | |
| ๐๐ฅ,Young Dragon,A scaled wyrm with smoke curling from its nostrils, hungry for treasure | |
| ๐ฆ๐งโโ๏ธ,Vampire,A pale, aristocratic figure with razor-sharp fangs and hypnotic eyes | |
| ๐ฟ๐,Treant,An ancient tree come to life, with bark-covered limbs and mossy beard | |
| ๐ฆ๐ฑ,Lizardfolk Warrior,A scaly humanoid with sharp claws and primitive weapons | |
| ๐ป๐,Poltergeist,An invisible spirit that delights in chaos and flying objects | |
| ๐๐,Kraken Spawn,A mass of writhing tentacles emerging from dark waters | |
| ๐ฆ ๐ฆ,Griffin,A majestic creature with the head and wings of an eagle and body of a lion | |
| ๐ฟ๐จ,Stone Golem,A hulking figure carved from living rock, nearly impervious to harm | |
| ๐ฆ๐,Corrupted Unicorn,Once pure, now twisted by dark magic with a deadly horn | |
| ๐งโโ๏ธ๐ถ,Siren,A hauntingly beautiful creature with an alluring, deadly song | |
| ๐ฒโ๏ธ,Cloud Giant,A towering humanoid wreathed in mists, with storm-powered magic | |
| ๐ฆ๐ฅ,Infernal Scorpion,A massive scorpion with a carapace of smoldering coals | |
| ๐ง ๐๏ธ,Mind Flayer,A tentacle-faced horror with powerful psionic abilities | |
| ๐๐บ,Shadow Wolf,A beast made of living darkness with glowing red eyes | |
| `; | |
| const settings = parseCSV(settingsData); | |
| const monsters = parseCSV(monstersData); | |
| function parseCSV(data) { | |
| return data.trim().split('\n').map(line => { | |
| const [emoji, name, description] = line.split(','); | |
| return { emoji, name, description }; | |
| }); | |
| } | |
| function createCharacter() { | |
| const race = document.getElementById('race').value; | |
| const characterClass = document.getElementById('class').value; | |
| player = { | |
| race, | |
| class: characterClass, | |
| maxHp: 50, | |
| hp: 50, | |
| attack: 10, | |
| defense: 5, | |
| magic: 8 | |
| }; | |
| document.getElementById('character-creation').style.display = 'none'; | |
| document.getElementById('game-area').style.display = 'block'; | |
| updateStats(); | |
| nextEncounter(); | |
| } | |
| function updateStats() { | |
| document.getElementById('player-stats').innerHTML = ` | |
| ๐ค Player: โค๏ธ HP ${player.hp}/${player.maxHp}, โ๏ธ ATK ${player.attack}, ๐ก๏ธ DEF ${player.defense}, ๐ฎ MAG ${player.magic} | |
| <br>๐ Score: ${score} | |
| `; | |
| } | |
| function nextEncounter() { | |
| const setting = settings[Math.floor(Math.random() * settings.length)]; | |
| const monster = monsters[Math.floor(Math.random() * monsters.length)]; | |
| currentEncounter = { | |
| setting, | |
| monster, | |
| monsterHp: 30, | |
| description: `${setting.emoji} You find yourself in ${setting.name}. ${setting.description} | |
| <br><br>${monster.emoji} Suddenly, a ${monster.name} appears! ${monster.description}`, | |
| choices: generateChoices() | |
| }; | |
| document.getElementById('dungeon-image').innerHTML = setting.emoji; | |
| document.getElementById('encounter-description').innerHTML = currentEncounter.description; | |
| displayChoices(); | |
| } | |
| function generateChoices() { | |
| return [ | |
| { text: "โ๏ธ Attack with your weapon", successRate: 0.7, statUsed: "attack" }, | |
| { text: "๐ฎ Cast a spell", successRate: 0.6, statUsed: "magic" }, | |
| { text: "๐ต๏ธ Try to sneak past", successRate: 0.5, statUsed: "defense" }, | |
| { text: "๐ฃ๏ธ Attempt to communicate", successRate: 0.3, statUsed: "magic" } | |
| ]; | |
| } | |
| function displayChoices() { | |
| const choicesDiv = document.getElementById('choices'); | |
| choicesDiv.innerHTML = ""; | |
| currentEncounter.choices.forEach((choice, index) => { | |
| const button = document.createElement('button'); | |
| button.textContent = `${String.fromCharCode(65 + index)}. ${choice.text}`; | |
| button.onclick = () => makeChoice(index); | |
| choicesDiv.appendChild(button); | |
| }); | |
| } | |
| async function makeChoice(index) { | |
| const choice = currentEncounter.choices[index]; | |
| const roll = await rollD20(); | |
| const success = roll / 20 <= choice.successRate; | |
| const statBonus = player[choice.statUsed] / 10; | |
| const totalSuccessRate = Math.min(choice.successRate + statBonus, 1); | |
| if (success) { | |
| const damage = Math.floor(roll * totalSuccessRate); | |
| currentEncounter.monsterHp -= damage; | |
| score += damage; | |
| log(`โ Success! ${choice.text} (๐ฒ Roll: ${roll}, ๐ Success Rate: ${(totalSuccessRate * 100).toFixed(1)}%, ๐ฅ Damage: ${damage}, ๐ Score: +${damage})`); | |
| } else { | |
| player.hp -= 5; | |
| log(`โ Failure... ${choice.text} (๐ฒ Roll: ${roll}, ๐ Success Rate: ${(totalSuccessRate * 100).toFixed(1)}%, โค๏ธ HP: -5)`); | |
| } | |
| updateStats(); | |
| if (player.hp <= 0) { | |
| log("๐ Game over! You have been defeated."); | |
| return; | |
| } | |
| if (currentEncounter.monsterHp <= 0) { | |
| log(`๐ Victory! You defeated the ${currentEncounter.monster.name}!`); | |
| monsterTrophies.push(currentEncounter.monster.emoji); | |
| updateMonsterTrophies(); | |
| player.hp = player.maxHp; // Heal to full after battle | |
| updateStats(); | |
| nextEncounter(); | |
| } else { | |
| log(`The ${currentEncounter.monster.name} has ${currentEncounter.monsterHp} HP remaining.`); | |
| } | |
| } | |
| function updateMonsterTrophies() { | |
| const trophiesDiv = document.getElementById('monster-trophies'); | |
| trophiesDiv.innerHTML = monsterTrophies.map(emoji => `<span class="monster-trophy">${emoji}</span>`).join(''); | |
| } | |
| function rollD20() { | |
| return new Promise(resolve => { | |
| const d20 = document.getElementById('d20'); | |
| d20.style.display = 'block'; | |
| const result = Math.floor(Math.random() * 20) + 1; | |
| document.getElementById('roll-result').textContent = result; | |
| d20.animate([ | |
| { transform: 'rotate(0deg)' }, | |
| { transform: 'rotate(360deg)' } | |
| ], { | |
| duration: 1000, | |
| iterations: 1 | |
| }).onfinish = () => { | |
| d20.style.display = 'none'; | |
| resolve(result); | |
| }; | |
| }); | |
| } | |
| function log(message) { | |
| const combatLog = document.getElementById('combat-log'); | |
| combatLog.innerHTML += `<p>${message}</p>`; | |
| combatLog.scrollTop = combatLog.scrollHeight; | |
| } | |
| </script> | |
| </body> | |
| </html> |