File size: 13,139 Bytes
1f5d9a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<!DOCTYPE html>
<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>