|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Legend of the Crystal Kingdom</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script> |
|
<style> |
|
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&display=swap'); |
|
|
|
body { |
|
font-family: 'VT323', monospace; |
|
background-color: #0f172a; |
|
color: #e2e8f0; |
|
background-image: radial-gradient(#1e293b 1px, transparent 1px); |
|
background-size: 20px 20px; |
|
} |
|
|
|
.game-title { |
|
font-family: 'Press Start 2P', cursive; |
|
text-shadow: 0 0 10px #3b82f6, 0 0 20px #3b82f6; |
|
} |
|
|
|
.text-output { |
|
border-right: 3px solid #3b82f6; |
|
animation: blink 0.7s step-end infinite; |
|
} |
|
|
|
@keyframes blink { |
|
from, to { border-color: transparent } |
|
50% { border-color: #3b82f6 } |
|
} |
|
|
|
.pixel-art { |
|
image-rendering: pixelated; |
|
image-rendering: -moz-crisp-edges; |
|
image-rendering: crisp-edges; |
|
} |
|
|
|
.typewriter { |
|
overflow: hidden; |
|
white-space: pre-wrap; |
|
letter-spacing: 1px; |
|
animation: typing 3.5s steps(40, end); |
|
} |
|
|
|
@keyframes typing { |
|
from { width: 0 } |
|
to { width: 100% } |
|
} |
|
|
|
.scrollbar-hide::-webkit-scrollbar { |
|
display: none; |
|
} |
|
|
|
.scrollbar-hide { |
|
-ms-overflow-style: none; |
|
scrollbar-width: none; |
|
} |
|
|
|
.rainbow-text { |
|
background-image: linear-gradient(to left, violet, indigo, blue, green, yellow, orange, red); |
|
-webkit-background-clip: text; |
|
background-clip: text; |
|
color: transparent; |
|
} |
|
</style> |
|
</head> |
|
<body class="min-h-screen flex flex-col items-center justify-center p-4"> |
|
<div class="w-full max-w-3xl bg-gray-900 rounded-lg shadow-xl overflow-hidden border-2 border-blue-500"> |
|
|
|
<div class="bg-gradient-to-r from-blue-900 to-indigo-900 p-4 flex items-center justify-between border-b-2 border-blue-500"> |
|
<h1 class="game-title text-2xl md:text-3xl text-blue-400">✨ Legend of the Crystal Kingdom ✨</h1> |
|
<div class="flex space-x-2"> |
|
<div class="w-3 h-3 rounded-full bg-red-500"></div> |
|
<div class="w-3 h-3 rounded-full bg-yellow-500"></div> |
|
<div class="w-3 h-3 rounded-full bg-green-500"></div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="flex flex-col md:flex-row h-96"> |
|
|
|
<div class="w-full md:w-1/3 bg-gray-800 p-4 flex items-center justify-center border-b-2 md:border-b-0 md:border-r-2 border-blue-500"> |
|
<div id="game-graphic" class="pixel-art w-40 h-40 bg-gray-700 rounded flex items-center justify-center text-4xl"> |
|
<i class="fas fa-dragon text-red-500"></i> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="w-full md:w-2/3 bg-gray-800 p-4 overflow-y-auto scrollbar-hide"> |
|
<div id="game-text" class="text-xl text-gray-200 leading-relaxed"> |
|
<p class="typewriter rainbow-text">Welcome, brave adventurer!</p> |
|
<p class="typewriter text-blue-300 mt-2">The Crystal Kingdom needs your help...</p> |
|
<p class="typewriter text-purple-300 mt-2">Dark forces have stolen the Sacred Crystals!</p> |
|
<p class="typewriter text-green-300 mt-2">Will you embark on this epic quest?</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="bg-gray-900 p-4 border-t-2 border-blue-500"> |
|
<div class="flex items-center"> |
|
<span class="text-green-400 mr-2 text-xl">></span> |
|
<input id="user-input" type="text" class="flex-1 bg-gray-800 text-gray-200 border-2 border-blue-500 rounded px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="What will you do?" autofocus> |
|
<button id="submit-btn" class="ml-2 bg-blue-600 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-200"> |
|
<i class="fas fa-paper-plane"></i> |
|
</button> |
|
</div> |
|
<div class="mt-2 flex space-x-2"> |
|
<button class="command-hint bg-gray-700 hover:bg-gray-600 text-gray-300 px-2 py-1 rounded text-sm" data-command="look">look</button> |
|
<button class="command-hint bg-gray-700 hover:bg-gray-600 text-gray-300 px-2 py-1 rounded text-sm" data-command="inventory">inventory</button> |
|
<button class="command-hint bg-gray-700 hover:bg-gray-600 text-gray-300 px-2 py-1 rounded text-sm" data-command="help">help</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<audio id="bg-music" loop> |
|
<source src="https://assets.mixkit.co/sfx/preview/mixkit-game-show-suspense-waiting-668.mp3" type="audio/mpeg"> |
|
</audio> |
|
<audio id="type-sound"> |
|
<source src="https://assets.mixkit.co/sfx/preview/mixkit-quick-jump-arcade-game-239.mp3" type="audio/mpeg"> |
|
</audio> |
|
|
|
<script> |
|
// Game State |
|
const gameState = { |
|
currentScene: 'start', |
|
inventory: [], |
|
health: 100, |
|
gold: 50, |
|
hasSword: false, |
|
hasShield: false, |
|
crystalsFound: 0, |
|
metWizard: false, |
|
dragonDefeated: false |
|
}; |
|
|
|
// Game Scenes |
|
const scenes = { |
|
start: { |
|
text: `<p class="text-yellow-300">You stand at the gates of the Crystal Kingdom. The once vibrant land now lies under a dark shadow.</p> |
|
<p class="text-blue-300">To the <span class="text-green-400 font-bold">north</span>, the Enchanted Forest whispers secrets.</p> |
|
<p class="text-red-300">To the <span class="text-green-400 font-bold">east</span>, the Dragon's Peak smolders ominously.</p> |
|
<p class="text-purple-300">To the <span class="text-green-400 font-bold">west</span>, the Mystic Caves beckon with ancient magic.</p> |
|
<p class="text-pink-300">In the town <span class="text-green-400 font-bold">square</span>, villagers gather nervously.</p>`, |
|
graphic: '<i class="fas fa-chess-rook text-blue-400"></i>', |
|
options: { |
|
'north': 'forest', |
|
'east': 'dragonPeak', |
|
'west': 'caves', |
|
'square': 'townSquare', |
|
'look': 'startLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
forest: { |
|
text: `<p class="text-green-400">Tall, ancient trees surround you. Their leaves glow faintly in the dim light.</p> |
|
<p class="text-yellow-300">A sparkling <span class="text-blue-400 font-bold">stream</span> trickles nearby.</p> |
|
<p class="text-red-400">Something moves in the <span class="text-blue-400 font-bold">bushes</span>...</p> |
|
<p class="text-purple-300">The path back to the <span class="text-blue-400 font-bold">gates</span> is clear.</p>`, |
|
graphic: '<i class="fas fa-tree text-green-500"></i>', |
|
options: { |
|
'stream': 'forestStream', |
|
'bushes': 'forestBushes', |
|
'gates': 'start', |
|
'look': 'forestLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
forestStream: { |
|
text: `<p class="text-blue-300">The crystal-clear water reveals colorful fish swimming beneath the surface.</p> |
|
<p class="text-yellow-300">You notice something <span class="text-blue-400 font-bold">shiny</span> in the water.</p> |
|
<p class="text-green-400">The <span class="text-blue-400 font-bold">forest</span> awaits your return.</p>`, |
|
graphic: '<i class="fas fa-water text-blue-300"></i>', |
|
options: { |
|
'take shiny': 'takeCrystal', |
|
'forest': 'forest', |
|
'look': 'streamLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
takeCrystal: { |
|
text: `<p class="rainbow-text">You reach into the water and pull out a glowing crystal!</p> |
|
<p class="text-green-400">The water spirit whispers: "One of four sacred crystals has been found."</p> |
|
<p class="text-yellow-300">Return to the <span class="text-blue-400 font-bold">forest</span>?</p>`, |
|
graphic: '<i class="fas fa-gem text-purple-500"></i>', |
|
onEnter: () => { |
|
gameState.crystalsFound++; |
|
gameState.inventory.push('Water Crystal'); |
|
playSound('success'); |
|
}, |
|
options: { |
|
'forest': 'forest', |
|
'look': 'crystalLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
// More scenes would continue here... |
|
townSquare: { |
|
text: `<p class="text-yellow-300">Villagers mill about nervously. The blacksmith's shop stands to your <span class="text-blue-400 font-bold">left</span>.</p> |
|
<p class="text-purple-300">The old <span class="text-blue-400 font-bold">wizard</span> watches you from his tower.</p> |
|
<p class="text-green-400">The town <span class="text-blue-400 font-bold">gates</span> are behind you.</p>`, |
|
graphic: '<i class="fas fa-users text-yellow-400"></i>', |
|
options: { |
|
'left': 'blacksmith', |
|
'wizard': 'wizardTower', |
|
'gates': 'start', |
|
'look': 'squareLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
blacksmith: { |
|
text: `<p class="text-orange-400">The blacksmith wipes sweat from his brow. "I can sell you weapons, but gold is scarce these days."</p> |
|
<p class="text-yellow-300">A sturdy <span class="text-blue-400 font-bold">sword</span> costs 30 gold.</p> |
|
<p class="text-blue-300">A reliable <span class="text-blue-400 font-bold">shield</span> costs 20 gold.</p> |
|
<p class="text-green-400">Return to the <span class="text-blue-400 font-bold">square</span>?</p>`, |
|
graphic: '<i class="fas fa-hammer text-orange-500"></i>', |
|
options: { |
|
'buy sword': 'buySword', |
|
'buy shield': 'buyShield', |
|
'square': 'townSquare', |
|
'look': 'blacksmithLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
buySword: { |
|
text: gameState.gold >= 30 |
|
? `<p class="text-blue-300">"A fine choice!" says the blacksmith as he hands you the sword.</p> |
|
<p class="text-yellow-300">You feel more confident in battle now.</p> |
|
<p class="text-green-400">Return to the <span class="text-blue-400 font-bold">square</span>?</p>` |
|
: `<p class="text-red-400">"You don't have enough gold," the blacksmith frowns.</p> |
|
<p class="text-yellow-300">You need ${30 - gameState.gold} more gold pieces.</p> |
|
<p class="text-green-400">Return to the <span class="text-blue-400 font-bold">square</span>?</p>`, |
|
graphic: '<i class="fas fa-sword text-gray-300"></i>', |
|
onEnter: () => { |
|
if (gameState.gold >= 30) { |
|
gameState.hasSword = true; |
|
gameState.gold -= 30; |
|
playSound('success'); |
|
} else { |
|
playSound('error'); |
|
} |
|
}, |
|
options: { |
|
'square': 'townSquare', |
|
'look': 'swordLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
// More game scenes would continue... |
|
dragonPeak: { |
|
text: gameState.dragonDefeated |
|
? `<p class="text-green-400">The defeated dragon lies motionless. You've reclaimed the Fire Crystal!</p> |
|
<p class="text-yellow-300">The path back to the <span class="text-blue-400 font-bold">gates</span> is clear.</p>` |
|
: `<p class="text-red-500">A massive dragon blocks your path, its scales glowing like embers!</p> |
|
<p class="text-yellow-300">In its claws, you see the glowing <span class="text-red-400 font-bold">Fire Crystal</span>.</p> |
|
<p class="text-purple-300">Will you <span class="text-blue-400 font-bold">fight</span> or <span class="text-blue-400 font-bold">flee</span>?</p>`, |
|
graphic: gameState.dragonDefeated |
|
? '<i class="fas fa-skull text-gray-400"></i>' |
|
: '<i class="fas fa-dragon text-red-500"></i>', |
|
options: { |
|
'fight': 'dragonFight', |
|
'flee': 'start', |
|
'gates': 'start', |
|
'look': 'dragonLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
dragonFight: { |
|
text: gameState.hasSword |
|
? `<p class="text-green-400">With a mighty swing of your sword, you strike the dragon down!</p> |
|
<p class="rainbow-text">The Fire Crystal is yours! (2/4 crystals found)</p> |
|
<p class="text-yellow-300">Return to the <span class="text-blue-400 font-bold">gates</span>?</p>` |
|
: `<p class="text-red-500">Without a proper weapon, the dragon's fire overwhelms you!</p> |
|
<p class="text-yellow-300">You barely escape with your life (-30 health).</p> |
|
<p class="text-purple-300">The path back to the <span class="text-blue-400 font-bold">gates</span> is clear.</p>`, |
|
graphic: gameState.hasSword |
|
? '<i class="fas fa-trophy text-yellow-500"></i>' |
|
: '<i class="fas fa-heart-broken text-red-500"></i>', |
|
onEnter: () => { |
|
if (gameState.hasSword) { |
|
gameState.dragonDefeated = true; |
|
gameState.crystalsFound++; |
|
gameState.inventory.push('Fire Crystal'); |
|
playSound('victory'); |
|
} else { |
|
gameState.health -= 30; |
|
if (gameState.health <= 0) { |
|
setTimeout(() => changeScene('gameOver'), 1000); |
|
} |
|
playSound('hurt'); |
|
} |
|
}, |
|
options: { |
|
'gates': 'start', |
|
'look': 'fightLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
// Special scenes |
|
showInventory: { |
|
text: `<p class="text-yellow-300">Health: ${gameState.health}/100</p> |
|
<p class="text-blue-300">Gold: ${gameState.gold}</p> |
|
<p class="text-green-400">Crystals Found: ${gameState.crystalsFound}/4</p> |
|
<p class="text-purple-300">Inventory: ${gameState.inventory.length > 0 ? gameState.inventory.join(', ') : 'Empty'}</p> |
|
<p class="text-pink-300">Return to <span class="text-blue-400 font-bold">previous</span> location?</p>`, |
|
graphic: '<i class="fas fa-backpack text-brown-500"></i>', |
|
options: { |
|
'previous': () => gameState.currentScene, |
|
'look': 'inventoryLook', |
|
'help': 'help' |
|
} |
|
}, |
|
|
|
help: { |
|
text: `<p class="text-green-400">Available commands:</p> |
|
<p class="text-yellow-300"><span class="text-blue-400 font-bold">look</span> - Examine your surroundings</p> |
|
<p class="text-purple-300"><span class="text-blue-400 font-bold">go [direction]</span> - Move in a direction (north, south, east, west)</p> |
|
<p class="text-pink-300"><span class="text-blue-400 font-bold">take [item]</span> - Pick up an item</p> |
|
<p class="text-blue-300"><span class="text-blue-400 font-bold">use [item]</span> - Use an item from inventory</p> |
|
<p class="text-red-300"><span class="text-blue-400 font-bold">talk to [character]</span> - Interact with characters</p> |
|
<p class="text-yellow-300"><span class="text-blue-400 font-bold">inventory</span> - Check your belongings</p> |
|
<p class="text-green-400">Return to <span class="text-blue-400 font-bold">previous</span> location?</p>`, |
|
graphic: '<i class="fas fa-question-circle text-blue-400"></i>', |
|
options: { |
|
'previous': () => gameState.currentScene, |
|
'look': 'helpLook', |
|
'inventory': 'showInventory' |
|
} |
|
}, |
|
|
|
gameOver: { |
|
text: `<p class="text-red-500 text-2xl">GAME OVER</p> |
|
<p class="text-yellow-300">Your journey ends here, brave adventurer...</p> |
|
<p class="text-blue-300">But the kingdom needs heroes!</p> |
|
<p class="text-green-400">Would you like to <span class="text-blue-400 font-bold">restart</span> your quest?</p>`, |
|
graphic: '<i class="fas fa-skull-crossbones text-red-500"></i>', |
|
options: { |
|
'restart': 'restartGame' |
|
} |
|
}, |
|
|
|
// More scenes would be added for the full game... |
|
}; |
|
|
|
// DOM Elements |
|
const gameText = document.getElementById('game-text'); |
|
const gameGraphic = document.getElementById('game-graphic'); |
|
const userInput = document.getElementById('user-input'); |
|
const submitBtn = document.getElementById('submit-btn'); |
|
const commandHints = document.querySelectorAll('.command-hint'); |
|
|
|
// Audio Elements |
|
const bgMusic = document.getElementById('bg-music'); |
|
const typeSound = document.getElementById('type-sound'); |
|
|
|
// Initialize game |
|
function initGame() { |
|
bgMusic.volume = 0.3; |
|
bgMusic.play(); |
|
changeScene('start'); |
|
|
|
// Event listeners |
|
submitBtn.addEventListener('click', handleInput); |
|
userInput.addEventListener('keypress', (e) => { |
|
if (e.key === 'Enter') handleInput(); |
|
}); |
|
|
|
commandHints.forEach(button => { |
|
button.addEventListener('click', (e) => { |
|
userInput.value = e.target.dataset.command; |
|
handleInput(); |
|
}); |
|
}); |
|
} |
|
|
|
// Handle user input |
|
function handleInput() { |
|
const input = userInput.value.trim().toLowerCase(); |
|
userInput.value = ''; |
|
|
|
if (!input) return; |
|
|
|
// Add to game text |
|
addText(`<p class="text-gray-400">> ${input}</p>`); |
|
|
|
// Find matching command |
|
const currentScene = scenes[gameState.currentScene]; |
|
let matched = false; |
|
|
|
for (const [command, destination] of Object.entries(currentScene.options)) { |
|
if (input.includes(command)) { |
|
matched = true; |
|
|
|
if (typeof destination === 'function') { |
|
changeScene(destination()); |
|
} else { |
|
changeScene(destination); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
if (!matched) { |
|
addText('<p class="text-red-400">I don\'t understand that command. Try "help" for options.</p>'); |
|
playSound('error'); |
|
} |
|
} |
|
|
|
// Change scene |
|
function changeScene(sceneId) { |
|
if (!scenes[sceneId]) { |
|
console.error(`Scene ${sceneId} not found!`); |
|
return; |
|
} |
|
|
|
gameState.currentScene = sceneId; |
|
const scene = scenes[sceneId]; |
|
|
|
// Run onEnter if it exists |
|
if (scene.onEnter) { |
|
scene.onEnter(); |
|
} |
|
|
|
// Update display |
|
gameGraphic.innerHTML = scene.graphic; |
|
|
|
// Clear and add new text with typing effect |
|
gameText.innerHTML = ''; |
|
addTextWithTyping(scene.text); |
|
|
|
// Update health display if needed |
|
if (gameState.health <= 0) { |
|
setTimeout(() => changeScene('gameOver'), 1000); |
|
} |
|
} |
|
|
|
// Add text with typing effect |
|
function addTextWithTyping(text) { |
|
const paragraphs = text.split('</p>').filter(p => p.trim()); |
|
|
|
paragraphs.forEach((p, index) => { |
|
setTimeout(() => { |
|
const para = document.createElement('div'); |
|
para.innerHTML = p + '</p>'; |
|
gameText.appendChild(para); |
|
playSound('type'); |
|
|
|
// Scroll to bottom |
|
gameText.scrollTop = gameText.scrollHeight; |
|
}, index * 1000); |
|
}); |
|
} |
|
|
|
// Add text immediately |
|
function addText(text) { |
|
const para = document.createElement('div'); |
|
para.innerHTML = text; |
|
gameText.appendChild(para); |
|
|
|
// Scroll to bottom |
|
gameText.scrollTop = gameText.scrollHeight; |
|
} |
|
|
|
// Play sound effects |
|
function playSound(type) { |
|
if (type === 'type') { |
|
typeSound.currentTime = 0; |
|
typeSound.play(); |
|
} |
|
// Other sounds would be handled here... |
|
} |
|
|
|
// Start the game |
|
window.onload = initGame; |
|
</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=sizzlebop/create-with-deepseek" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html> |