austrian-game-idea / index.html
austrian11's picture
undefined - Initial Deployment
5e534dc verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dice & Destiny RPG</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=MedievalSharp&display=swap');
body {
font-family: 'MedievalSharp', cursive;
background-image: url('https://images.unsplash.com/photo-1506318137071-a8e06380a6a1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1470&q=80');
background-size: cover;
background-attachment: fixed;
color: #e2e8f0;
}
.stat-bar {
height: 20px;
background: linear-gradient(90deg, #4a5568 0%, #2d3748 50%, #4a5568 100%);
border-radius: 10px;
overflow: hidden;
}
.stat-fill {
height: 100%;
border-radius: 10px;
transition: width 0.5s ease-in-out;
}
.hp-fill { background: linear-gradient(90deg, #dc2626 0%, #b91c1c 50%, #dc2626 100%); }
.mana-fill { background: linear-gradient(90deg, #2563eb 0%, #1d4ed8 50%, #2563eb 100%); }
.stamina-fill { background: linear-gradient(90deg, #16a34a 0%, #15803d 50%, #16a34a 100%); }
.dice {
animation: roll 0.5s ease-out;
transform-style: preserve-3d;
}
@keyframes roll {
0% { transform: rotateX(0deg) rotateY(0deg); }
100% { transform: rotateX(720deg) rotateY(360deg); }
}
.character-card {
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.character-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2);
}
.tooltip {
position: relative;
display: inline-block;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 200px;
background-color: #1e293b;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
transform: translateX(-50%);
opacity: 0;
transition: opacity 0.3s;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
.ability-btn {
transition: all 0.2s ease;
}
.ability-btn:hover {
transform: scale(1.05);
}
.ability-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.dialog-container {
animation: fadeIn 0.3s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body class="min-h-screen bg-gray-900 bg-opacity-90">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-8">
<h1 class="text-5xl font-bold text-yellow-400 mb-2">Dice & Destiny</h1>
<p class="text-xl text-gray-300">A Tabletop RPG Experience in Your Browser</p>
</header>
<div id="game-container" class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Character Selection Section -->
<div id="character-selection" class="lg:col-span-3 bg-gray-800 bg-opacity-75 rounded-lg p-6 shadow-xl">
<h2 class="text-3xl text-yellow-300 mb-6 text-center">Choose Your Hero</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Warrior -->
<div class="character-card bg-gray-700 p-4 rounded-lg cursor-pointer border-2 border-transparent hover:border-yellow-400" onclick="selectCharacter('warrior')">
<div class="flex justify-between items-center mb-3">
<h3 class="text-xl font-bold text-red-400">Warrior</h3>
<i class="fas fa-shield-alt text-2xl text-red-300"></i>
</div>
<img src="https://i.imgur.com/JTQf8jD.png" alt="Warrior" class="w-full h-40 object-contain mb-3">
<div class="text-sm text-gray-300">
<p><span class="font-bold">HP:</span> High</p>
<p><span class="font-bold">Damage:</span> Moderate</p>
<p><span class="font-bold">Abilities:</span> Toughness, Powerful Strikes</p>
<p class="mt-2 text-xs">A battle-hardened fighter with exceptional durability and strong melee attacks.</p>
</div>
</div>
<!-- Rogue -->
<div class="character-card bg-gray-700 p-4 rounded-lg cursor-pointer border-2 border-transparent hover:border-yellow-400" onclick="selectCharacter('rogue')">
<div class="flex justify-between items-center mb-3">
<h3 class="text-xl font-bold text-green-400">Rogue</h3>
<i class="fas fa-mask text-2xl text-green-300"></i>
</div>
<img src="https://i.imgur.com/F4zVHj2.png" alt="Rogue" class="w-full h-40 object-contain mb-3">
<div class="text-sm text-gray-300">
<p><span class="font-bold">HP:</span> Low</p>
<p><span class="font-bold">Damage:</span> High</p>
<p><span class="font-bold">Abilities:</span> Sneak Attack, Evasion</p>
<p class="mt-2 text-xs">A stealthy assassin who relies on speed and precision to land critical hits.</p>
</div>
</div>
<!-- Mage -->
<div class="character-card bg-gray-700 p-4 rounded-lg cursor-pointer border-2 border-transparent hover:border-yellow-400" onclick="selectCharacter('mage')">
<div class="flex justify-between items-center mb-3">
<h3 class="text-xl font-bold text-blue-400">Mage</h3>
<i class="fas fa-hat-wizard text-2xl text-blue-300"></i>
</div>
<img src="https://i.imgur.com/9QZmXTK.png" alt="Mage" class="w-full h-40 object-contain mb-3">
<div class="text-sm text-gray-300">
<p><span class="font-bold">HP:</span> Very Low</p>
<p><span class="font-bold">Damage:</span> Very High</p>
<p><span class="font-bold">Abilities:</span> Fireball, Magic Shield</p>
<p class="mt-2 text-xs">A master of arcane arts who can unleash devastating spells but is fragile.</p>
</div>
</div>
<!-- Cleric -->
<div class="character-card bg-gray-700 p-4 rounded-lg cursor-pointer border-2 border-transparent hover:border-yellow-400" onclick="selectCharacter('cleric')">
<div class="flex justify-between items-center mb-3">
<h3 class="text-xl font-bold text-purple-400">Cleric</h3>
<i class="fas fa-bible text-2xl text-purple-300"></i>
</div>
<img src="https://i.imgur.com/lp8b5Ly.png" alt="Cleric" class="w-full h-40 object-contain mb-3">
<div class="text-sm text-gray-300">
<p><span class="font-bold">HP:</span> Moderate</p>
<p><span class="font-bold">Damage:</span> Low</p>
<p><span class="font-bold">Abilities:</span> Healing, Turn Undead</p>
<p class="mt-2 text-xs">A holy warrior who can heal wounds and smite enemies with divine power.</p>
</div>
</div>
</div>
<div id="selected-character-info" class="mt-6 bg-gray-700 p-4 rounded-lg hidden">
<div class="flex items-center justify-between">
<div>
<h3 id="selected-class" class="text-2xl font-bold"></h3>
<p id="selected-description" class="text-gray-300"></p>
</div>
<button onclick="startGame()" class="bg-yellow-500 hover:bg-yellow-600 text-black font-bold py-2 px-6 rounded-lg transition-colors flex items-center">
<i class="fas fa-dice mr-2"></i> Begin Adventure
</button>
</div>
</div>
</div>
<!-- Main Game Area (Hidden Initially) -->
<div id="game-area" class="lg:col-span-3 hidden">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Player Stats -->
<div class="bg-gray-800 bg-opacity-75 rounded-lg p-6 shadow-xl">
<div class="flex justify-between items-center mb-4">
<h2 id="player-name" class="text-2xl font-bold text-yellow-300"></h2>
<span id="player-level" class="bg-yellow-600 text-black rounded-full px-3 py-1 text-sm font-bold">Level 1</span>
</div>
<div id="player-icon" class="flex justify-center mb-4">
<i class="fas fa-user-circle text-8xl"></i>
</div>
<div class="mb-4">
<div class="flex justify-between mb-1">
<span class="text-red-300">HP</span>
<span id="hp-text" class="font-bold">100/100</span>
</div>
<div class="stat-bar">
<div id="hp-fill" class="stat-fill hp-fill" style="width: 100%"></div>
</div>
</div>
<div class="mb-4">
<div class="flex justify-between mb-1">
<span class="text-blue-300">Mana</span>
<span id="mana-text" class="font-bold">50/50</span>
</div>
<div class="stat-bar">
<div id="mana-fill" class="stat-fill mana-fill" style="width: 100%"></div>
</div>
</div>
<div class="mb-6">
<div class="flex justify-between mb-1">
<span class="text-green-300">Stamina</span>
<span id="stamina-text" class="font-bold">80/80</span>
</div>
<div class="stat-bar">
<div id="stamina-fill" class="stat-fill stamina-fill" style="width: 100%"></div>
</div>
</div>
<div class="grid grid-cols-2 gap-2 mb-4">
<div class="bg-gray-700 p-2 rounded text-center">
<div class="text-xl font-bold text-yellow-300">10</div>
<div class="text-xs text-gray-300">Strength</div>
</div>
<div class="bg-gray-700 p-2 rounded text-center">
<div class="text-xl font-bold text-yellow-300">12</div>
<div class="text-xs text-gray-300">Dexterity</div>
</div>
<div class="bg-gray-700 p-2 rounded text-center">
<div class="text-xl font-bold text-yellow-300">14</div>
<div class="text-xs text-gray-300">Intellect</div>
</div>
<div class="bg-gray-700 p-2 rounded text-center">
<div class="text-xl font-bold text-yellow-300">8</div>
<div class="text-xs text-gray-300">Constitution</div>
</div>
</div>
<div class="mt-4">
<h3 class="text-lg font-bold text-yellow-300 mb-2">Equipment</h3>
<div class="grid grid-cols-3 gap-2 text-center">
<div class="tooltip">
<div class="bg-gray-700 p-2 rounded cursor-pointer">
<i class="fas fa-sword text-xl"></i>
</div>
<span class="tooltiptext">Iron Sword - 1d8 damage</span>
</div>
<div class="tooltip">
<div class="bg-gray-700 p-2 rounded cursor-pointer">
<i class="fas fa-tshirt text-xl"></i>
</div>
<span class="tooltiptext">Leather Armor - +2 AC</span>
</div>
<div class="tooltip">
<div class="bg-gray-700 p-2 rounded cursor-pointer">
<i class="fas fa-ring text-xl"></i>
</div>
<span class="tooltiptext">Ring of Vitality - +5 HP</span>
</div>
</div>
</div>
</div>
<!-- Game Log -->
<div class="bg-gray-800 bg-opacity-75 rounded-lg p-6 shadow-xl lg:col-span-2">
<div class="h-96 overflow-y-auto mb-4 bg-black bg-opacity-50 p-3 rounded-lg border border-gray-700" id="game-log">
<div class="text-yellow-300 text-center italic">Welcome to Dice & Destiny! Your adventure begins...</div>
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2 mb-4" id="action-buttons">
<!-- Buttons will be added dynamically -->
</div>
<div class="flex flex-wrap gap-2 justify-between">
<button onclick="rollDice('d4')" class="bg-purple-600 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice-d4 mr-2"></i> d4
</button>
<button onclick="rollDice('d6')" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice-d6 mr-2"></i> d6
</button>
<button onclick="rollDice('d8')" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice-d8 mr-2"></i> d8
</button>
<button onclick="rollDice('d10')" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice-d10 mr-2"></i> d10
</button>
<button onclick="rollDice('d12')" class="bg-red-600 hover:bg-red-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice-d12 mr-2"></i> d12
</button>
<button onclick="rollDice('d20')" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice-d20 mr-2"></i> d20
</button>
<button onclick="rollDice('d100')" class="bg-pink-600 hover:bg-pink-700 text-white font-bold py-2 px-4 rounded flex items-center">
<i class="fas fa-dice mr-2"></i> d100
</button>
</div>
<div class="mt-4 flex items-center">
<div id="dice-result-container" class="mr-4 hidden">
<div class="text-xl font-bold text-yellow-300">Rolled: <span id="dice-result" class="text-white">0</span></div>
<div class="text-xs text-gray-400">Dice: <span id="dice-type" class="text-white">d20</span></div>
</div>
<div id="dice-animation" class="dice text-4xl hidden">
<i class="fas fa-dice-d20"></i>
</div>
</div>
</div>
<!-- Enemy Encounter -->
<div id="enemy-container" class="lg:col-span-3 bg-gray-800 bg-opacity-75 rounded-lg p-6 shadow-xl hidden">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="md:col-span-2">
<div class="flex justify-between items-center mb-4">
<h2 id="enemy-name" class="text-2xl font-bold text-red-400"></h2>
<span id="enemy-health" class="font-bold">100/100 HP</span>
</div>
<div class="mb-4">
<div class="stat-bar">
<div id="enemy-hp-fill" class="stat-fill hp-fill" style="width: 100%"></div>
</div>
</div>
<div id="enemy-image" class="flex justify-center mb-4">
<img src="https://i.imgur.com/gmQdQ9T.png" alt="Goblin" class="h-48">
</div>
<div class="grid grid-cols-3 gap-4 text-center">
<div class="bg-gray-700 p-2 rounded">
<div class="text-lg font-bold">5</div>
<div class="text-xs">ATK</div>
</div>
<div class="bg-gray-700 p-2 rounded">
<div class="text-lg font-bold">12</div>
<div class="text-xs">AC</div>
</div>
<div class="bg-gray-700 p-2 rounded">
<div class="text-lg font-bold">50</div>
<div class="text-xs">XP</div>
</div>
</div>
</div>
<div>
<h3 class="text-lg font-bold text-yellow-300 mb-2">Combat Actions</h3>
<div class="space-y-2">
<button onclick="attackEnemy()" class="ability-btn bg-red-600 hover:bg-red-700 text-white w-full py-2 px-4 rounded flex items-center justify-between">
<span>Attack</span>
<span class="text-xs">1d20 + STR</span>
</button>
<button onclick="castSpell('fireball')" class="ability-btn bg-orange-500 hover:bg-orange-600 text-white w-full py-2 px-4 rounded flex items-center justify-between">
<span>Fireball</span>
<span class="text-xs">3d6 damage, 10 mana</span>
</button>
<button onclick="useAbility('heal')" class="ability-btn bg-green-600 hover:bg-green-700 text-white w-full py-2 px-4 rounded flex items-center justify-between">
<span>Heal</span>
<span class="text-xs">2d8 + WIS, 15 mana</span>
</button>
<button onclick="useAbility('dodge')" class="ability-btn bg-blue-600 hover:bg-blue-700 text-white w-full py-2 px-4 rounded flex items-center justify-between">
<span>Dodge</span>
<span class="text-xs">+2 AC, 5 stamina</span>
</button>
</div>
<div class="mt-4 bg-gray-700 p-3 rounded">
<h4 class="text-md font-bold mb-1">Enemy Description</h4>
<p id="enemy-desc" class="text-sm text-gray-300">A small but vicious goblin with sharp teeth and a rusty dagger. It looks hungry for trouble.</p>
</div>
</div>
</div>
</div>
<!-- Inventory & Quests -->
<div id="inventory-quests" class="lg:col-span-3 grid grid-cols-1 lg:grid-cols-2 gap-6 hidden">
<!-- Inventory -->
<div class="bg-gray-800 bg-opacity-75 rounded-lg p-6 shadow-xl">
<h2 class="text-2xl font-bold text-yellow-300 mb-4">Inventory</h2>
<div class="grid grid-cols-3 gap-2">
<div class="bg-gray-700 p-3 rounded cursor-pointer hover:bg-gray-600 transition-colors">
<div class="flex justify-between items-start">
<i class="fas fa-flask text-xl text-blue-300"></i>
<span class="bg-yellow-600 text-black text-xs px-1 rounded">5</span>
</div>
<div class="text-sm mt-1">Health Potion</div>
</div>
<div class="bg-gray-700 p-3 rounded cursor-pointer hover:bg-gray-600 transition-colors">
<i class="fas fa-scroll text-xl text-yellow-300"></i>
<div class="text-sm mt-1">Scroll of Fire</div>
</div>
<div class="bg-gray-700 p-3 rounded cursor-pointer hover:bg-gray-600 transition-colors">
<i class="fas fa-key text-xl text-yellow-400"></i>
<div class="text-sm mt-1">Old Key</div>
</div>
<div class="bg-gray-700 p-3 rounded cursor-pointer hover:bg-gray-600 transition-colors">
<i class="fas fa-bread-slice text-xl text-yellow-200"></i>
<div class="text-sm mt-1">Rations (3)</div>
</div>
<div class="bg-gray-700 p-3 rounded cursor-pointer hover:bg-gray-600 transition-colors">
<i class="fas fa-gem text-xl text-purple-300"></i>
<div class="text-sm mt-1">Amethyst</div>
</div>
<div class="bg-gray-700 p-3 rounded cursor-pointer hover:bg-gray-600 transition-colors">
<i class="fas fa-coins text-xl text-yellow-500"></i>
<div class="text-sm mt-1">25 Gold</div>
</div>
</div>
<div class="mt-4">
<button onclick="useItem('Health Potion')" class="bg-green-600 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
Use Health Potion
</button>
<button onclick="sellItems()" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-4 rounded ml-2">
Sell Items
</button>
</div>
</div>
<!-- Quests -->
<div class="bg-gray-800 bg-opacity-75 rounded-lg p-6 shadow-xl">
<h2 class="text-2xl font-bold text-yellow-300 mb-4">Quests</h2>
<div class="mb-4 bg-gray-700 p-4 rounded border-l-4 border-yellow-500">
<h3 class="text-lg font-bold flex items-center">
<i class="fas fa-check-circle text-green-400 mr-2"></i>
The Missing Blacksmith
</h3>
<p class="text-sm mt-1 text-gray-300">Find the blacksmith's lost tools in the goblin caves. Reward: 50 gold</p>
<div class="w-full bg-gray-600 rounded-full h-2 mt-2">
<div class="bg-green-400 h-2 rounded-full" style="width: 100%"></div>
</div>
<p class="text-xs text-right mt-1 text-gray-400">Completed</p>
</div>
<div class="mb-4 bg-gray-700 p-4 rounded border-l-4 border-yellow-500">
<h3 class="text-lg font-bold flex items-center">
<i class="fas fa-hourglass-half text-yellow-400 mr-2"></i>
Bandit Camp Infiltration
</h3>
<p class="text-sm mt-1 text-gray-300">Gather information about the bandit camp near the river. Reward: Leather Armor</p>
<div class="w-full bg-gray-600 rounded-full h-2 mt-2">
<div class="bg-blue-400 h-2 rounded-full" style="width: 65%"></div>
</div>
<p class="text-xs text-right mt-1 text-gray-400">65% complete</p>
</div>
<div class="mb-4 bg-gray-700 p-4 rounded border-l-4 border-blue-500">
<h3 class="text-lg font-bold flex items-center">
<i class="fas fa-exclamation-circle text-blue-400 mr-2"></i>
Ancient Relics
</h3>
<p class="text-sm mt-1 text-gray-300">Recover the three ancient relics from the ruins for the mage's guild. Reward: Arcane Tome</p>
<div class="flex space-x-2 mt-2">
<div class="w-full bg-gray-600 rounded-full h-2">
<div class="bg-blue-400 h-2 rounded-full" style="width: 0%"></div>
</div>
<div class="w-full bg-gray-600 rounded-full h-2">
<div class="bg-blue-400 h-2 rounded-full" style="width: 0%"></div>
</div>
<div class="w-full bg-gray-600 rounded-full h-2">
<div class="bg-blue-400 h-2 rounded-full" style="width: 0%"></div>
</div>
</div>
<p class="text-xs text-right mt-1 text-gray-400">0/3 relics found</p>
</div>
<button onclick="showQuestDetails()" class="bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-full">
View Quest Details
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Game Over Modal -->
<div id="game-over-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center hidden z-50">
<div class="bg-gray-800 p-8 rounded-lg max-w-md w-full dialog-container">
<div class="text-center">
<h2 class="text-3xl font-bold text-red-500 mb-4">Game Over!</h2>
<p class="text-gray-300 mb-6">Your brave adventurer has fallen in battle. But every end is a new beginning...</p>
<div class="mb-6">
<p class="text-yellow-300">Your accomplishments:</p>
<ul class="text-gray-300 text-left list-disc pl-5 mt-2">
<li>Defeated 3 enemies</li>
<li>Completed 1 quest</li>
<li>Reached level 2</li>
</ul>
</div>
<div class="flex justify-center space-x-4">
<button onclick="retryGame()" class="bg-yellow-600 hover:bg-yellow-700 text-white font-bold py-2 px-6 rounded">
Try Again
</button>
<button onclick="newCharacter()" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-6 rounded">
New Hero
</button>
</div>
</div>
</div>
</div>
<!-- Level Up Modal -->
<div id="level-up-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center hidden z-50">
<div class="bg-gray-800 p-8 rounded-lg max-w-md w-full dialog-container">
<div class="text-center">
<h2 class="text-3xl font-bold text-yellow-400 mb-4">Level Up!</h2>
<p class="text-gray-300 mb-6">Congratulations! You've reached level <span id="new-level" class="text-yellow-300">2</span>.</p>
<div class="mb-6">
<p class="text-yellow-300 mb-3">Choose an attribute to improve:</p>
<div class="grid grid-cols-3 gap-2">
<button onclick="levelUpAttribute('strength')" class="bg-red-700 hover:bg-red-800 text-white font-bold py-2 px-4 rounded">
Strength +1
</button>
<button onclick="levelUpAttribute('dexterity')" class="bg-green-700 hover:bg-green-800 text-white font-bold py-2 px-4 rounded">
Dexterity +1
</button>
<button onclick="levelUpAttribute('intellect')" class="bg-blue-700 hover:bg-blue-800 text-white font-bold py-2 px-4 rounded">
Intellect +1
</button>
</div>
</div>
<p class="text-gray-300 text-sm">HP and resource pools have been fully restored.</p>
</div>
</div>
</div>
</div>
<script>
// Game State
const gameState = {
character: null,
hp: 0,
maxHp: 0,
mana: 0,
maxMana: 0,
stamina: 0,
maxStamina: 0,
level: 1,
xp: 0,
xpToNextLevel: 100,
enemiesDefeated: 0,
currentEnemy: null,
inCombat: false,
abilities: [],
inventory: [],
quests: []
};
// Character Classes
const characterClasses = {
warrior: {
name: "Warrior",
icon: "fa-shield-alt",
color: "red",
description: "A battle-hardened fighter with high health and strong physical attacks.",
stats: {
str: 16,
dex: 12,
int: 8,
con: 14
},
resources: {
hp: 120,
mana: 20,
stamina: 100
},
abilities: [
{
name: "Power Strike",
description: "A mighty swing that deals extra damage.",
cost: 15,
costType: "stamina",
effect: "2d8 + STR damage"
},
{
name: "Defensive Stance",
description: "Brace yourself, reducing damage taken by 50% for one turn.",
cost: 20,
costType: "stamina",
effect: "Damage reduction"
}
]
},
rogue: {
name: "Rogue",
icon: "fa-mask",
color: "green",
description: "A stealthy assassin who relies on speed and precision to land critical hits.",
stats: {
str: 10,
dex: 18,
int: 12,
con: 10
},
resources: {
hp: 80,
mana: 30,
stamina: 120
},
abilities: [
{
name: "Backstab",
description: "Strike an unsuspecting foe for critical damage.",
cost: 20,
costType: "stamina",
effect: "3d6 + DEX damage (2x on surprise)"
},
{
name: "Evasion",
description: "Dodge the next attack automatically.",
cost: 25,
costType: "stamina",
effect: "Avoid next attack"
}
]
},
mage: {
name: "Mage",
icon: "fa-hat-wizard",
color: "blue",
description: "A master of arcane arts who can unleash devastating spells but is fragile.",
stats: {
str: 8,
dex: 10,
int: 18,
con: 8
},
resources: {
hp: 60,
mana: 150,
stamina: 40
},
abilities: [
{
name: "Fireball",
description: "Launch a fiery projectile that explodes on impact.",
cost: 30,
costType: "mana",
effect: "3d6 + INT damage in area"
},
{
name: "Magic Shield",
description: "Create a protective barrier that absorbs damage.",
cost: 25,
costType: "mana",
effect: "Absorb 15 damage"
}
]
},
cleric: {
name: "Cleric",
icon: "fa-bible",
color: "purple",
description: "A holy warrior who can heal wounds and smite enemies with divine power.",
stats: {
str: 12,
dex: 10,
int: 16,
con: 12
},
resources: {
hp: 100,
mana: 100,
stamina: 60
},
abilities: [
{
name: "Healing Light",
description: "Channel divine energy to restore health.",
cost: 20,
costType: "mana",
effect: "2d8 + WIS healing"
},
{
name: "Smite",
description: "Infuse your attack with divine energy.",
cost: 25,
costType: "mana",
effect: "1d8 + STR + 1d8 radiant damage"
}
]
}
};
// Enemies
const enemies = [
{
name: "Goblin",
image: "https://i.imgur.com/gmQdQ9T.png",
maxHp: 40,
hp: 40,
attack: 5,
ac: 12,
xp: 50,
description: "A small but vicious goblin with sharp teeth and a rusty dagger. It looks hungry for trouble."
},
{
name: "Orc",
image: "https://i.imgur.com/QyRmS0c.png",
maxHp: 65,
hp: 65,
attack: 8,
ac: 14,
xp: 100,
description: "A hulking orc warrior with crude armor and a massive club. His eyes burn with battle lust."
},
{
name: "Skeleton",
image: "https://i.imgur.com/CBYlZ7t.png",
maxHp: 30,
hp: 30,
attack: 4,
ac: 13,
xp: 40,
description: "An animated skeleton with a rusted sword. Its hollow eye sockets glow with an eerie red light."
},
{
name: "Bandit",
image: "https://i.imgur.com/wxY8y1Q.png",
maxHp: 50,
hp: 50,
attack: 6,
ac: 13,
xp: 75,
description: "A human bandit with leather armor and a short sword. He looks desperate but dangerous."
}
];
// Quests
const quests = [
{
name: "The Missing Blacksmith",
description: "Find the blacksmith's lost tools in the goblin caves.",
reward: "50 gold",
progress: 1,
completed: true
},
{
name: "Bandit Camp Infiltration",
description: "Gather information about the bandit camp near the river.",
reward: "Leather Armor",
progress: 0.65,
completed: false
},
{
name: "Ancient Relics",
description: "Recover the three ancient relics from the ruins for the mage's guild.",
reward: "Arcane Tome",
progress: 0,
completed: false
}
];
// Inventory
const inventoryItems = [
{
name: "Health Potion",
icon: "fa-flask",
color: "blue-300",
quantity: 5,
description: "Restores 2d4+2 HP when consumed."
},
{
name: "Scroll of Fire",
icon: "fa-scroll",
color: "yellow-300",
quantity: 1,
description: "Can cast Fireball once (3d6 fire damage)."
},
{
name: "Old Key",
icon: "fa-key",
color: "yellow-400",
quantity: 1,
description: "A rusty old key. It might open something important."
},
{
name: "Rations",
icon: "fa-bread-slice",
color: "yellow-200",
quantity: 3,
description: "Enough food for one day of travel."
},
{
name: "Amethyst",
icon: "fa-gem",
color: "purple-300",
quantity: 1,
description: "A precious gemstone worth about 25 gold."
},
{
name: "Gold",
icon: "fa-coins",
color: "yellow-500",
quantity: 25,
description: "Shiny gold coins for purchasing items."
}
];
// DOM Elements
const characterSelection = document.getElementById('character-selection');
const selectedCharacterInfo = document.getElementById('selected-character-info');
const selectedClass = document.getElementById('selected-class');
const selectedDescription = document.getElementById('selected-description');
const gameArea = document.getElementById('game-area');
const gameContainer = document.getElementById('game-container');
const playerName = document.getElementById('player-name');
const playerLevel = document.getElementById('player-level');
const playerIcon = document.getElementById('player-icon');
const hpText = document.getElementById('hp-text');
const hpFill = document.getElementById('hp-fill');
const manaText = document.getElementById('mana-text');
const manaFill = document.getElementById('mana-fill');
const staminaText = document.getElementById('stamina-text');
const staminaFill = document.getElementById('stamina-fill');
const actionButtons = document.getElementById('action-buttons');
const gameLog = document.getElementById('game-log');
const enemyContainer = document.getElementById('enemy-container');
const enemyName = document.getElementById('enemy-name');
const enemyHealth = document.getElementById('enemy-health');
const enemyHpFill = document.getElementById('enemy-hp-fill');
const enemyImage = document.getElementById('enemy-image');
const enemyDesc = document.getElementById('enemy-desc');
const inventoryQuests = document.getElementById('inventory-quests');
const diceResultContainer = document.getElementById('dice-result-container');
const diceResult = document.getElementById('dice-result');
const diceType = document.getElementById('dice-type');
const diceAnimation = document.getElementById('dice-animation');
const gameOverModal = document.getElementById('game-over-modal');
const levelUpModal = document.getElementById('level-up-modal');
const newLevel = document.getElementById('new-level');
// Game Functions
function selectCharacter(characterClass) {
const character = characterClasses[characterClass];
gameState.character = characterClass;
selectedCharacterInfo.classList.remove('hidden');
selectedClass.textContent = character.name;
selectedClass.className = `text-2xl font-bold text-${character.color}-400`;
selectedDescription.textContent = character.description;
// Highlight selected character card
document.querySelectorAll('.character-card').forEach(card => {
card.classList.remove('border-yellow-400', 'bg-gray-600');
});
const selectedCard = document.querySelector(`[onclick="selectCharacter('${characterClass}')"]`);
selectedCard.classList.add('border-yellow-400', 'bg-gray-600');
addToLog(`Selected character: ${character.name}`);
}
function startGame() {
if (!gameState.character) {
alert("Please select a character first!");
return;
}
const character = characterClasses[gameState.character];
// Set up player stats
gameState.hp = character.resources.hp;
gameState.maxHp = character.resources.hp;
gameState.mana = character.resources.mana;
gameState.maxMana = character.resources.mana;
gameState.stamina = character.resources.stamina;
gameState.maxStamina = character.resources.stamina;
gameState.level = 1;
gameState.xp = 0;
gameState.xpToNextLevel = 100;
gameState.enemiesDefeated = 0;
gameState.inventory = JSON.parse(JSON.stringify(inventoryItems));
gameState.quests = JSON.parse(JSON.stringify(quests));
gameState.abilities = JSON.parse(JSON.stringify(character.abilities));
// Update UI
characterSelection.classList.add('hidden');
gameArea.classList.remove('hidden');
playerName.textContent = character.name;
playerName.className = `text-2xl font-bold text-${character.color}-300`;
playerLevel.textContent = `Level ${gameState.level}`;
playerIcon.innerHTML = `<i class="fas ${character.icon} text-8xl text-${character.color}-300"></i>`;
updateStats();
// Set up action buttons
setupActionButtons();
// Add initial game log messages
addToLog(`Welcome, brave ${character.name}! Your adventure begins now.`, 'yellow');
addToLog("You find yourself in a small village at the edge of a dark forest.");
addToLog("The villagers speak of increasing monster attacks in the area.");
addToLog("You decide to help by exploring the forest and eliminating threats.");
// Show first quest
setTimeout(() => {
addToLog(`Quest: ${gameState.quests[0].name} - ${gameState.quests[0].description}`, 'yellow');
addToLog("Reward: " + gameState.quests[0].reward, 'yellow');
addToLog("You set out towards the goblin caves to the east...");
// Trigger first encounter after a delay
setTimeout(() => {
createRandomEncounter();
}, 2000);
}, 1000);
}
function updateStats() {
const character = characterClasses[gameState.character];
hpText.textContent = `${gameState.hp}/${gameState.maxHp}`;
manaText.textContent = `${gameState.mana}/${gameState.maxMana}`;
staminaText.textContent = `${gameState.stamina}/${gameState.maxStamina}`;
hpFill.style.width = `${(gameState.hp / gameState.maxHp) * 100}%`;
manaFill.style.width = `${(gameState.mana / gameState.maxMana) * 100}%`;
staminaFill.style.width = `${(gameState.stamina / gameState.maxStamina) * 100}%`;
playerLevel.textContent = `Level ${gameState.level}`;
}
function setupActionButtons() {
actionButtons.innerHTML = '';
// Common actions
const commonActions = [
{ name: "Explore", icon: "fa-binoculars", color: "green", action: "explore" },
{ name: "Rest", icon: "fa-campground", color: "blue", action: "rest" },
{ name: "Inventory", icon: "fa-backpack", color: "yellow", action: "showInventory" },
{ name: "Quests", icon: "fa-scroll", color: "purple", action: "showQuests" }
];
commonActions.forEach(action => {
const button = document.createElement('button');
button.className = `bg-${action.color}-600 hover:bg-${action.color}-700 text-white font-bold py-2 px-4 rounded flex items-center justify-center`;
button.innerHTML = `<i class="fas ${action.icon} mr-2"></i> ${action.name}`;
button.onclick = function() { handleAction(action.action); };
actionButtons.appendChild(button);
});
// Class-specific abilities
gameState.abilities.forEach(ability => {
const button = document.createElement('button');
button.className = `ability-btn bg-${gameState.character === 'mage' ? 'purple' : gameState.character === 'cleric' ? 'blue' : 'gray'}-600 hover:bg-${gameState.character === 'mage' ? 'purple' : gameState.character === 'cleric' ? 'blue' : 'gray'}-700 text-white font-bold py-2 px-4 rounded col-span-2 md:col-span-1`;
button.innerHTML = ability.name;
button.onclick = function() { useAbility(ability.name.toLowerCase().replace(' ', '-')); };
actionButtons.appendChild(button);
});
}
function handleAction(action) {
switch(action) {
case 'explore':
if (!gameState.inCombat) {
addToLog("You venture forth, looking for adventure...");
setTimeout(() => {
createRandomEncounter();
}, 1500);
} else {
addToLog("You can't explore while in combat!", 'red');
}
break;
case 'rest':
if (!gameState.inCombat) {
const hpRecovered = Math.min(Math.floor(gameState.maxHp * 0.3), gameState.maxHp - gameState.hp);
const manaRecovered = Math.min(Math.floor(gameState.maxMana * 0.5), gameState.maxMana - gameState.mana);
const staminaRecovered = Math.min(Math.floor(gameState.maxStamina * 0.7), gameState.maxStamina - gameState.stamina);
gameState.hp += hpRecovered;
gameState.mana += manaRecovered;
gameState.stamina += staminaRecovered;
addToLog(`You take a moment to rest and recover.`, 'blue');
if (hpRecovered > 0) addToLog(`+${hpRecovered} HP`);
if (manaRecovered > 0) addToLog(`+${manaRecovered} Mana`);
if (staminaRecovered > 0) addToLog(`+${staminaRecovered} Stamina`);
updateStats();
} else {
addToLog("You can't rest in the middle of combat!", 'red');
}
break;
case 'showInventory':
inventoryQuests.classList.remove('hidden');
enemyContainer.classList.add('hidden');
break;
case 'showQuests':
inventoryQuests.classList.remove('hidden');
enemyContainer.classList.add('hidden');
break;
}
}
function createRandomEncounter() {
if (gameState.inCombat) return;
const randomEnemyIndex = Math.floor(Math.random() * enemies.length);
gameState.currentEnemy = JSON.parse(JSON.stringify(enemies[randomEnemyIndex]));
gameState.inCombat = true;
enemyContainer.classList.remove('hidden');
inventoryQuests.classList.add('hidden');
enemyName.textContent = gameState.currentEnemy.name;
enemyHealth.textContent = `${gameState.currentEnemy.hp}/${gameState.currentEnemy.maxHp} HP`;
enemyHpFill.style.width = '100%';
enemyImage.innerHTML = `<img src="${gameState.currentEnemy.image}" alt="${gameState.currentEnemy.name}" class="h-48">`;
enemyDesc.textContent = gameState.currentEnemy.description;
addToLog(`You encounter a ${gameState.currentEnemy.name}!`, 'red');
}
function attackEnemy() {
if (!gameState.inCombat || !gameState.currentEnemy) return;
// Player attack
const attackRoll = rollDiceInBackground('d20');
const attackBonus = Math.floor(characterClasses[gameState.character].stats.str / 4);
setTimeout(() => {
if (attackRoll + attackBonus >= gameState.currentEnemy.ac) {
const damageRoll = rollDiceInBackground('d8');
setTimeout(() => {
const damage = damageRoll;
gameState.currentEnemy.hp -= damage;
if (gameState.currentEnemy.hp < 0) gameState.currentEnemy.hp = 0;
enemyHealth.textContent = `${gameState.currentEnemy.hp}/${gameState.currentEnemy.maxHp} HP`;
enemyHpFill.style.width = `${(gameState.currentEnemy.hp / gameState.currentEnemy.maxHp) * 100}%`;
addToLog(`You hit the ${gameState.currentEnemy.name} for ${damage} damage!`, 'yellow');
if (gameState.currentEnemy.hp <= 0) {
enemyDefeated();
return;
}
// Enemy attack
setTimeout(() => {
enemyAttack();
}, 1000);
}, 500);
} else {
addToLog(`Your attack misses the ${gameState.currentEnemy.name}!`, 'gray');
// Enemy attack
setTimeout(() => {
enemyAttack();
}, 1000);
}
}, 500);
}
function enemyAttack() {
if (!gameState.inCombat || !gameState.currentEnemy || gameState.currentEnemy.hp <= 0) return;
const attackRoll = rollDiceInBackground('d20');
setTimeout(() => {
if (attackRoll >= 10) { // Simple AC calculation
const damageRoll = rollDiceInBackground('d6');
setTimeout(() => {
const damage = damageRoll;
gameState.hp -= damage;
if (gameState.hp < 0) gameState.hp = 0;
updateStats();
addToLog(`The ${gameState.currentEnemy.name} hits you for ${damage} damage!`, 'red');
if (gameState.hp <= 0) {
gameOver();
}
}, 500);
} else {
addToLog(`The ${gameState.currentEnemy.name}'s attack misses you!`, 'gray');
}
}, 500);
}
function enemyDefeated() {
addToLog(`You defeated the ${gameState.currentEnemy.name}!`, 'green');
addToLog(`+${gameState.currentEnemy.xp} XP gained`, 'yellow');
gameState.xp += gameState.currentEnemy.xp;
gameState.enemiesDefeated++;
// Check for level up
if (gameState.xp >= gameState.xpToNextLevel) {
levelUp();
}
// Loot drop
const lootChance = Math.random();
if (lootChance > 0.7) {
const goldDrop = Math.floor(Math.random() * 20) + 10;
addToLog(`You found ${goldDrop} gold coins on the enemy!`, 'yellow');
const goldIndex = gameState.inventory.findIndex(item => item.name === "Gold");
if (goldIndex !== -1) {
gameState.inventory[goldIndex].quantity += goldDrop;
}
}
gameState.inCombat = false;
gameState.currentEnemy = null;
setTimeout(() => {
enemyContainer.classList.add('hidden');
// Auto progress quest if enemy was goblin
if (gameState.enemiesDefeated >= 3) {
gameState.quests[0].progress = 1;
gameState.quests[0].completed = true;
addToLog("Quest Complete: The Missing Blacksmith", 'yellow');
// Add reward
const goldIndex = gameState.inventory.findIndex(item => item.name === "Gold");
if (goldIndex !== -1) {
gameState.inventory[goldIndex].quantity += 50;
}
addToLog("You received 50 gold as a reward!", 'yellow');
addToLog("You complete the quest and return to the village.");
// Start next quest
setTimeout(() => {
addToLog(`New Quest: ${gameState.quests[1].name} - ${gameState.quests[1].description}`);
addToLog("Reward: " + gameState.quests[1].reward);
}, 1500);
}
}, 1500);
}
function useAbility(ability) {
if (!gameState.inCombat || !gameState.currentEnemy) {
addToLog("You can only use abilities in combat!", 'red');
return;
}
// Find the ability
const foundAbility = gameState.abilities.find(a => a.name.toLowerCase().replace(' ', '-') === ability);
if (!foundAbility) {
addToLog("Ability not found!", 'red');
return;
}
// Check resource
if (foundAbility.costType === 'mana' && gameState.mana < foundAbility.cost) {
addToLog(`Not enough mana to use ${foundAbility.name}!`, 'red');
return;
} else if (foundAbility.costType === 'stamina' && gameState.stamina < foundAbility.cost) {
addToLog(`Not enough stamina to use ${foundAbility.name}!`, 'red');
return;
}
// Deduct resource
if (foundAbility.costType === 'mana') {
gameState.mana -= foundAbility.cost;
} else {
gameState.stamina -= foundAbility.cost;
}
updateStats();
addToLog(`You use ${foundAbility.name}!`, 'blue');
// Simulate ability effect
if (ability === 'power-strike' || ability === 'backstab' || ability === 'smite') {
const damageRoll = ability === 'power-strike' ? rollDiceInBackground('2d8') :
ability === 'backstab' ? rollDiceInBackground('3d6') :
rollDiceInBackground('1d8 + 1d8');
setTimeout(() => {
const damage = damageRoll;
gameState.currentEnemy.hp -= damage;
if (gameState.currentEnemy.hp < 0) gameState.currentEnemy.hp = 0;
enemyHealth.textContent = `${gameState.currentEnemy.hp}/${gameState.currentEnemy.maxHp} HP`;
enemyHpFill.style.width = `${(gameState.currentEnemy.hp / gameState.currentEnemy.maxHp) * 100}%`;
addToLog(`${foundAbility.name} hits for ${damage} damage!`, 'yellow');
if (gameState.currentEnemy.hp <= 0) {
enemyDefeated();
return;
}
// Enemy attack
setTimeout(() => {
enemyAttack();
}, 1000);
}, 500);
} else if (ability === 'healing-light') {
const healRoll = rollDiceInBackground('2d8');
setTimeout(() => {
const healAmount = healRoll;
gameState.hp = Math.min(gameState.hp + healAmount, gameState.maxHp);
updateStats();
addToLog(`${foundAbility.name} heals you for ${healAmount} HP!`, 'green');
// Enemy attack
setTimeout(() => {
enemyAttack();
}, 1000);
}, 500);
} else if (ability === 'fireball') {
const damageRoll = rollDiceInBackground('3d6');
setTimeout(() => {
const damage = damageRoll;
gameState.currentEnemy.hp -= damage;
if (gameState.currentEnemy.hp < 0) gameState.currentEnemy.hp = 0;
enemyHealth.textContent = `${gameState.currentEnemy.hp}/${gameState.currentEnemy.maxHp} HP`;
enemyHpFill.style.width = `${(gameState.currentEnemy.hp / gameState.currentEnemy.maxHp) * 100}%`;
addToLog(`${foundAbility.name} burns the enemy for ${damage} damage!`, 'orange');
if (gameState.currentEnemy.hp <= 0) {
enemyDefeated();
return;
}
// Enemy attack
setTimeout(() => {
enemyAttack();
}, 1000);
}, 500);
} else {
// Default effect for other abilities
addToLog(`${foundAbility.name}: ${foundAbility.effect}`, 'blue');
// Enemy attack
setTimeout(() => {
enemyAttack();
}, 1000);
}
}
function useItem(itemName) {
const itemIndex = gameState.inventory.findIndex(item => item.name === itemName);
if (itemIndex === -1 || gameState.inventory[itemIndex].quantity <= 0) {
addToLog(`You don't have any ${itemName}`, 'red');
return;
}
if (itemName === "Health Potion") {
const healAmount = rollDiceInBackground('2d4+2');
gameState.inventory[itemIndex].quantity--;
setTimeout(() => {
gameState.hp = Math.min(gameState.hp + healAmount, gameState.maxHp);
updateStats();
addToLog(`You drink a Health Potion and recover ${healAmount} HP!`, 'green');
if (gameState.inventory[itemIndex].quantity <= 0) {
gameState.inventory.splice(itemIndex, 1);
}
}, 500);
} else {
addToLog(`You use the ${itemName}`, 'blue');
gameState.inventory[itemIndex].quantity--;
if (gameState.inventory[itemIndex].quantity <= 0) {
gameState.inventory.splice(itemIndex, 1);
}
}
}
function sellItems() {
let totalGold = 0;
// Sell all non-essential items
const itemsToSell = gameState.inventory.filter(item =>
item.name !== "Health Potion" &&
item.name !== "Gold" &&
item.name !== "Old Key"
);
if (itemsToSell.length === 0) {
addToLog("You have nothing valuable to sell!", 'gray');
return;
}
itemsToSell.forEach(item => {
let value = 0;
switch(item.name) {
case "Scroll of Fire":
value = 20;
break;
case "Rations":
value = 1 * item.quantity;
break;
case "Amethyst":
value = 25;
break;
default:
value = 10;
}
totalGold += value;
addToLog(`Sold ${item.quantity > 1 ? item.quantity + ' ' + item.name + 's' : item.name} for ${value} gold.`, 'yellow');
});
// Remove sold items
gameState.inventory = gameState.inventory.filter(item =>
item.name === "Health Potion" ||
item.name === "Gold" ||
item.name === "Old Key"
);
// Add gold to inventory
const goldIndex = gameState.inventory.findIndex(item => item.name === "Gold");
if (goldIndex !== -1) {
gameState.inventory[goldIndex].quantity += totalGold;
} else if (totalGold > 0) {
gameState.inventory.push({
name: "Gold",
icon: "fa-coins",
color: "yellow-500",
quantity: totalGold,
description: "Shiny gold coins for purchasing items."
});
}
addToLog(`Total gained: ${totalGold} gold`, 'yellow');
}
function showQuestDetails() {
addToLog("=== Active Quests ===", 'yellow');
gameState.quests.filter(q => !q.completed).forEach(quest => {
addToLog(`${quest.name}: ${quest.description}`);
addToLog(`Reward: ${quest.reward}`);
addToLog(`Progress: ${Math.floor(quest.progress * 100)}%`);
addToLog("----------------", 'gray');
});
const completedQuests = gameState.quests.filter(q => q.completed);
if (completedQuests.length > 0) {
addToLog("=== Completed Quests ===", 'green');
completedQuests.forEach(quest => {
addToLog(`${quest.name}`);
});
}
}
function levelUp() {
gameState.level++;
gameState.xp -= gameState.xpToNextLevel;
gameState.xpToNextLevel = Math.floor(gameState.xpToNextLevel * 1.5);
// Restore all resources
gameState.hp = gameState.maxHp;
gameState.mana = gameState.maxMana;
gameState.stamina = gameState.maxStamina;
// Increase max resources slightly
gameState.maxHp += 10;
gameState.maxMana += 5;
gameState.maxStamina += 8;
newLevel.textContent = gameState.level;
levelUpModal.classList.remove('hidden');
addToLog(`Congratulations! You reached level ${gameState.level}!`, 'yellow');
}
function levelUpAttribute(attribute) {
const character = characterClasses[gameState.character];
switch(attribute) {
case 'strength':
character.stats.str++;
addToLog("Your strength increases!", 'yellow');
break;
case 'dexterity':
character.stats.dex++;
addToLog("Your dexterity increases!", 'yellow');
break;
case 'intellect':
character.stats.int++;
addToLog("Your intellect increases!", 'yellow');
break;
}
updateStats();
levelUpModal.classList.add('hidden');
// Fully restore stats
gameState.hp = gameState.maxHp;
gameState.mana = gameState.maxMana;
gameState.stamina = gameState.maxStamina;
updateStats();
}
function gameOver() {
gameState.inCombat = false;
gameOverModal.classList.remove('hidden');
addToLog("You have been defeated...", 'red');
}
function retryGame() {
gameOverModal.classList.add('hidden');
startGame();
}
function newCharacter() {
gameOverModal.classList.add('hidden');
gameArea.classList.add('hidden');
characterSelection.classList.remove('hidden');
selectedCharacterInfo.classList.add('hidden');
// Remove selection highlight
document.querySelectorAll('.character-card').forEach(card => {
card.classList.remove('border-yellow-400', 'bg-gray-600');
});
gameState.character = null;
}
function rollDice(diceType) {
diceAnimation.classList.remove('hidden');
diceResultContainer.classList.add('hidden');
setTimeout(() => {
const result = rollDiceInBackground(diceType);
diceAnimation.classList.add('hidden');
diceResultContainer.classList.remove('hidden');
diceResult.textContent = result;
diceType.textContent = diceType;
addToLog(`Rolled ${diceType}: ${result}`, 'gray');
}, 1000);
}
function rollDiceInBackground(diceString) {
// Parse dice string like "2d8+3" or "d20"
const parts = diceString.split(/[d+-]/);
let numDice = 1;
let numSides = 20;
let modifier = 0;
if (parts[0] !== '') {
numDice = parseInt(parts[0]);
}
if (parts.length > 1) {
numSides = parseInt(parts[1]);
}
if (diceString.includes('+') && parts.length > 2) {
modifier = parseInt(parts[2]);
} else if (diceString.includes('-') && parts.length > 2) {
modifier = -parseInt(parts[2]);
}
let total = modifier;
for (let i = 0; i < numDice; i++) {
total += Math.floor(Math.random() * numSides) + 1;
}
return total;
}
function addToLog(message, color = 'white') {
const logEntry = document.createElement('div');
switch(color) {
case 'red':
logEntry.className = 'text-red-400';
break;
case 'green':
logEntry.className = 'text-green-400';
break;
case 'blue':
logEntry.className = 'text-blue-400';
break;
case 'yellow':
logEntry.className = 'text-yellow-400';
break;
case 'orange':
logEntry.className = 'text-orange-400';
break;
case 'purple':
logEntry.className = 'text-purple-400';
break;
case 'gray':
logEntry.className = 'text-gray-400';
break;
default:
logEntry.className = 'text-white';
}
logEntry.textContent = message;
gameLog.appendChild(logEntry);
// Auto-scroll to bottom
gameLog.scrollTop = gameLog.scrollHeight;
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=austrian11/austrian-game-idea" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>