Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Tiny Healer - RPG Healer Combat Game</title> | |
| <style> | |
| /* Basic body styling */ | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100vh; | |
| margin: 0; | |
| background: linear-gradient(to bottom right, #1e3c72, #2a5298); | |
| color: #ffd100; | |
| } | |
| /* Main game container */ | |
| #game-container { | |
| width: 800px; | |
| height: 600px; | |
| border: none; | |
| padding: 20px; | |
| background: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), rgba(50, 50, 50, 0.9)); | |
| position: relative; | |
| box-shadow: 0 0 30px rgba(0, 0, 0, 0.7); | |
| border-radius: 15px; | |
| display: none; /* Hidden by default, shown after Start is clicked */ | |
| } | |
| /* Welcome screen */ | |
| #welcome-screen { | |
| width: 800px; | |
| height: 600px; | |
| border: none; | |
| padding: 20px; | |
| background: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), rgba(50, 50, 50, 0.9)); | |
| position: relative; | |
| box-shadow: 0 0 30px rgba(0, 0, 0, 0.7); | |
| border-radius: 15px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| } | |
| #welcome-screen h1 { | |
| font-size: 48px; | |
| margin-bottom: 20px; | |
| } | |
| #start-button { | |
| font-size: 24px; | |
| padding: 10px 20px; | |
| background: linear-gradient(to bottom, #ffd100, #ffb800); | |
| color: #000; | |
| border: none; | |
| cursor: pointer; | |
| border-radius: 10px; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 0 15px rgba(0, 0, 0, 0.7); | |
| } | |
| #start-button:hover { | |
| background: linear-gradient(to bottom, #ffea00, #ffdd00); | |
| } | |
| /* Combat window containing allies and boss */ | |
| #combat-window { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 20px; | |
| } | |
| /* Allies section */ | |
| #allies { | |
| width: 45%; | |
| } | |
| /* Boss section */ | |
| #boss { | |
| width: 45%; | |
| } | |
| /* Health bar styling */ | |
| .health-bar { | |
| height: 30px; | |
| background: linear-gradient(to right, #232526, #414345); | |
| margin-bottom: 10px; | |
| position: relative; | |
| cursor: pointer; | |
| border-radius: 10px; | |
| overflow: hidden; | |
| box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.7); | |
| } | |
| .health-bar::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| height: 100%; | |
| width: 100%; | |
| background: linear-gradient(to right, #32cd32, #228b22); | |
| transition: width 0.3s ease; | |
| border-radius: 10px; | |
| } | |
| /* Selected ally health bar */ | |
| .health-bar.selected::before { | |
| background: linear-gradient(to right, #00bfff, #1e90ff); | |
| } | |
| /* Dead ally health bar */ | |
| .health-bar.dead::before { | |
| background: linear-gradient(to right, #696969, #808080); | |
| } | |
| /* Bleeding ally health bar */ | |
| .health-bar.bleeding::before { | |
| background-image: linear-gradient(45deg, #32cd32 25%, #ff4500 25%, #ff4500 50%, #32cd32 50%, #32cd32 75%, #ff4500 75%, #ff4500 100%); | |
| background-size: 50px 50px; | |
| animation: bleed-animation 1s linear infinite; | |
| } | |
| @keyframes bleed-animation { | |
| 0% { | |
| background-position: 0 0; | |
| } | |
| 100% { | |
| background-position: 50px 0; | |
| } | |
| } | |
| /* Health bar text */ | |
| .health-bar-text { | |
| position: absolute; | |
| width: 100%; | |
| text-align: center; | |
| line-height: 30px; | |
| z-index: 1; | |
| text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8); | |
| color: #fff; | |
| } | |
| /* Boss health bar */ | |
| #boss-health-bar { | |
| height: 50px; | |
| } | |
| #boss-health-bar::before { | |
| background: linear-gradient(to right, #ff4500, #b22222); | |
| } | |
| /* Spell buttons container */ | |
| #spell-buttons { | |
| display: flex; | |
| justify-content: space-between; | |
| margin-bottom: 10px; | |
| } | |
| /* Individual spell button */ | |
| .spell-button { | |
| width: 18%; | |
| height: 50px; | |
| background: linear-gradient(to bottom, #4a4a4a, #2e2e2e); | |
| border: 1px solid #ffd100; | |
| color: #ffd100; | |
| text-align: center; | |
| text-decoration: none; | |
| display: inline-block; | |
| font-size: 14px; | |
| margin: 4px 2px; | |
| cursor: pointer; | |
| position: relative; | |
| overflow: hidden; | |
| border-radius: 10px; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); | |
| } | |
| .spell-button:hover { | |
| background: linear-gradient(to bottom, #666, #444); | |
| } | |
| /* Disabled spell button */ | |
| .spell-button:disabled { | |
| background: linear-gradient(to bottom, #333, #111); | |
| border-color: #666; | |
| color: #666; | |
| cursor: not-allowed; | |
| } | |
| /* Cooldown progress bar on spell buttons */ | |
| .cooldown-progress { | |
| position: absolute; | |
| top: 0; | |
| right: 0; | |
| height: 100%; | |
| width: 0; | |
| background-color: rgba(0, 0, 0, 0.5); | |
| transition: width 0.1s linear; | |
| } | |
| /* Mana bar container */ | |
| #mana-bar { | |
| width: 100%; | |
| height: 20px; | |
| background: linear-gradient(to right, #141414, #282828); | |
| position: relative; | |
| border-radius: 10px; | |
| overflow: hidden; | |
| box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.7); | |
| } | |
| /* Mana bar fill */ | |
| #mana-bar-fill { | |
| height: 100%; | |
| width: 100%; | |
| background: linear-gradient(to right, #1e90ff, #4169e1); | |
| transition: width 0.5s; | |
| border-radius: 10px; | |
| } | |
| /* Mana text */ | |
| #mana-text { | |
| position: absolute; | |
| width: 100%; | |
| text-align: center; | |
| line-height: 20px; | |
| color: #fff; | |
| text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8); | |
| } | |
| /* Reset button */ | |
| #reset-button { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| font-size: 24px; | |
| padding: 10px 20px; | |
| background: linear-gradient(to bottom, #ffd100, #ffb800); | |
| color: #000; | |
| border: none; | |
| cursor: pointer; | |
| display: none; | |
| border-radius: 10px; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 0 15px rgba(0, 0, 0, 0.7); | |
| } | |
| #reset-button:hover { | |
| background: linear-gradient(to bottom, #ffea00, #ffdd00); | |
| } | |
| /* Game over and victory messages */ | |
| #game-over, #victory { | |
| position: absolute; | |
| top: 40%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| font-size: 36px; | |
| display: none; | |
| text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.8); | |
| } | |
| #game-over { | |
| color: #ff4500; | |
| } | |
| #victory { | |
| color: #32cd32; | |
| } | |
| /* Accordion styles */ | |
| .accordion { | |
| background: linear-gradient(to right, #333, #444); | |
| color: #ffd100; | |
| cursor: pointer; | |
| padding: 18px; | |
| width: 100%; | |
| text-align: left; | |
| border: none; | |
| outline: none; | |
| transition: 0.4s; | |
| font-size: 16px; | |
| border-radius: 10px; | |
| margin-top: 10px; | |
| box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); | |
| } | |
| .accordion:hover { | |
| background: linear-gradient(to right, #444, #555); | |
| } | |
| .panel { | |
| padding: 0 18px; | |
| background: linear-gradient(to bottom, #1a1a1a, #222); | |
| max-height: 0; | |
| overflow: hidden; | |
| transition: max-height 0.2s ease-out; | |
| border-radius: 0 0 10px 10px; | |
| box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.7); | |
| } | |
| /* Markdown styles */ | |
| .markdown { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| line-height: 1.6; | |
| color: #ccc; | |
| } | |
| .markdown h1, .markdown h2, .markdown h3 { | |
| color: #ffd100; | |
| } | |
| .markdown code { | |
| background-color: #333; | |
| padding: 2px 4px; | |
| border-radius: 4px; | |
| } | |
| .markdown pre { | |
| background-color: #333; | |
| padding: 10px; | |
| border-radius: 4px; | |
| overflow-x: auto; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Welcome screen --> | |
| <div id="welcome-screen"> | |
| <h1>Welcome to Tiny Healer</h1> | |
| <button id="start-button">Start Game</button> | |
| </div> | |
| <!-- Main game container --> | |
| <div id="game-container"> | |
| <!-- Combat window containing allies and boss --> | |
| <div id="combat-window"> | |
| <!-- Allies section --> | |
| <div id="allies"></div> | |
| <!-- Boss section --> | |
| <div id="boss"> | |
| <!-- Boss health bar --> | |
| <div id="boss-health-bar" class="health-bar"> | |
| <div class="health-bar-text">Boss HP: 5000/5000</div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Spell buttons container --> | |
| <div id="spell-buttons"></div> | |
| <!-- Mana bar --> | |
| <div id="mana-bar"> | |
| <div id="mana-bar-fill"></div> | |
| <div id="mana-text">Mana: 100/100</div> | |
| </div> | |
| <!-- Game over message --> | |
| <div id="game-over">GAME OVER</div> | |
| <!-- Victory message --> | |
| <div id="victory">VICTORY!</div> | |
| <!-- Reset button --> | |
| <button id="reset-button">RESET</button> | |
| <!-- Spellbook Accordion --> | |
| <button class="accordion">Spellbook</button> | |
| <div class="panel"> | |
| <div id="spellbook"></div> | |
| </div> | |
| <!-- Info Accordion --> | |
| <button class="accordion">Info</button> | |
| <div class="panel"> | |
| <div id="info" class="markdown"> | |
| <h1>Tiny Healer</h1> | |
| <h2>TL;DR</h2> | |
| <p>Tiny Healer is made with the gameplay loop of a healer in World of Warcraft in mind. Healing in a dungeon minus the game.</p> | |
| <h2>Rules</h2> | |
| <ul> | |
| <li>You are a healer, you cannot die yourself.</li> | |
| <li>Target your allies and cast spells to heal them (keybinds are 1-5).</li> | |
| <li>Allies automatically attack the boss.</li> | |
| <li>Game Over when all allies are dead.</li> | |
| <li>Victory when the boss is dead.</li> | |
| </ul> | |
| <h2>Mechanics</h2> | |
| <ul> | |
| <li>The boss deals damage to a random ally every second.</li> | |
| <li>The boss periodically applies a Bleed to a random ally.</li> | |
| <li>You regenerate Mana passively.</li> | |
| <li>The Global Cooldown is 1.5 sec.</li> | |
| </ul> | |
| <h2>Usage</h2> | |
| <p>It's only a few hundred lines of Pygame! The idea is to have a structure to tweak/tune mechanics and create your own boss encounters.</p> | |
| </div> | |
| </div> | |
| <script> | |
| // Array to store ally objects | |
| const allies = []; | |
| // Boss object | |
| let boss = { hp: 5000, maxHp: 5000 }; | |
| // Currently selected ally | |
| let selectedAlly = null; | |
| // Global cooldown flag | |
| let globalCooldown = false; | |
| // Game active flag | |
| let gameActive = true; | |
| // Player's current mana | |
| let playerMana = 100; | |
| // Maximum mana | |
| const maxMana = 100; | |
| // Boss bleed ability cooldown | |
| let bossBleedCooldown = 0; | |
| // Spell definitions | |
| const spells = [ | |
| { name: "Quick Heal", cooldown: 0, heal: 5, manaCost: 3, description: "A fast, low-cost heal that restores 5 HP." }, | |
| { name: "Strong Heal", cooldown: 15, heal: 30, manaCost: 5, description: "A powerful heal that restores 30 HP, but has a longer cooldown." }, | |
| { name: "Healing Stream", cooldown: 45, heal: 3, duration: 10, interval: 0.5, manaCost: 6, description: "Heals the target for 3 HP every 0.5 seconds for 10 seconds." }, | |
| { name: "Chain Heal", cooldown: 15, heal: 10, targets: 3, manaCost: 10, description: "Heals 3 random allies for 10 HP each." }, | |
| { name: "Team Heal", cooldown: 120, heal: 30, manaCost: 20, description: "Heals all allies for 30 HP, but has a very long cooldown." } | |
| ]; | |
| // Function to create an ally object | |
| function createAlly(id) { | |
| const allyTypes = [ | |
| { type: "Tank", maxHp: 300 }, // First ally is a Tank | |
| { type: "Rogue", maxHp: 100 }, // Second ally is a Rogue | |
| { type: "Rogue", maxHp: 100 }, // Third ally is a Rogue | |
| { type: "Mage", maxHp: 100 }, // Fourth ally is a Mage | |
| { type: "Mage", maxHp: 100 }, // Fifth ally is a Mage | |
| ]; | |
| const ally = allyTypes[id]; | |
| return { id, type: ally.type, hp: ally.maxHp, maxHp: ally.maxHp, alive: true, bleeding: false }; | |
| } | |
| // Function to update health bars | |
| function updateHealthBar(entity, barElement) { | |
| const percentage = (entity.hp / entity.maxHp) * 100; | |
| barElement.style.width = `${percentage}%`; | |
| barElement.querySelector('.health-bar-text').textContent = `${entity.type} HP: ${entity.hp}/${entity.maxHp}`; | |
| if (entity.hp <= 0) { | |
| barElement.classList.add('dead'); | |
| } | |
| if (entity.bleeding) { | |
| barElement.classList.add('bleeding'); | |
| } else { | |
| barElement.classList.remove('bleeding'); | |
| } | |
| } | |
| // Function to update the mana bar | |
| function updateManaBar() { | |
| const manaBarFill = document.getElementById('mana-bar-fill'); | |
| const manaText = document.getElementById('mana-text'); | |
| const percentage = (playerMana / maxMana) * 100; | |
| manaBarFill.style.width = `${percentage}%`; | |
| manaText.textContent = `Mana: ${playerMana}/${maxMana}`; | |
| } | |
| // Function to create ally health bars | |
| function createAllies() { | |
| const alliesContainer = document.getElementById('allies'); | |
| alliesContainer.innerHTML = ''; | |
| allies.length = 0; | |
| for (let i = 0; i < 5; i++) { | |
| const ally = createAlly(i); | |
| allies.push(ally); | |
| const healthBar = document.createElement('div'); | |
| healthBar.className = 'health-bar'; | |
| healthBar.innerHTML = `<div class="health-bar-text">${ally.type} HP: ${ally.hp}/${ally.maxHp}</div>`; | |
| healthBar.onclick = () => selectAlly(i); | |
| alliesContainer.appendChild(healthBar); | |
| updateHealthBar(ally, healthBar); | |
| } | |
| } | |
| // Function to select an ally | |
| function selectAlly(index) { | |
| if (!allies[index].alive) return; | |
| const healthBars = document.querySelectorAll('#allies .health-bar'); | |
| healthBars.forEach(bar => bar.classList.remove('selected')); | |
| healthBars[index].classList.add('selected'); | |
| selectedAlly = allies[index]; | |
| updateSpellButtons(); | |
| } | |
| // Function to create spell buttons | |
| function createSpellButtons() { | |
| const spellButtonsContainer = document.getElementById('spell-buttons'); | |
| spellButtonsContainer.innerHTML = ''; | |
| spells.forEach((spell, index) => { | |
| const button = document.createElement('button'); | |
| button.className = 'spell-button'; | |
| button.innerHTML = `${spell.name}<br>(${spell.manaCost} Mana)<div class="cooldown-progress"></div>`; | |
| button.onclick = () => castSpell(index); | |
| button.disabled = true; | |
| spellButtonsContainer.appendChild(button); | |
| }); | |
| } | |
| // Function to update spell button states | |
| function updateSpellButtons() { | |
| const spellButtons = document.querySelectorAll('.spell-button'); | |
| spellButtons.forEach((button, index) => { | |
| button.disabled = !selectedAlly || !selectedAlly.alive || globalCooldown || button.classList.contains('on-cooldown') || !gameActive || playerMana < spells[index].manaCost; | |
| }); | |
| } | |
| // Function to cast a spell | |
| function castSpell(spellIndex) { | |
| const spell = spells[spellIndex]; | |
| if (selectedAlly && selectedAlly.alive && !globalCooldown && !document.querySelectorAll('.spell-button')[spellIndex].classList.contains('on-cooldown') && gameActive && playerMana >= spell.manaCost) { | |
| playerMana -= spell.manaCost; | |
| updateManaBar(); | |
| switch (spellIndex) { | |
| case 0: | |
| case 1: | |
| healAlly(selectedAlly, spell.heal); | |
| break; | |
| case 2: | |
| startHealingStream(selectedAlly, spell); | |
| break; | |
| case 3: | |
| chainHeal(spell); | |
| break; | |
| case 4: | |
| teamHeal(spell); | |
| break; | |
| } | |
| startSpellCooldown(spellIndex); | |
| startGlobalCooldown(); | |
| } | |
| } | |
| // Function to heal an ally | |
| function healAlly(ally, amount) { | |
| if (ally.alive) { | |
| ally.hp = Math.min(ally.hp + amount, ally.maxHp); | |
| updateHealthBar(ally, document.querySelectorAll('.health-bar')[ally.id]); | |
| } | |
| } | |
| // Function to start a healing stream | |
| function startHealingStream(ally, spell) { | |
| let ticks = spell.duration / spell.interval; | |
| function tick() { | |
| if (ticks > 0 && ally.alive && gameActive) { | |
| healAlly(ally, spell.heal); | |
| ticks--; | |
| setTimeout(tick, spell.interval * 1000); | |
| } | |
| } | |
| tick(); | |
| } | |
| // Function to perform chain heal | |
| function chainHeal(spell) { | |
| const livingAllies = allies.filter(ally => ally.alive); | |
| const targets = livingAllies.sort(() => 0.5 - Math.random()).slice(0, spell.targets); | |
| targets.forEach(ally => healAlly(ally, spell.heal)); | |
| } | |
| // Function to heal all allies | |
| function teamHeal(spell) { | |
| allies.forEach(ally => healAlly(ally, spell.heal)); | |
| } | |
| // Function to start spell cooldown | |
| function startSpellCooldown(spellIndex) { | |
| const button = document.querySelectorAll('.spell-button')[spellIndex]; | |
| const progressBar = button.querySelector('.cooldown-progress'); | |
| button.classList.add('on-cooldown'); | |
| progressBar.style.transition = 'width 0.1s linear'; | |
| progressBar.style.width = '100%'; | |
| setTimeout(() => { | |
| progressBar.style.transition = `width ${spells[spellIndex].cooldown}s linear`; | |
| progressBar.style.width = '0'; | |
| }, 100); | |
| setTimeout(() => { | |
| button.classList.remove('on-cooldown'); | |
| updateSpellButtons(); | |
| }, spells[spellIndex].cooldown * 1000); | |
| } | |
| // Function to start global cooldown | |
| function startGlobalCooldown() { | |
| globalCooldown = true; | |
| updateSpellButtons(); | |
| setTimeout(() => { | |
| globalCooldown = false; | |
| updateSpellButtons(); | |
| }, 1500); | |
| } | |
| // Function for boss to damage allies | |
| function bossDamage() { | |
| if (!gameActive) return; | |
| const damageAmount = 5; | |
| const livingAllies = allies.filter(ally => ally.alive); | |
| if (livingAllies.length === 0) { | |
| endGame(); | |
| return; | |
| } | |
| const targetAlly = livingAllies[Math.floor(Math.random() * livingAllies.length)]; | |
| targetAlly.hp = Math.max(targetAlly.hp - damageAmount, 0); | |
| if (targetAlly.hp === 0) { | |
| targetAlly.alive = false; | |
| } | |
| updateHealthBar(targetAlly, document.querySelectorAll('.health-bar')[targetAlly.id]); | |
| if (allies.every(ally => !ally.alive)) { | |
| endGame(); | |
| } | |
| } | |
| // Function for boss bleed ability | |
| function bossBleedAbility() { | |
| if (!gameActive) return; | |
| bossBleedCooldown--; | |
| if (bossBleedCooldown <= 0) { | |
| const livingAllies = allies.filter(ally => ally.alive); | |
| if (livingAllies.length > 0) { | |
| const targetAlly = livingAllies[Math.floor(Math.random() * livingAllies.length)]; | |
| targetAlly.bleeding = true; | |
| updateHealthBar(targetAlly, document.querySelectorAll('.health-bar')[targetAlly.id]); | |
| let bleedTicks = 4; | |
| function bleedTick() { | |
| if (bleedTicks > 0 && targetAlly.alive && gameActive) { | |
| targetAlly.hp = Math.max(targetAlly.hp - 10, 0); | |
| updateHealthBar(targetAlly, document.querySelectorAll('.health-bar')[targetAlly.id]); | |
| bleedTicks--; | |
| if (bleedTicks === 0) { | |
| targetAlly.bleeding = false; | |
| updateHealthBar(targetAlly, document.querySelectorAll('.health-bar')[targetAlly.id]); | |
| } else { | |
| setTimeout(bleedTick, 1000); | |
| } | |
| } | |
| } | |
| bleedTick(); | |
| } | |
| bossBleedCooldown = 15; | |
| } | |
| } | |
| // Function for allies to attack boss | |
| function alliesAttack() { | |
| if (!gameActive) return; | |
| const damagePerAlly = 5; | |
| const livingAllies = allies.filter(ally => ally.alive); | |
| const totalDamage = livingAllies.length * damagePerAlly; | |
| boss.hp = Math.max(boss.hp - totalDamage, 0); | |
| updateHealthBar(boss, document.getElementById('boss-health-bar')); | |
| if (boss.hp <= 0) { | |
| victory(); | |
| } | |
| } | |
| // Function to regenerate mana | |
| function regenerateMana() { | |
| if (gameActive && playerMana < maxMana) { | |
| playerMana = Math.min(playerMana + 1, maxMana); | |
| updateManaBar(); | |
| } | |
| } | |
| // Function to end the game (loss) | |
| function endGame() { | |
| gameActive = false; | |
| document.getElementById('game-over').style.display = 'block'; | |
| document.getElementById('reset-button').style.display = 'block'; | |
| updateSpellButtons(); | |
| } | |
| // Function to end the game (victory) | |
| function victory() { | |
| gameActive = false; | |
| document.getElementById('victory').style.display = 'block'; | |
| document.getElementById('reset-button').style.display = 'block'; | |
| updateSpellButtons(); | |
| } | |
| // Function to reset the game and return to the welcome screen | |
| function resetGame() { | |
| document.getElementById('game-container').style.display = 'none'; | |
| document.getElementById('welcome-screen').style.display = 'flex'; | |
| gameActive = true; | |
| document.getElementById('game-over').style.display = 'none'; | |
| document.getElementById('victory').style.display = 'none'; | |
| document.getElementById('reset-button').style.display = 'none'; | |
| createAllies(); | |
| createSpellButtons(); | |
| boss = { hp: 5000, maxHp: 5000 }; | |
| updateHealthBar(boss, document.getElementById('boss-health-bar')); | |
| selectedAlly = null; | |
| globalCooldown = false; | |
| playerMana = maxMana; | |
| bossBleedCooldown = 15; | |
| updateManaBar(); | |
| updateSpellButtons(); | |
| } | |
| // Main game loop | |
| function gameLoop() { | |
| if (gameActive) { | |
| bossDamage(); | |
| bossBleedAbility(); | |
| alliesAttack(); | |
| setTimeout(gameLoop, 1000); | |
| } | |
| } | |
| // Function to populate the spellbook | |
| function populateSpellbook() { | |
| const spellbookContainer = document.getElementById('spellbook'); | |
| spellbookContainer.innerHTML = ''; | |
| spells.forEach(spell => { | |
| const spellInfo = document.createElement('div'); | |
| spellInfo.innerHTML = `<h3>${spell.name}</h3> | |
| <p>${spell.description}</p> | |
| <p>Mana Cost: ${spell.manaCost}</p> | |
| <p>Cooldown: ${spell.cooldown} seconds</p>`; | |
| spellbookContainer.appendChild(spellInfo); | |
| }); | |
| } | |
| // Set up mana regeneration interval | |
| setInterval(regenerateMana, 2000); | |
| // Event listener for keyboard input | |
| document.addEventListener('keydown', (event) => { | |
| const key = event.key; | |
| if (key >= '1' && key <= '5') { | |
| castSpell(parseInt(key) - 1); | |
| } | |
| }); | |
| // Event listener for reset button | |
| document.getElementById('reset-button').addEventListener('click', resetGame); | |
| // Event listener for start button | |
| document.getElementById('start-button').addEventListener('click', () => { | |
| document.getElementById('welcome-screen').style.display = 'none'; | |
| document.getElementById('game-container').style.display = 'block'; | |
| gameLoop(); // Start the game loop | |
| }); | |
| // Event listeners for accordions | |
| const accordions = document.getElementsByClassName("accordion"); | |
| for (let i = 0; i < accordions.length; i++) { | |
| accordions[i].addEventListener("click", function() { | |
| this.classList.toggle("active"); | |
| const panel = this.nextElementSibling; | |
| if (panel.style.maxHeight) { | |
| panel.style.maxHeight = null; | |
| } else { | |
| panel.style.maxHeight = panel.scrollHeight + "px"; | |
| } | |
| }); | |
| } | |
| // Initial game setup | |
| createAllies(); | |
| createSpellButtons(); | |
| updateHealthBar(boss, document.getElementById('boss-health-bar')); | |
| updateManaBar(); | |
| populateSpellbook(); | |
| </script> | |
| </body> | |
| </html> |