Pixel Quest - Fantasy
by ShadowTurtle30307 lines7.2 KB
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pixel Quest - Fantasy</title>
<style>
body {
margin: 0;
background: #171831;
color: #fff1c4;
font-family: monospace;
text-align: center;
}
h1 { color: #ffe68c; }
#hud {
display: flex;
justify-content: space-around;
padding: 12px;
background: #29264c;
}
canvas {
width: min(96vw, 720px);
height: auto;
image-rendering: pixelated;
border: 3px solid #7564ad;
background: #252951;
}
button {
background: #494278;
color: white;
border: 2px solid #a69de9;
padding: 12px;
margin: 4px;
font: bold 14px monospace;
border-radius: 6px;
cursor: pointer;
touch-action: none;
}
#msg { padding: 12px; color: #ffe68c; }
</style>
</head>
<body>
<h1>⚔ PIXEL QUEST</h1>
<div id="hud">
<span id="score">💎 0/5</span>
<span id="lives">♥ 3</span>
</div>
<canvas id="game" width="480" height="320"></canvas>
<div id="msg">Raccogli 5 gemme e raggiungi il portale!</div>
<div>
<button data-key="w">⬆ W</button><br>
<button data-key="a">⬅ A</button>
<button data-key="s">⬇ S</button>
<button data-key="d">➡ D</button>
<button data-key="jump">🦘 SALTO</button>
<button id="reset">↻ RICOMINCIA</button>
</div>
<p>W avanti · A sinistra · S indietro · D destra · Spazio salta</p>
<script>
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const W = 480, H = 320;
const walls = [
{x:0,y:0,w:480,h:12},
{x:0,y:308,w:480,h:12},
{x:0,y:0,w:12,h:320},
{x:468,y:0,w:12,h:320},
{x:75,y:65,w:15,h:115},
{x:145,y:0,w:15,h:100},
{x:145,y:170,w:15,h:100},
{x:215,y:65,w:15,h:115},
{x:285,y:0,w:15,h:100},
{x:285,y:170,w:15,h:100},
{x:355,y:65,w:15,h:115},
{x:390,y:210,w:48,h:15}
];
let player, gems, enemies, keys, lives, won, over;
let frame = 0;
function reset() {
player = {x:25,y:25,w:12,h:15,inv:0};
gems = [[120,38],[185,140],[250,225],
[330,45],[420,265]]
.map(([x,y])=>({x,y,got:false}));
enemies = [
{x:112,y:220,vx:1.1,vy:.7},
{x:245,y:35,vx:-.8,vy:1},
{x:410,y:135,vx:.7,vy:-1}
];
keys = {};
lives = 3;
won = false;
over = false;
document.getElementById("msg").textContent =
"Raccogli le 5 gemme e raggiungi il portale!";
updateHUD();
}
function updateHUD() {
document.getElementById("score").textContent =
"💎 " + gems.filter(g=>g.got).length + "/5";
document.getElementById("lives").textContent = "♥ " + lives;
}
function rect(x,y,w,h,color) {
ctx.fillStyle = color;
ctx.fillRect(Math.round(x),Math.round(y),w,h);
}
function hit(a,b) {
return a.x < b.x+b.w && a.x+a.w > b.x &&
a.y < b.y+b.h && a.y+a.h > b.y;
}
function blocked(a) {
return walls.some(w=>hit(a,w));
}
function draw() {
ctx.fillStyle = "#252951";
ctx.fillRect(0,0,W,H);
// Stelle
for(let i=0;i<55;i++) {
rect((i*83+17)%W,(i*47+8)%H,2,2,
i%2 ? "#dce4ff" : "#ffe8a0");
}
// Muri del labirinto
walls.forEach(w=>{
rect(w.x,w.y,w.w,w.h,"#654b5c");
rect(w.x,w.y,w.w,3,"#61c779");
for(let y=w.y+8;y<w.y+w.h-3;y+=10)
rect(w.x+3,y,4,3,"#a57a70");
});
// Portale
rect(425,275,22,25,"#302253");
rect(429,278,14,19,
gems.every(g=>g.got) ? "#5bffe0" : "#9260cb");
rect(433,281,6,13,"#e7c6ff");
ctx.fillStyle="#ffe99c";
ctx.font="bold 8px monospace";
ctx.fillText("PORTALE",413,270);
// Gemme
gems.forEach(g=>{
if(g.got) return;
const y=g.y+Math.sin(frame*.09+g.x)*2;
rect(g.x+3,y-3,4,4,"#edffff");
rect(g.x,y,10,6,"#48dafa");
rect(g.x+3,y+6,4,3,"#9d83ff");
});
// Goblin
enemies.forEach(e=>{
rect(e.x,e.y+3,13,10,"#64d57c");
rect(e.x+1,e.y,4,5,"#9df28c");
rect(e.x+8,e.y,4,5,"#9df28c");
rect(e.x+3,e.y+5,2,3,"white");
rect(e.x+9,e.y+5,2,3,"white");
rect(e.x+4,e.y+6,1,1,"#171831");
rect(e.x+10,e.y+6,1,1,"#171831");
});
// Eroe
if(player.inv%8<5) {
rect(player.x+2,player.y,8,6,"#f5c39c");
rect(player.x,player.y-2,12,5,"#64417a");
rect(player.x+1,player.y+6,10,7,"#578fed");
rect(player.x,player.y+12,5,3,"#e9ca84");
rect(player.x+7,player.y+12,5,3,"#e9ca84");
rect(player.x+8,player.y+2,2,2,"white");
}
if(won || over) {
rect(68,108,344,91,"#20203f");
rect(72,112,336,83,"#494278");
ctx.fillStyle=won ? "#fff09b" : "#ff9eaf";
ctx.font="bold 22px monospace";
ctx.fillText(won?"VITTORIA!":"GAME OVER",150,145);
ctx.fillStyle="white";
ctx.font="11px monospace";
ctx.fillText(
won?"Hai salvato il regno!":"Premi R per riprovare",
160,166
);
}
}
function update() {
frame++;
if(!won && !over) {
let dx=(keys.d?2:0)-(keys.a?2:0);
let dy=(keys.s?2:0)-(keys.w?2:0);
if(dx && dy) { dx*=.707; dy*=.707; }
let next={...player,x:player.x+dx};
if(!blocked(next)) player.x=next.x;
next={...player,y:player.y+dy};
if(!blocked(next)) player.y=next.y;
if(player.inv>0) player.inv--;
// Raccogli gemme
gems.forEach(g=>{
if(!g.got && hit(player,
{x:g.x-2,y:g.y-2,w:13,h:13})) {
g.got=true;
updateHUD();
document.getElementById("msg").textContent =
"✨ Hai raccolto una gemma!";
}
});
// Movimento dei goblin
enemies.forEach(e=>{
e.x+=e.vx;
e.y+=e.vy;
if(e.x<18 || e.x>450) e.vx*=-1;
if(e.y<18 || e.y>292) e.vy*=-1;
if(!player.inv && hit(player,
{x:e.x,y:e.y,w:13,h:13})) {
lives--;
player.inv=80;
updateHUD();
if(lives<=0) {
over=true;
document.getElementById("msg").textContent =
"Game over! Premi R per riprovare.";
} else {
document.getElementById("msg").textContent =
"Attento ai goblin!";
}
}
});
// Vittoria
if(player.x>418 && player.y>265) {
if(gems.every(g=>g.got)) {
won=true;
document.getElementById("msg").textContent =
"👑 Vittoria! Hai liberato il regno!";
} else {
document.getElementById("msg").textContent =
"Ti mancano " + gems.filter(g=>!g.got).length +
" gemme!";
}
}
}
draw();
requestAnimationFrame(update);
}
function setKey(e,down) {
const k=e.key.toLowerCase();
const map={
w:"w",arrowup:"w",
a:"a",arrowleft:"a",
s:"s",arrowdown:"s",
d:"d",arrowright:"d",
" ":"jump"
};
if(map[k]) {
keys[map[k]]=down;
e.preventDefault();
}
if(down && k==="r") reset();
}
window.addEventListener("keydown",e=>setKey(e,true));
window.addEventListener("keyup",e=>setKey(e,false));
document.querySelectorAll("[data-key]").forEach(btn=>{
const k=btn.dataset.key;
btn.addEventListener("pointerdown",e=>{
e.preventDefault();
keys[k]=true;
btn.setPointerCapture(e.pointerId);
});
["pointerup","pointercancel","lostpointercapture"]
.forEach(ev=>btn.addEventListener(ev,()=>keys[k]=false));
});
document.getElementById("reset").onclick=reset;
reset();
update();
</script>
</body>
</html>
Game Source: Pixel Quest - Fantasy
Creator: ShadowTurtle30
Libraries: none
Complexity: complex (307 lines, 7.2 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: pixel-quest-fantasy-shadowturtle30" to link back to the original. Then publish at arcadelab.ai/publish.