RPG
GLM 4.6V GGUF · full

Browser check: Artifact runtime error. This is a technical check, not a quality review.
Reviews & generation details
Reviews
No human review yet. Unreviewed does not mean unsuccessful.
Generation context
Imported historical artifact. Its exact original instructions and lineage are not known.
Generation prompt · exact text unavailable
See the current prompt for reference. It may differ from the instructions used to generate this artifact.
Inspect original source 42,140 bytes · SHA-256 c2fcf3a80fc4
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Adventure Quest - Action RPG</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #1a1a2e;
color: #fff;
overflow: hidden;
cursor: crosshair;
}
#gameCanvas {
display: block;
background: linear-gradient(180deg, #0f0f1e 0%, #1a1a2e 100%);
}
#ui {
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 20px;
pointer-events: none;
z-index: 10;
}
#healthContainer {
display: flex;
gap: 10px;
margin-bottom: 10px;
}
.heart {
width: 30px;
height: 30px;
background: #ff4757;
clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
transition: all 0.3s ease;
box-shadow: 0 0 10px rgba(255, 71, 87, 0.5);
}
.heart.empty {
background: #2f3640;
opacity: 0.3;
box-shadow: none;
}
#inventory {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(0, 0, 0, 0.8);
padding: 15px;
border-radius: 10px;
display: flex;
gap: 10px;
border: 2px solid #7f8c8d;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
}
.inventory-slot {
width: 50px;
height: 50px;
background: rgba(255, 255, 255, 0.1);
border: 2px solid #7f8c8d;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
transition: all 0.2s ease;
}
.inventory-slot:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.05);
}
#money {
position: absolute;
top: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
padding: 10px 20px;
border-radius: 10px;
font-size: 18px;
border: 2px solid #f1c40f;
box-shadow: 0 0 20px rgba(241, 196, 15, 0.3);
}
#shop {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.95);
padding: 30px;
border-radius: 15px;
display: none;
z-index: 100;
border: 3px solid #3498db;
box-shadow: 0 0 30px rgba(52, 152, 219, 0.5);
}
#shop h2 {
margin-bottom: 20px;
text-align: center;
color: #3498db;
text-shadow: 0 0 10px rgba(52, 152, 219, 0.5);
}
.shop-item {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 0;
padding: 10px;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
transition: all 0.2s ease;
}
.shop-item:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateX(5px);
}
.shop-item button {
padding: 5px 15px;
background: #3498db;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
transition: all 0.2s ease;
}
.shop-item button:hover {
background: #2980b9;
transform: scale(1.05);
}
#savePoint {
position: absolute;
bottom: 20px;
right: 20px;
background: rgba(0, 0, 0, 0.8);
padding: 10px 20px;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
border: 2px solid #2ecc71;
box-shadow: 0 0 20px rgba(46, 204, 113, 0.3);
}
#savePoint:hover {
background: rgba(46, 204, 113, 0.3);
transform: scale(1.05);
box-shadow: 0 0 30px rgba(46, 204, 113, 0.5);
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.95);
padding: 30px;
border-radius: 15px;
text-align: center;
display: none;
z-index: 200;
border: 3px solid #e74c3c;
box-shadow: 0 0 30px rgba(231, 76, 60, 0.5);
}
#gameOver button {
margin-top: 20px;
padding: 10px 20px;
background: #3498db;
border: none;
border-radius: 5px;
color: white;
cursor: pointer;
font-size: 16px;
transition: all 0.2s ease;
}
#gameOver button:hover {
background: #2980b9;
transform: scale(1.05);
}
.damage-text {
position: absolute;
color: #ff4757;
font-weight: bold;
font-size: 24px;
pointer-events: none;
animation: damageFloat 1s ease-out forwards;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
@keyframes damageFloat {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(-50px);
}
}
/* Add visual feedback for stamina */
.stamina-bar {
position: absolute;
bottom: 20px;
right: 20px;
width: 150px;
height: 20px;
background: rgba(0, 0, 0, 0.7);
border-radius: 10px;
overflow: hidden;
border: 2px solid #7f8c8d;
}
.stamina-fill {
height: 100%;
background: linear-gradient(90deg, #2ecc71, #27ae60);
transition: width 0.3s ease;
}
.stamina-empty {
background: #e74c3c;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="ui">
<div id="healthContainer"></div>
<div id="money">Gold: 0</div>
</div>
<div id="inventory">
<div class="inventory-slot" data-slot="0"></div>
<div class="inventory-slot" data-slot="1"></div>
<div class="inventory-slot" data-slot="2"></div>
<div class="inventory-slot" data-slot="3"></div>
<div class="inventory-slot" data-slot="4"></div>
</div>
<div class="stamina-bar">
<div class="stamina-fill" id="staminaFill"></div>
</div>
<div id="savePoint">Save Point</div>
<div id="shop">
<h2>Item Shop</h2>
<div id="shopItems"></div>
</div>
<div id="gameOver">
<h2>Game Over</h2>
<p>You have been defeated!</p>
<button onclick="restartGame()">Restart</button>
</div>
<script>
// Game Configuration
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Player State & Visuals
const gameState = {
player: {
x: canvas.width / 2,
y: canvas.height / 2,
width: 40,
height: 40,
health: 6,
maxHealth: 6,
healthContainers: 3,
maxHealthContainers: 5,
speed: 3,
stamina: 100,
maxStamina: 100,
weapon: 'sword',
weaponLevel: 0,
hasRanged: false,
money: 0,
inventory: [],
facing: 'right',
isWalking: false,
walkFrame: 0,
lastWalkTime: 0
},
keys: {},
enemies: [],
items: [],
particles: [],
shops: [],
savePoints: [],
currentArea: 0,
gameProgress: 0,
isShopOpen: false,
isGameOver: false,
lastAttackTime: 0
};
// Sound Effects
const sounds = {
walk: new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAAD//w=='),
attack: new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAAD//w=='),
damage: new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAAD//w=='),
pickup: new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAAD//w=='),
shop: new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAAD//w=='),
save: new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoAAAD//w==')
};
// Initialize sound effects (using base64 encoded empty sounds as placeholders)
Object.values(sounds).forEach(sound => {
sound.volume = 0.3;
});
// Item Definitions
const itemDefinitions = {
potion: { name: 'Health Potion', icon: '❤️', effect: 'heal', value: 20 },
key: { name: 'Dungeon Key', icon: '🔑', effect: 'key', value: 0 },
coin: { name: 'Gold Coin', icon: '💰', effect: 'money', value: 10 },
shield: { name: 'Iron Shield', icon: '🛡️', effect: 'defense', value: 0 },
bomb: { name: 'Bomb', icon: '💣', effect: 'bomb', value: 0 }
};
// Weapon Definitions
const weapons = {
sword: {
name: 'Iron Sword',
damage: 2,
attackSpeed: 1000,
range: 50,
color: '#8e44ad',
level: 0,
description: 'A basic iron sword'
},
sword2: {
name: 'Steel Sword',
damage: 4,
attackSpeed: 800,
range: 60,
color: '#3498db',
level: 1,
description: 'A stronger steel sword'
},
sword3: {
name: 'Diamond Sword',
damage: 7,
attackSpeed: 600,
range: 70,
color: '#f1c40f',
level: 2,
description: 'A powerful diamond sword'
},
bow: {
name: 'Wooden Bow',
damage: 3,
attackSpeed: 1500,
range: 150,
color: '#27ae60',
level: 0,
description: 'A ranged wooden bow'
}
};
// Shop Items
const shopItems = [
{ name: 'Health Potion', price: 20, icon: '❤️', effect: 'heal' },
{ name: 'Health Container', price: 100, icon: '💚', effect: 'healthContainer' },
{ name: 'Steel Sword', price: 150, icon: '⚔️', effect: 'weaponUpgrade' },
{ name: 'Diamond Sword', price: 300, icon: '⚔️', effect: 'weaponUpgrade' },
{ name: 'Wooden Bow', price: 200, icon: '🏹', effect: 'rangedWeapon' }
];
// Initialize Game
function init() {
setupEventListeners();
createInitialWorld();
updateUI();
gameLoop();
}
// Event Listeners
function setupEventListeners() {
window.addEventListener('keydown', (e) => {
gameState.keys[e.key.toLowerCase()] = true;
// Visual feedback for key presses
if (e.key === ' ') {
e.preventDefault();
attack();
}
if (e.key === 'i') {
openShop();
}
if (e.key === 's') {
saveGame();
}
});
window.addEventListener('keyup', (e) => {
gameState.keys[e.key.toLowerCase()] = false;
});
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gameState.player.x = Math.min(gameState.player.x, canvas.width - gameState.player.width);
gameState.player.y = Math.min(gameState.player.y, canvas.height - gameState.player.height);
});
document.getElementById('savePoint').addEventListener('click', saveGame);
}
// Create Initial World
function createInitialWorld() {
// Create enemies with varying types and difficulties
for (let i = 0; i < 5; i++) {
const enemyTypes = ['goblin', 'orc', 'skeleton'];
const enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
let health, damage, speed;
switch(enemyType) {
case 'goblin':
health = 3;
damage = 1;
speed = 1;
break;
case 'orc':
health = 5;
damage = 2;
speed = 0.8;
break;
case 'skeleton':
health = 4;
damage = 1.5;
speed = 1.2;
break;
}
gameState.enemies.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
width: 30,
height: 30,
health: health,
maxHealth: health,
speed: speed,
damage: damage,
type: enemyType
});
}
// Create items with better distribution
gameState.items.push({
x: 200,
y: 200,
type: 'potion',
collected: false
});
gameState.items.push({
x: 400,
y: 300,
type: 'coin',
collected: false
});
gameState.items.push({
x: 600,
y: 150,
type: 'shield',
collected: false
});
gameState.items.push({
x: 300,
y: 450,
type: 'bomb',
collected: false
});
gameState.items.push({
x: 700,
y: 400,
type: 'key',
collected: false
});
// Create save point
gameState.savePoints.push({
x: canvas.width / 2,
y: canvas.height / 2,
radius: 50
});
// Create shop
gameState.shops.push({
x: canvas.width - 200,
y: canvas.height - 200,
radius: 60
});
// Create additional save points for progression
gameState.savePoints.push({
x: canvas.width / 4,
y: canvas.height / 4,
radius: 50
});
gameState.savePoints.push({
x: (canvas.width * 3) / 4,
y: (canvas.height * 3) / 4,
radius: 50
});
}
// Game Loop
function gameLoop() {
if (gameState.isGameOver) return;
update();
render();
// Limit frame rate for better performance
setTimeout(() => {
requestAnimationFrame(gameLoop);
}, 16); // ~60 FPS
}
// Update Game State
function update() {
updatePlayer();
updateEnemies();
updateItems();
updateParticles();
checkCollisions();
updateProgression();
}
// Update Player
function updatePlayer() {
let dx = 0;
let dy = 0;
let isMoving = false;
if (gameState.keys['a'] || gameState.keys['arrowleft']) {
dx = -gameState.player.speed;
gameState.player.facing = 'left';
isMoving = true;
}
if (gameState.keys['d'] || gameState.keys['arrowright']) {
dx = gameState.player.speed;
gameState.player.facing = 'right';
isMoving = true;
}
if (gameState.keys['w'] || gameState.keys['arrowup']) {
dy = -gameState.player.speed;
isMoving = true;
}
if (gameState.keys['s'] || gameState.keys['arrowdown']) {
dy = gameState.player.speed;
isMoving = true;
}
// Update walking animation
if (isMoving) {
gameState.player.isWalking = true;
const now = Date.now();
if (now - gameState.player.lastWalkTime > 150) {
gameState.player.walkFrame = (gameState.player.walkFrame + 1) % 4;
gameState.player.lastWalkTime = now;
}
} else {
gameState.player.isWalking = false;
gameState.player.walkFrame = 0;
}
// Update position
gameState.player.x += dx;
gameState.player.y += dy;
// Keep player in bounds
gameState.player.x = Math.max(0, Math.min(canvas.width - gameState.player.width, gameState.player.x));
gameState.player.y = Math.max(0, Math.min(canvas.height - gameState.player.height, gameState.player.y));
// Update stamina
if (isMoving) {
gameState.player.stamina = Math.max(0, gameState.player.stamina - 0.5);
} else {
gameState.player.stamina = Math.min(gameState.player.maxStamina, gameState.player.stamina + 0.2);
}
}
// Update Enemies
function updateEnemies() {
gameState.enemies.forEach(enemy => {
// Simple AI: move towards player
const dx = gameState.player.x - enemy.x;
const dy = gameState.player.y - enemy.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
enemy.x += (dx / distance) * enemy.speed;
enemy.y += (dy / distance) * enemy.speed;
}
});
}
// Update Items
function updateItems() {
gameState.items.forEach(item => {
if (!item.collected) {
// Check if player is near item
const dx = gameState.player.x - item.x;
const dy = gameState.player.y - item.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 30) {
collectItem(item);
}
}
});
}
// Update Particles
function updateParticles() {
gameState.particles = gameState.particles.filter(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
particle.life -= 0.02;
return particle.life > 0;
});
}
// Check Collisions
function checkCollisions() {
// Enemy collisions
gameState.enemies.forEach(enemy => {
const dx = gameState.player.x - enemy.x;
const dy = gameState.player.y - enemy.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 30) {
takeDamage(enemy.damage);
}
});
// Shop interaction
gameState.shops.forEach(shop => {
const dx = gameState.player.x - shop.x;
const dy = gameState.player.y - shop.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shop.radius) {
if (!gameState.isShopOpen) {
openShop();
}
}
});
// Save point interaction
gameState.savePoints.forEach(savePoint => {
const dx = gameState.player.x - savePoint.x;
const dy = gameState.player.y - savePoint.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < savePoint.radius) {
saveGame();
createNotification('Progress saved at this location!');
}
});
// Update game progress based on actions
gameState.gameProgress += 0.1;
}
// Update Progression
function updateProgression() {
// Check for mid-game progression (ranged weapon discovery)
if (gameState.gameProgress >= 50 && !gameState.hasRanged) {
gameState.hasRanged = true;
gameState.player.weapon = 'bow';
createNotification('You discovered a Wooden Bow! Press Space to shoot arrows.');
createParticles(gameState.player.x, gameState.player.y, '#27ae60');
}
// Check for late-game progression
if (gameState.gameProgress >= 100 && gameState.player.healthContainers < gameState.player.maxHealthContainers) {
createNotification('You feel stronger! Maximum health increased!');
gameState.player.healthContainers++;
gameState.player.maxHealth = gameState.player.healthContainers * 2;
gameState.player.health = gameState.player.maxHealth;
updateUI();
}
}
// Attack
function attack() {
const now = Date.now();
if (now - gameState.lastAttackTime < weapons[gameState.player.weapon].attackSpeed) return;
gameState.lastAttackTime = now;
const weapon = weapons[gameState.player.weapon];
const attackRange = weapon.range;
// Play attack sound
sounds.attack.play().catch(() => {});
// Create attack effect
createAttackEffect();
// Check for enemy hits
gameState.enemies.forEach(enemy => {
const dx = enemy.x - gameState.player.x;
const dy = enemy.y - gameState.player.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < attackRange) {
enemy.health -= weapon.damage;
createDamageText(enemy.x, enemy.y, weapon.damage);
if (enemy.health <= 0) {
// Enemy defeated
createParticles(enemy.x, enemy.y, '#ff4757');
gameState.enemies.splice(gameState.enemies.indexOf(enemy), 1);
gameState.player.money += 10;
updateUI();
}
}
});
}
// Take Damage
function takeDamage(amount) {
gameState.player.health -= amount;
createDamageText(gameState.player.x, gameState.player.y, amount);
createParticles(gameState.player.x, gameState.player.y, '#ff4757');
// Play damage sound
sounds.damage.play().catch(() => {});
if (gameState.player.health <= 0) {
gameOver();
}
updateUI();
}
// Collect Item
function collectItem(item) {
item.collected = true;
createParticles(item.x, item.y, '#2ecc71');
createNotification(`Collected ${itemDefinitions[item.type].name}`);
// Play pickup sound
sounds.pickup.play().catch(() => {});
switch (item.type) {
case 'potion':
gameState.player.health = Math.min(gameState.player.maxHealth, gameState.player.health + itemDefinitions[item.type].value);
break;
case 'coin':
gameState.player.money += itemDefinitions[item.type].value;
break;
case 'key':
gameState.player.inventory.push(item.type);
break;
case 'shield':
gameState.player.inventory.push(item.type);
break;
case 'bomb':
gameState.player.inventory.push(item.type);
break;
}
updateUI();
}
// Create Attack Effect
function createAttackEffect() {
const weapon = weapons[gameState.player.weapon];
const direction = gameState.player.facing === 'right' ? 1 : -1;
// Create more dramatic attack particles
for (let i = 0; i < 10; i++) {
const angle = (Math.PI * 2 * i) / 10;
const speed = 3 + Math.random() * 2;
gameState.particles.push({
x: gameState.player.x + (direction * 20),
y: gameState.player.y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 1,
color: weapon.color
});
}
// Add attack flash effect
const flash = document.createElement('div');
flash.style.position = 'absolute';
flash.style.left = '0';
flash.style.top = '0';
flash.style.width = '100%';
flash.style.height = '100%';
flash.style.background = `rgba(255, 255, 255, 0.1)`;
flash.style.pointerEvents = 'none';
flash.style.zIndex = '100';
document.body.appendChild(flash);
setTimeout(() => flash.remove(), 100);
}
// Create Damage Text
function createDamageText(x, y, damage) {
const damageText = document.createElement('div');
damageText.className = 'damage-text';
damageText.textContent = `-${damage}`;
damageText.style.left = `${x}px`;
damageText.style.top = `${y}px`;
damageText.style.fontSize = '24px';
damageText.style.fontWeight = 'bold';
damageText.style.textShadow = '2px 2px 4px rgba(0, 0, 0, 0.5)';
document.body.appendChild(damageText);
setTimeout(() => damageText.remove(), 1000);
}
// Create Particles
function createParticles(x, y, color) {
// Create burst of particles with varying sizes and velocities
for (let i = 0; i < 15; i++) {
const angle = (Math.PI * 2 * i) / 15;
const speed = 2 + Math.random() * 4;
const size = 3 + Math.random() * 4;
gameState.particles.push({
x: x,
y: y,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
life: 1,
color: color,
size: size
});
}
}
// Create Notification
function createNotification(message) {
const notification = document.createElement('div');
notification.style.position = 'absolute';
notification.style.top = '50%';
notification.style.left = '50%';
notification.style.transform = 'translate(-50%, -50%)';
notification.style.background = 'rgba(0, 0, 0, 0.9)';
notification.style.padding = '20px 30px';
notification.style.borderRadius = '10px';
notification.style.fontSize = '20px';
notification.style.zIndex = '1000';
notification.style.border = '2px solid #3498db';
notification.style.boxShadow = '0 0 20px rgba(52, 152, 219, 0.5)';
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 2000);
}
// Open Shop
function openShop() {
if (gameState.isShopOpen) return;
gameState.isShopOpen = true;
document.getElementById('shop').style.display = 'block';
// Play shop sound
sounds.shop.play().catch(() => {});
const shopItemsContainer = document.getElementById('shopItems');
shopItemsContainer.innerHTML = '';
shopItems.forEach(item => {
const itemElement = document.createElement('div');
itemElement.className = 'shop-item';
itemElement.innerHTML = `
<span>${item.icon} ${item.name}</span>
<button onclick="buyItem('${item.effect}', ${item.price})">Buy (${item.price}g)</button>
`;
shopItemsContainer.appendChild(itemElement);
});
}
// Buy Item
function buyItem(effect, price) {
if (gameState.player.money >= price) {
gameState.player.money -= price;
switch (effect) {
case 'heal':
gameState.player.health = Math.min(gameState.player.maxHealth, gameState.player.health + 20);
createNotification('Health restored!');
break;
case 'healthContainer':
if (gameState.player.healthContainers < gameState.player.maxHealthContainers) {
gameState.player.healthContainers++;
gameState.player.maxHealth = gameState.player.healthContainers * 2;
gameState.player.health = gameState.player.maxHealth;
createNotification('Maximum health increased!');
}
break;
case 'weaponUpgrade':
if (gameState.player.weaponLevel < 2) {
gameState.player.weaponLevel++;
gameState.player.weapon = `sword${gameState.player.weaponLevel + 1}`;
createNotification(`${weapons[gameState.player.weapon].name} acquired!`);
}
break;
case 'rangedWeapon':
if (!gameState.hasRanged) {
gameState.hasRanged = true;
gameState.player.weapon = 'bow';
createNotification('Wooden Bow acquired! Press Space to shoot arrows.');
}
break;
}
updateUI();
createNotification('Purchase successful!');
} else {
createNotification('Not enough gold!');
}
}
// Save Game
function saveGame() {
const saveData = {
player: gameState.player,
enemies: gameState.enemies,
items: gameState.items,
money: gameState.player.money,
gameProgress: gameState.gameProgress
};
localStorage.setItem('adventureQuestSave', JSON.stringify(saveData));
// Play save sound
sounds.save.play().catch(() => {});
createNotification('Game saved!');
}
// Game Over
function gameOver() {
gameState.isGameOver = true;
document.getElementById('gameOver').style.display = 'block';
}
// Restart Game
function restartGame() {
gameState.isGameOver = false;
document.getElementById('gameOver').style.display = 'none';
// Reset game state with proper initialization
gameState.player = {
x: canvas.width / 2,
y: canvas.height / 2,
width: 40,
height: 40,
health: 6,
maxHealth: 6,
healthContainers: 3,
maxHealthContainers: 5,
speed: 3,
stamina: 100,
maxStamina: 100,
weapon: 'sword',
weaponLevel: 0,
hasRanged: false,
money: 0,
inventory: [],
facing: 'right',
isWalking: false,
walkFrame: 0,
lastWalkTime: 0
};
gameState.enemies = [];
gameState.items = [];
gameState.particles = [];
gameState.gameProgress = 0;
gameState.lastAttackTime = 0;
createInitialWorld();
updateUI();
gameLoop();
createNotification('Game restarted! Good luck on your adventure!');
}
// Update UI
function updateUI() {
// Update health display
const healthContainer = document.getElementById('healthContainer');
healthContainer.innerHTML = '';
for (let i = 0; i < gameState.player.healthContainers; i++) {
const heart = document.createElement('div');
heart.className = 'heart';
if (i >= Math.ceil(gameState.player.health / 2)) {
heart.classList.add('empty');
}
healthContainer.appendChild(heart);
}
// Update money
document.getElementById('money').textContent = `Gold: ${gameState.player.money}`;
// Update stamina bar
const staminaFill = document.getElementById('staminaFill');
staminaFill.style.width = `${(gameState.player.stamina / gameState.player.maxStamina) * 100}%`;
staminaFill.className = gameState.player.stamina > 30 ? 'stamina-fill' : 'stamina-fill stamina-empty';
// Update inventory
const inventorySlots = document.querySelectorAll('.inventory-slot');
inventorySlots.forEach((slot, index) => {
slot.innerHTML = '';
if (gameState.player.inventory[index]) {
slot.textContent = itemDefinitions[gameState.player.inventory[index]].icon;
slot.style.borderColor = '#f1c40f';
slot.style.boxShadow = '0 0 10px rgba(241, 196, 15, 0.5)';
} else {
slot.style.borderColor = '#7f8c8d';
slot.style.boxShadow = 'none';
}
});
}
// Render Game
function render() {
// Clear canvas
ctx.fillStyle = '#0f0f1e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw grid pattern
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
ctx.lineWidth = 1;
for (let x = 0; x < canvas.width; x += 50) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
for (let y = 0; y < canvas.height; y += 50) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
// Draw save points
gameState.savePoints.forEach(savePoint => {
ctx.fillStyle = 'rgba(46, 204, 113, 0.3)';
ctx.beginPath();
ctx.arc(savePoint.x, savePoint.y, savePoint.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#2ecc71';
ctx.lineWidth = 2;
ctx.stroke();
});
// Draw shops
gameState.shops.forEach(shop => {
ctx.fillStyle = 'rgba(52, 152, 219, 0.3)';
ctx.beginPath();
ctx.arc(shop.x, shop.y, shop.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#3498db';
ctx.lineWidth = 2;
ctx.stroke();
});
// Draw items
gameState.items.forEach(item => {
if (!item.collected) {
ctx.fillStyle = '#f1c40f';
ctx.font = '20px Arial';
ctx.fillText(itemDefinitions[item.type].icon, item.x - 10, item.y + 5);
}
});
// Draw enemies
gameState.enemies.forEach(enemy => {
ctx.fillStyle = '#e74c3c';
ctx.fillRect(enemy.x - enemy.width/2, enemy.y - enemy.height/2, enemy.width, enemy.height);
// Draw health bar
ctx.fillStyle = '#c0392b';
ctx.fillRect(enemy.x - 15, enemy.y - 25, 30, 4);
ctx.fillStyle = '#2ecc71';
ctx.fillRect(enemy.x - 15, enemy.y - 25, (enemy.health / enemy.maxHealth) * 30, 4);
});
// Draw player with walking animation
ctx.fillStyle = '#3498db';
ctx.fillRect(gameState.player.x - gameState.player.width/2, gameState.player.y - gameState.player.height/2, gameState.player.width, gameState.player.height);
// Draw walking animation effect
if (gameState.player.isWalking) {
ctx.fillStyle = 'rgba(52, 152, 219, 0.3)';
const offset = Math.sin(gameState.player.walkFrame * Math.PI / 2) * 5;
ctx.fillRect(
gameState.player.x - gameState.player.width/2 + offset,
gameState.player.y - gameState.player.height/2,
gameState.player.width,
gameState.player.height
);
}
// Draw stamina bar
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(gameState.player.x - 25, gameState.player.y - 40, 50, 6);
ctx.fillStyle = gameState.player.stamina > 30 ? '#2ecc71' : '#e74c3c';
ctx.fillRect(gameState.player.x - 25, gameState.player.y - 40, (gameState.player.stamina / gameState.player.maxStamina) * 50, 6);
// Draw weapon
const weapon = weapons[gameState.player.weapon];
ctx.fillStyle = weapon.color;
const direction = gameState.player.facing === 'right' ? 1 : -1;
ctx.fillRect(
gameState.player.x + (direction * 20),
gameState.player.y - 5,
20,
10
);
// Draw particles
gameState.particles.forEach(particle => {
ctx.fillStyle = particle.color;
ctx.globalAlpha = particle.life;
ctx.fillRect(particle.x - particle.size/2, particle.y - particle.size/2, particle.size, particle.size);
ctx.globalAlpha = 1;
});
}
// Initialize game
init();
</script>
</body>
</html>
<!-- agent-meta {"model":"unsloth/GLM-4.6V-GGUF","provider":"brigid","persona":"full","sessionId":"11e1fead-3d4a-4221-b03d-f7f196759d0e","tokensIn":1421928,"tokensOut":17999,"tokensTotal":1439927,"cost":0,"turns":47,"toolCalls":46,"failedToolCalls":1,"timestamp":"2026-08-06T00:22:31.149Z"} -->