A Aventura da Mochila Perdida
by RocketHawk87895 lines30.7 KB
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>A Aventura da Mochila Perdida</title>
<style>
:root {
--bg-color: #2c3e50;
--panel-bg: #34495e;
--accent-color: #f1c40f;
--text-color: #ecf0f1;
--btn-color: #27ae60;
--btn-active: #2ecc71;
--font-family: 'Comic Sans MS', 'Chalkboard SE', 'Arial', sans-serif;
}
* {
box-sizing: border-box;
user-select: none;
-webkit-user-select: none;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: var(--font-family);
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
min-height: 100vh;
padding: 10px;
}
h1 {
color: var(--accent-color);
text-align: center;
margin-bottom: 10px;
font-size: 24px;
text-shadow: 2px 2px #000;
}
/* Container Principal */
.game-container {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 800px;
gap: 10px;
}
/* Top HUD */
.hud {
display: flex;
justify-content: space-between;
width: 100%;
background-color: var(--panel-bg);
padding: 10px 15px;
border-radius: 10px;
border: 3px solid #1abc9c;
font-size: 16px;
font-weight: bold;
}
.hud-item {
display: flex;
align-items: center;
gap: 5px;
}
/* Canvas / Mapa */
#gameCanvas {
background-color: #e0d2b4;
border: 4px solid #d35400;
border-radius: 8px;
box-shadow: 0 8px 16px rgba(0,0,0,0.5);
image-rendering: pixelated;
max-width: 100%;
height: auto;
}
/* Caixas de Diálogo e Informações */
.dialog-box {
width: 100%;
background-color: var(--panel-bg);
border: 3px solid #2980b9;
border-radius: 10px;
padding: 12px;
min-height: 60px;
text-align: center;
font-size: 15px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: inset 0 0 10px rgba(0,0,0,0.3);
}
/* Painel Inferior: Missão e Inventário */
.info-panel {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
width: 100%;
}
.panel-box {
background: var(--panel-bg);
border: 2px solid #8e44ad;
border-radius: 8px;
padding: 10px;
font-size: 13px;
}
.panel-box h3 {
color: var(--accent-color);
margin-bottom: 5px;
font-size: 14px;
border-bottom: 1px solid #8e44ad;
padding-bottom: 3px;
}
.inventory-list {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 5px;
}
.inventory-tag {
background: #2c3e50;
border: 1px solid #1abc9c;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
}
/* Controles Virtuais */
.controls {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
width: 100%;
margin-top: 5px;
}
.btn-interact {
background-color: var(--btn-color);
color: white;
border: none;
padding: 12px 30px;
font-size: 18px;
font-weight: bold;
font-family: var(--font-family);
border-radius: 25px;
box-shadow: 0 4px #1e8449;
cursor: pointer;
width: 80%;
max-width: 300px;
transition: background-color 0.1s, transform 0.1s;
}
.btn-interact:active {
transform: translateY(3px);
box-shadow: 0 1px #1e8449;
background-color: var(--btn-active);
}
.dpad {
display: grid;
grid-template-columns: repeat(3, 50px);
grid-template-rows: repeat(2, 50px);
gap: 5px;
justify-content: center;
}
.btn-dir {
background-color: #34495e;
border: 2px solid #bdc3c7;
color: white;
font-size: 20px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 3px #2c3e50;
}
.btn-dir:active {
transform: translateY(2px);
box-shadow: 0 1px #2c3e50;
background-color: #7f8c8d;
}
.btn-reset {
background-color: #e74c3c;
color: white;
border: none;
padding: 6px 12px;
font-size: 12px;
border-radius: 5px;
cursor: pointer;
margin-top: 5px;
}
/* Modal / Pop-up de Desafio e Coleta */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
}
.modal {
background: var(--panel-bg);
border: 4px solid var(--accent-color);
border-radius: 15px;
padding: 20px;
max-width: 350px;
width: 90%;
text-align: center;
box-shadow: 0 10px 25px rgba(0,0,0,0.8);
}
.modal h2 {
color: var(--accent-color);
margin-bottom: 10px;
}
.modal p {
margin-bottom: 15px;
font-size: 16px;
line-height: 1.4;
}
.modal-options {
display: flex;
flex-direction: column;
gap: 10px;
}
.btn-option {
background: #2980b9;
color: white;
border: none;
padding: 10px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
font-family: var(--font-family);
}
.btn-option:hover {
background: #3498db;
}
.hidden {
display: none !important;
}
@media (max-width: 500px) {
.info-panel {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<h1>🎒 A Aventura da Mochila Perdida</h1>
<div class="game-container">
<!-- HUD Superior -->
<div class="hud">
<div class="hud-item">⭐ <span id="xpDisplay">XP: 0</span></div>
<div class="hud-item">🏆 <span id="levelDisplay">Nível 1</span></div>
<div class="hud-item">🎒 <span id="itemCountDisplay">Itens: 0/5</span></div>
</div>
<!-- Tela Principal do Jogo -->
<canvas id="gameCanvas" width="640" height="480"></canvas>
<!-- Mensagem de Diálogo / Dicas -->
<div id="dialogBox" class="dialog-box">
Aproxime-se de um personagem ou item para interagir.
</div>
<!-- Botão Principal de Interação -->
<button id="btnInteract" class="btn-interact">🤝 Interagir</button>
<!-- D-Pad Controles Virtuais para Celular -->
<div class="controls">
<div class="dpad">
<div></div>
<button class="btn-dir" id="btnUp">▲</button>
<div></div>
<button class="btn-dir" id="btnLeft">◄</button>
<button class="btn-dir" id="btnDown">▼</button>
<button class="btn-dir" id="btnRight">►</button>
</div>
</div>
<!-- Painel de Missão e Inventário -->
<div class="info-panel">
<div class="panel-box">
<h3>📜 Missão</h3>
<p id="missionText">Encontre os 5 itens escolares espalhados pela escola e aprenda seus nomes em inglês.</p>
</div>
<div class="panel-box">
<h3>🎒 Inventário</h3>
<div id="inventoryContainer" class="inventory-list">
<em>Inventário: Vazio</em>
</div>
</div>
</div>
<button id="btnReset" class="btn-reset">🔄 Reiniciar Jogo</button>
</div>
<!-- Modal de Coleta / Quiz -->
<div id="quizModal" class="modal-overlay hidden">
<div class="modal">
<h2 id="modalTitle">Novo Item Encontrado!</h2>
<p id="modalDesc">Você achou um item!</p>
<div id="modalOptions" class="modal-options"></div>
</div>
</div>
<script>
// CONFIGURAÇÃO DO CANVAS E VARIÁVEIS GLOBAIS
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
// Elementos da UI
const dialogBox = document.getElementById("dialogBox");
const xpDisplay = document.getElementById("xpDisplay");
const levelDisplay = document.getElementById("levelDisplay");
const itemCountDisplay = document.getElementById("itemCountDisplay");
const inventoryContainer = document.getElementById("inventoryContainer");
const missionText = document.getElementById("missionText");
const quizModal = document.getElementById("quizModal");
const modalTitle = document.getElementById("modalTitle");
const modalDesc = document.getElementById("modalDesc");
const modalOptions = document.getElementById("modalOptions");
// Estado do Jogo
let xp = 0;
let level = 1;
let collectedItems = [];
let totalItemsCount = 5;
let isModalOpen = false;
// Configuração de Grade/Tamanho (Grid 16x12 tiles de 40px)
const TILE_SIZE = 40;
const COLS = 16;
const ROWS = 12;
// TECLAS
const keys = {
ArrowUp: false,
ArrowDown: false,
ArrowLeft: false,
ArrowRight: false
};
// ESTRUTURA DO MAPA (0: Chão, 1: Parede Externa, 2: Parede Interna, 3: Mesa/Balcão, 4: Estante)
const mapGrid = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1],
[1,0,3,3,1,0,3,3,0,1,0,4,4,4,0,1],
[1,0,3,3,2,0,3,3,0,2,0,4,4,4,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,2,1,1,1,2,1,1,1,2,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,3,3,0,1,0,3,3,0,1,0,3,3,0,1],
[1,0,3,3,0,1,0,3,3,0,1,0,3,3,0,1],
[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
// RÓTULOS DAS SALAS
const roomLabels = [
{ text: "Classroom", x: 2.5 * TILE_SIZE, y: 1.5 * TILE_SIZE },
{ text: "Laboratory", x: 7 * TILE_SIZE, y: 1.5 * TILE_SIZE },
{ text: "Library", x: 12.5 * TILE_SIZE, y: 1.5 * TILE_SIZE }
];
// JOGADOR
const player = {
x: 2 * TILE_SIZE,
y: 10 * TILE_SIZE,
width: 32,
height: 32,
speed: 3,
direction: 'down',
isMoving: false,
animFrame: 0,
animTimer: 0
};
// NPCs
const npcs = [
{
id: 'teacher',
name: 'Ms. Ana',
role: 'Teacher',
x: 2 * TILE_SIZE,
y: 2 * TILE_SIZE,
color: '#e74c3c',
dialog: "Olá! Eu sou uma Teacher, professora. Encontre os itens escolares espalhados pela escola!"
},
{
id: 'student',
name: 'Leo',
role: 'Student',
x: 7 * TILE_SIZE,
y: 4 * TILE_SIZE,
color: '#f39c12',
dialog: "Oi! Eu sou um Student, aluno. Você já encontrou algum item?"
},
{
id: 'librarian',
name: 'Librarian',
role: 'Librarian',
x: 13 * TILE_SIZE,
y: 4 * TILE_SIZE,
color: '#9b59b6',
dialog: "Bem-vindo à Library! Aqui há muitos livros."
}
];
// ITENS PERDIDOS
const items = [
{ id: 'book', nameEn: 'Book', namePt: 'livro', icon: '📖', x: 13 * TILE_SIZE + 4, y: 2 * TILE_SIZE + 4, collected: false },
{ id: 'pencil', nameEn: 'Pencil', namePt: 'lápis', icon: '✏️', x: 2 * TILE_SIZE + 4, y: 3 * TILE_SIZE + 4, collected: false },
{ id: 'notebook', nameEn: 'Notebook', namePt: 'caderno', icon: '📓', x: 7 * TILE_SIZE + 4, y: 2 * TILE_SIZE + 4, collected: false },
{ id: 'eraser', nameEn: 'Eraser', namePt: 'borracha', icon: '🧹', x: 2 * TILE_SIZE + 4, y: 8 * TILE_SIZE + 4, collected: false },
{ id: 'ruler', nameEn: 'Ruler', namePt: 'régua', icon: '📏', x: 12 * TILE_SIZE + 4, y: 8 * TILE_SIZE + 4, collected: false }
];
// BANCO DE OPÇÕES PARA DESAFIOS (EXPANSÍVEL)
const wordOptions = ["Book", "Pencil", "Notebook", "Eraser", "Ruler", "Pen", "Scissors", "Backpack", "Glue", "Sharpener"];
// CONTROLES TECLADO
window.addEventListener("keydown", (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = true;
}
});
window.addEventListener("keyup", (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = false;
}
});
// CONTROLES VIRTUAIS (MOBILE)
const setupMobileBtn = (id, keyName) => {
const btn = document.getElementById(id);
const startHandler = (e) => { e.preventDefault(); keys[keyName] = true; };
const endHandler = (e) => { e.preventDefault(); keys[keyName] = false; };
btn.addEventListener("touchstart", startHandler);
btn.addEventListener("touchend", endHandler);
btn.addEventListener("mousedown", startHandler);
btn.addEventListener("mouseup", endHandler);
};
setupMobileBtn("btnUp", "ArrowUp");
setupMobileBtn("btnDown", "ArrowDown");
setupMobileBtn("btnLeft", "ArrowLeft");
setupMobileBtn("btnRight", "ArrowRight");
document.getElementById("btnInteract").addEventListener("click", () => handleInteraction());
document.getElementById("btnReset").addEventListener("click", () => resetGame());
// LÓGICA DE DETECÇÃO DE COLISÃO COM PAREDES E MÓVEIS
function isSolidTile(x, y) {
const col = Math.floor(x / TILE_SIZE);
const row = Math.floor(y / TILE_SIZE);
if (col < 0 || col >= COLS || row < 0 || row >= ROWS) return true;
const tile = mapGrid[row][col];
return tile !== 0; // Se não for 0 (chão), é sólido
}
function checkCollision(newX, newY) {
const margin = 4;
// Checar os 4 cantos da caixa de colisão do jogador
const p1 = isSolidTile(newX + margin, newY + margin);
const p2 = isSolidTile(newX + player.width - margin, newY + margin);
const p3 = isSolidTile(newX + margin, newY + player.height - margin);
const p4 = isSolidTile(newX + player.width - margin, newY + player.height - margin);
if (p1 || p2 || p3 || p4) return true;
// Colisão com NPCs
for (let npc of npcs) {
if (Math.abs(newX - npc.x) < 28 && Math.abs(newY - npc.y) < 28) {
return true;
}
}
return false;
}
// ATUALIZAÇÃO DA POSIÇÃO
function updatePlayer() {
if (isModalOpen) return;
let dx = 0;
let dy = 0;
if (keys.ArrowUp) { dy -= player.speed; player.direction = 'up'; }
if (keys.ArrowDown) { dy += player.speed; player.direction = 'down'; }
if (keys.ArrowLeft) { dx -= player.speed; player.direction = 'left'; }
if (keys.ArrowRight) { dx += player.speed; player.direction = 'right'; }
player.isMoving = (dx !== 0 || dy !== 0);
if (player.isMoving) {
// Tenta mover em X
if (!checkCollision(player.x + dx, player.y)) {
player.x += dx;
}
// Tenta mover em Y
if (!checkCollision(player.x, player.y + dy)) {
player.y += dy;
}
// Animação de passos simples
player.animTimer++;
if (player.animTimer > 10) {
player.animFrame = (player.animFrame + 1) % 2;
player.animTimer = 0;
}
} else {
player.animFrame = 0;
}
checkProximityHint();
}
// VERIFICAR PROXIMIDADE PARA MOSTRAR DICAS NA DIALOG BOX
function checkProximityHint() {
let nearAnything = false;
// Checa NPCs
for (let npc of npcs) {
const dist = Math.hypot((player.x + 16) - (npc.x + 16), (player.y + 16) - (npc.y + 16));
if (dist < 45) {
dialogBox.innerText = `Pressione Interagir para falar com ${npc.name} (${npc.role}).`;
nearAnything = true;
break;
}
}
// Checa Itens
if (!nearAnything) {
for (let item of items) {
if (!item.collected) {
const dist = Math.hypot((player.x + 16) - (item.x + 16), (player.y + 16) - (item.y + 16));
if (dist < 40) {
dialogBox.innerText = `Pressione Interagir para pegar o item brilhante!`;
nearAnything = true;
break;
}
}
}
}
if (!nearAnything) {
dialogBox.innerText = "Aproxime-se de um personagem ou item para interagir.";
}
}
// AÇÃO DE INTERAGIR (BOTÃO "INTERAGIR")
function handleInteraction() {
if (isModalOpen) return;
// Tenta interagir com NPC
for (let npc of npcs) {
const dist = Math.hypot((player.x + 16) - (npc.x + 16), (player.y + 16) - (npc.y + 16));
if (dist < 50) {
dialogBox.innerText = `[${npc.name} - ${npc.role}]: "${npc.dialog}"`;
return;
}
}
// Tenta interagir com Item
for (let item of items) {
if (!item.collected) {
const dist = Math.hypot((player.x + 16) - (item.x + 16), (player.y + 16) - (item.y + 16));
if (dist < 45) {
startItemCollection(item);
return;
}
}
}
}
// SISTEMA DE COLETA E MINI-QUIZ EDUCATIVO
function startItemCollection(item) {
isModalOpen = true;
modalTitle.innerText = "Novo item encontrado!";
modalDesc.innerHTML = `${item.icon} Este item é um <strong>${item.nameEn}</strong>!<br>Em português: <strong>${item.namePt}</strong>.`;
// Gerar opções do Desafio
modalOptions.innerHTML = "";
const questionText = document.createElement("p");
questionText.style.fontWeight = "bold";
questionText.style.marginTop = "10px";
questionText.innerText = `Desafio: Como se diz "${item.namePt}" em inglês?`;
modalOptions.appendChild(questionText);
// Selecionar opções incorretas aleatórias
let wrongOpts = wordOptions.filter(w => w !== item.nameEn);
wrongOpts.sort(() => 0.5 - Math.random());
let choices = [item.nameEn, wrongOpts[0], wrongOpts[1]];
choices.sort(() => 0.5 - Math.random());
choices.forEach(choice => {
const btn = document.createElement("button");
btn.className = "btn-option";
btn.innerText = choice;
btn.onclick = () => {
if (choice === item.nameEn) {
item.collected = true;
collectedItems.push(item);
addXP(10);
updateInventoryUI();
modalTitle.innerText = "Excelente! ✨";
modalDesc.innerText = `Você acertou! "${item.nameEn}" adicionado ao seu inventário. (+10 XP)`;
modalOptions.innerHTML = "";
const btnClose = document.createElement("button");
btnClose.className = "btn-option";
btnClose.innerText = "Continuar Explorando";
btnClose.onclick = () => {
quizModal.classList.add("hidden");
isModalOpen = false;
checkMissionCompletion();
};
modalOptions.appendChild(btnClose);
} else {
alert("Ops! Tente novamente.");
}
};
modalOptions.appendChild(btn);
});
quizModal.classList.remove("hidden");
}
// SISTEMA DE XP E NÍVEIS
function addXP(amount) {
xp += amount;
if (xp >= 30 && level === 1) {
level = 2;
dialogBox.innerText = "🎉 Parabéns! Você subiu para o Nível 2!";
} else if (xp >= 60 && level === 2) {
level = 3;
dialogBox.innerText = "🎉 Incrível! Você alcançou o Nível 3!";
}
xpDisplay.innerText = `XP: ${xp}`;
levelDisplay.innerText = `Nível ${level}`;
}
// ATUALIZAÇÃO DO INVENTÁRIO
function updateInventoryUI() {
itemCountDisplay.innerText = `Itens: ${collectedItems.length}/${totalItemsCount}`;
if (collectedItems.length === 0) {
inventoryContainer.innerHTML = "<em>Inventário: Vazio</em>";
return;
}
inventoryContainer.innerHTML = "";
collectedItems.forEach(item => {
const tag = document.createElement("span");
tag.className = "inventory-tag";
tag.innerText = `${item.icon} ${item.nameEn}`;
inventoryContainer.appendChild(tag);
});
}
// VERIFICAÇÃO DE CONCLUSÃO DA MISSÃO PRINCIPAL
function checkMissionCompletion() {
if (collectedItems.length === totalItemsCount) {
setTimeout(() => {
isModalOpen = true;
modalTitle.innerText = "🏆 Missão Completa!";
modalDesc.innerHTML = "<strong>Parabéns! Você encontrou todos os itens escolares!</strong><br><br>Recompensa: Você ganhou +50 XP e completou a aventura da mochila!";
modalOptions.innerHTML = "";
addXP(50);
missionText.innerHTML = "<strong style='color:#2ecc71;'>Missão Concluída com Sucesso!</strong>";
const btnFinish = document.createElement("button");
btnFinish.className = "btn-option";
btnFinish.innerText = "Eba!";
btnFinish.onclick = () => {
quizModal.classList.add("hidden");
isModalOpen = false;
dialogBox.innerText = "Você recuperou todos os materiais da mochila mágica! Parabéns!";
};
modalOptions.appendChild(btnFinish);
quizModal.classList.remove("hidden");
}, 300);
}
}
// REINICIAR O JOGO
function resetGame() {
xp = 0;
level = 1;
collectedItems = [];
player.x = 2 * TILE_SIZE;
player.y = 10 * TILE_SIZE;
items.forEach(i => i.collected = false);
xpDisplay.innerText = `XP: 0`;
levelDisplay.innerText = `Nível 1`;
missionText.innerText = "Encontre os 5 itens escolares espalhados pela escola e aprenda seus nomes em inglês.";
updateInventoryUI();
dialogBox.innerText = "Jogo reiniciado! Aproxime-se de um personagem ou item para interagir.";
}
// FUNÇÕES DE RENDERIZAÇÃO GRÁFICA (PIXEL ART EM CANVAS)
function drawMap() {
for (let r = 0; r < ROWS; r++) {
for (let c = 0; c < COLS; c++) {
const tile = mapGrid[r][c];
const x = c * TILE_SIZE;
const y = r * TILE_SIZE;
// Chão (Piso escolar)
ctx.fillStyle = "#f5e6ca";
ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
ctx.strokeStyle = "#e6d5b8";
ctx.strokeRect(x, y, TILE_SIZE, TILE_SIZE);
if (tile === 1) { // Parede Externa
ctx.fillStyle = "#8e44ad";
ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
ctx.fillStyle = "#6c3483";
ctx.fillRect(x + 2, y + 2, TILE_SIZE - 4, TILE_SIZE - 4);
} else if (tile === 2) { // Parede Interna / Divisória
ctx.fillStyle = "#34495e";
ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
} else if (tile === 3) { // Mesa de Madeira
ctx.fillStyle = "#d35400";
ctx.fillRect(x + 2, y + 2, TILE_SIZE - 4, TILE_SIZE - 4);
ctx.fillStyle = "#e67e22";
ctx.fillRect(x + 5, y + 5, TILE_SIZE - 10, TILE_SIZE - 10);
} else if (tile === 4) { // Estante de Livros
ctx.fillStyle = "#7e5109";
ctx.fillRect(x, y, TILE_SIZE, TILE_SIZE);
ctx.fillStyle = "#f1c40f";
ctx.fillRect(x + 4, y + 6, 6, 28);
ctx.fillStyle = "#e74c3c";
ctx.fillRect(x + 14, y + 6, 6, 28);
ctx.fillStyle = "#3498db";
ctx.fillRect(x + 24, y + 6, 6, 28);
}
}
}
// Nomes das Salas no Chão
ctx.font = "bold 14px " + getComputedStyle(document.body).fontFamily;
ctx.fillStyle = "#7f8c8d";
ctx.textAlign = "center";
roomLabels.forEach(room => {
ctx.fillText(room.text, room.x, room.y);
});
}
function drawNPCs() {
npcs.forEach(npc => {
// Corpo / Roupa
ctx.fillStyle = npc.color;
ctx.fillRect(npc.x + 6, npc.y + 12, 20, 16);
// Cabeça
ctx.fillStyle = "#ffcc99";
ctx.fillRect(npc.x + 8, npc.y + 2, 16, 12);
// Cabelo
ctx.fillStyle = "#4a235a";
ctx.fillRect(npc.x + 8, npc.y, 16, 4);
// Olhos
ctx.fillStyle = "#000";
ctx.fillRect(npc.x + 11, npc.y + 6, 2, 2);
ctx.fillRect(npc.x + 19, npc.y + 6, 2, 2);
// Nome acima do NPC
ctx.font = "10px " + getComputedStyle(document.body).fontFamily;
ctx.fillStyle = "#000";
ctx.textAlign = "center";
ctx.fillText(`${npc.name} (${npc.role})`, npc.x + 16, npc.y - 4);
});
}
function drawItems() {
items.forEach(item => {
if (!item.collected) {
// Brilho sob o item
ctx.fillStyle = "rgba(241, 196, 15, 0.4)";
ctx.beginPath();
ctx.arc(item.x + 16, item.y + 16, 14, 0, Math.PI * 2);
ctx.fill();
// Emoji do Item
ctx.font = "20px Arial";
ctx.textAlign = "center";
ctx.fillText(item.icon, item.x + 16, item.y + 22);
}
});
}
function drawPlayer() {
const px = player.x;
const py = player.y;
// Animação simples de Pernas
let legOffset = (player.isMoving && player.animFrame === 1) ? 3 : 0;
// Pernas
ctx.fillStyle = "#2c3e50";
ctx.fillRect(px + 8, py + 22, 6, 8 - legOffset);
ctx.fillRect(px + 18, py + 22, 6, 8 + legOffset);
// Corpo / Camiseta
ctx.fillStyle = "#27ae60";
ctx.fillRect(px + 6, py + 12, 20, 12);
// Cabeça
ctx.fillStyle = "#ffe0bd";
ctx.fillRect(px + 8, py + 2, 16, 12);
// Cabelo
ctx.fillStyle = "#d35400";
ctx.fillRect(px + 6, py, 20, 5);
// Olhos
ctx.fillStyle = "#000";
if (player.direction === 'left') {
ctx.fillRect(px + 9, py + 6, 2, 3);
} else if (player.direction === 'right') {
ctx.fillRect(px + 21, py + 6, 2, 3);
} else {
ctx.fillRect(px + 11, py + 6, 2, 3);
ctx.fillRect(px + 19, py + 6, 2, 3);
}
}
// LOOP PRINCIPAL DO JOGO (GAME LOOP)
function gameLoop() {
updatePlayer();
// Limpa Tela
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Desenha Elementos
drawMap();
drawItems();
drawNPCs();
drawPlayer();
requestAnimationFrame(gameLoop);
}
// INICIALIZAÇÃO
updateInventoryUI();
requestAnimationFrame(gameLoop);
</script>
</body>
</html>Game Source: A Aventura da Mochila Perdida
Creator: RocketHawk87
Libraries: none
Complexity: complex (895 lines, 30.7 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: a-aventura-da-mochila-perdida-rockethawk87" to link back to the original. Then publish at arcadelab.ai/publish.