Lula: Uma Jornada Brasileira
by DriftOtter981574 lines59.4 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">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<title>Lula: Uma Jornada Brasileira</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; -webkit-user-select: none; user-select: none; }
html, body { width: 100%; height: 100%; overflow: hidden; background: #000; font-family: 'Segoe UI', 'Trebuchet MS', sans-serif; touch-action: none; }
#gameContainer { position: relative; width: 100vw; height: 100vh; background: #1a1a2e; overflow: hidden; display: flex; align-items: center; justify-content: center; }
canvas { display: block; image-rendering: pixelated; image-rendering: crisp-edges; box-shadow: 0 0 30px rgba(0,0,0,0.5); }
#gameContainer::before {
content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
background:
radial-gradient(circle at 20% 30%, rgba(0,156,59,0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 70%, rgba(0,39,118,0.15) 0%, transparent 50%);
pointer-events: none;
}
/* Tela inicial */
#startScreen, #factScreen, #winScreen, #gameOverScreen, #levelIntro {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
display: flex; flex-direction: column; align-items: center; justify-content: center;
text-align: center; z-index: 100; padding: 20px;
}
#startScreen { background: linear-gradient(135deg, #009c3b 0%, #ffdf00 50%, #002776 100%); }
#startScreen h1 { font-size: clamp(28px, 8vw, 64px); color: #fff; text-shadow: 3px 3px 0 #000, -1px -1px 0 #000; margin-bottom: 10px; font-weight: 900; letter-spacing: 2px; }
#startScreen h2 { font-size: clamp(16px, 4vw, 28px); color: #fff; text-shadow: 2px 2px 0 #000; margin-bottom: 20px; }
#startScreen p { font-size: clamp(12px, 3vw, 18px); color: #fff; text-shadow: 1px 1px 0 #000; max-width: 600px; margin-bottom: 30px; line-height: 1.6; }
.btnStart { background: #fff; color: #002776; border: 4px solid #000; padding: 15px 40px; font-size: clamp(16px, 4vw, 24px); font-weight: 900; cursor: pointer; border-radius: 8px; box-shadow: 0 6px 0 #002776; transition: all 0.1s; }
.btnStart:active { transform: translateY(4px); box-shadow: 0 2px 0 #002776; }
#levelIntro { background: rgba(0,0,0,0.85); }
#levelIntro h2 { font-size: clamp(20px, 5vw, 40px); color: #ffdf00; text-shadow: 2px 2px 0 #000; margin-bottom: 15px; }
#levelIntro p { font-size: clamp(14px, 3.5vw, 20px); color: #fff; max-width: 600px; margin-bottom: 20px; line-height: 1.5; }
#levelIntro .levelNumber { font-size: clamp(40px, 12vw, 80px); color: #009c3b; font-weight: 900; text-shadow: 3px 3px 0 #fff; }
#factScreen { background: rgba(0,0,0,0.9); }
.factBox { background: #ffdf00; border: 5px solid #002776; border-radius: 15px; padding: 25px; max-width: 90%; max-width: 500px; color: #000; box-shadow: 0 8px 0 #002776; }
.factBox h3 { color: #009c3b; font-size: clamp(20px, 5vw, 32px); margin-bottom: 15px; text-shadow: 1px 1px 0 #000; }
.factBox p { font-size: clamp(14px, 3.5vw, 18px); line-height: 1.6; margin-bottom: 20px; }
.factBtn { background: #009c3b; color: #fff; border: 3px solid #000; padding: 12px 30px; font-size: clamp(14px, 3.5vw, 20px); font-weight: 900; border-radius: 8px; cursor: pointer; box-shadow: 0 4px 0 #005020; }
.factBtn:active { transform: translateY(2px); box-shadow: 0 2px 0 #005020; }
#winScreen { background: linear-gradient(135deg, #009c3b, #ffdf00, #002776); }
#winScreen h1 { font-size: clamp(30px, 8vw, 60px); color: #fff; text-shadow: 3px 3px 0 #000; margin-bottom: 20px; }
#winScreen p { font-size: clamp(14px, 3.5vw, 20px); color: #fff; text-shadow: 1px 1px 0 #000; max-width: 600px; margin-bottom: 25px; line-height: 1.6; }
#gameOverScreen { background: rgba(0,0,0,0.9); }
#gameOverScreen h1 { font-size: clamp(30px, 8vw, 60px); color: #ff3333; text-shadow: 3px 3px 0 #000; margin-bottom: 20px; }
#gameOverScreen p { font-size: clamp(14px, 3.5vw, 20px); color: #fff; margin-bottom: 25px; }
/* HUD */
#hud { position: absolute; top: 10px; left: 0; right: 0; padding: 0 15px; display: flex; justify-content: space-between; align-items: center; z-index: 50; pointer-events: none; }
.hudItem { background: rgba(0,0,0,0.6); color: #fff; padding: 6px 12px; border-radius: 6px; font-size: clamp(12px, 3vw, 16px); font-weight: bold; border: 2px solid #fff; }
.hudItem.score { color: #ff5555; }
/* Controles touch - aparecem só em telas pequenas ou dispositivos sem mouse */
#touchControls { position: absolute; bottom: 0; left: 0; right: 0; height: 140px; display: none; z-index: 40; pointer-events: none; }
@media (max-width: 900px) { #touchControls { display: block; } }
@media (hover: none) and (pointer: coarse) { #touchControls { display: block; } }
.touchBtn { position: absolute; bottom: 30px; width: 70px; height: 70px; border-radius: 50%; background: rgba(255,255,255,0.25); border: 3px solid rgba(255,255,255,0.7); color: #fff; font-size: 32px; font-weight: bold; display: flex; align-items: center; justify-content: center; pointer-events: auto; user-select: none; backdrop-filter: blur(2px); }
.touchBtn:active { background: rgba(255,255,255,0.55); transform: scale(0.92); }
#btnLeft { left: 20px; }
#btnRight { left: 100px; }
#btnJump { right: 20px; width: 88px; height: 88px; background: rgba(0,156,59,0.45); border-color: rgba(255,255,255,0.9); bottom: 25px; font-size: 38px; }
/* Tela de girar celular - aparece em mobile portrait */
#rotateHint {
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(135deg, #1a1a2e, #16213e);
display: none; flex-direction: column; align-items: center; justify-content: center;
z-index: 1000; text-align: center; padding: 30px; color: #fff;
}
#rotateHint .rotateIcon { font-size: 80px; animation: rotateIcon 2s ease-in-out infinite; }
@keyframes rotateIcon { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(90deg); } }
#rotateHint h2 { font-size: 22px; margin: 20px 0 10px; color: #ffdf00; }
#rotateHint p { font-size: 14px; max-width: 300px; line-height: 1.5; opacity: 0.9; }
@media (max-width: 768px) and (orientation: portrait) { #rotateHint { display: flex; } }
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="rotateHint">
<div class="rotateIcon">📱</div>
<h2>Gire seu celular</h2>
<p>Para uma melhor experiência de jogo, gire seu aparelho para o modo paisagem (deitado).</p>
</div>
<div id="hud" style="display:none;">
<div class="hudItem" id="levelDisplay">Fase 1</div>
<div class="hudItem score" id="scoreDisplay">★ 0</div>
<div class="hudItem" id="livesDisplay">❤ 3</div>
</div>
<div id="touchControls">
<div class="touchBtn" id="btnLeft">◀</div>
<div class="touchBtn" id="btnRight">▶</div>
<div class="touchBtn" id="btnJump">↑</div>
</div>
<div id="startScreen">
<h1>LULA</h1>
<h2>Uma Jornada Brasileira</h2>
<p>Viva a história de Luiz Inácio Lula da Silva — da infância no sertão de Pernambuco à Presidência da República. Colete estrelas, pule plataformas, enfrente desafios e aprenda fatos sobre um dos líderes mais influentes do Brasil!</p>
<button class="btnStart" id="startBtn">▶ COMEÇAR</button>
<p style="margin-top:20px; font-size: 12px; opacity:0.9;">Use as setas / espaço no teclado, ou os botões na tela do celular.</p>
</div>
<div id="levelIntro" style="display:none;">
<div class="levelNumber" id="introNumber">1</div>
<h2 id="introTitle">Caetés - Pernambuco</h2>
<p id="introText"></p>
<button class="btnStart" id="introBtn">COMEÇAR FASE</button>
</div>
<div id="factScreen" style="display:none;">
<div class="factBox">
<h3 id="factTitle">Você sabia?</h3>
<p id="factText"></p>
<button class="factBtn" id="factBtn">CONTINUAR ▶</button>
</div>
</div>
<div id="gameOverScreen" style="display:none;">
<h1>FIM DE JOGO</h1>
<p id="gameOverText">Você perdeu todas as vidas. Tente novamente!</p>
<button class="btnStart" id="retryBtn">↻ TENTAR DE NOVO</button>
</div>
<div id="winScreen" style="display:none;">
<h1>PARABÉNS!</h1>
<p id="winText"></p>
<button class="btnStart" id="restartBtn">↻ JOGAR NOVAMENTE</button>
</div>
</div>
<script>
// ============================================================
// LULA: UMA JORNADA BRASILEIRA - Jogo de plataforma HTML5
// ============================================================
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Dimensões lógicas do jogo (mundo virtual)
const GAME_WIDTH = 800;
const GAME_HEIGHT = 450;
let scale = 1;
let offsetX = 0;
let offsetY = 0;
// Variáveis do jogo
const GRAVITY = 0.6;
const JUMP_POWER = -12;
const MOVE_SPEED = 3.5;
const FRICTION = 0.85;
let gameState = 'start'; // start, intro, playing, fact, gameover, win
let currentLevel = 0;
let score = 0;
let lives = 3;
let cameraX = 0;
let levelData = null;
let player = null;
let enemies = [];
let collectibles = [];
let platforms = [];
let npcs = [];
let goal = null;
let particles = [];
let collectedStars = 0;
let totalStarsInLevel = 0;
let factsShown = [];
// Controle de input
const keys = { left: false, right: false, jump: false };
let jumpJustPressed = false;
// ============== RESPONSIVE CANVAS ==============
function resize() {
const container = document.getElementById('gameContainer');
const w = container.clientWidth;
const h = container.clientHeight;
// Ajusta mantendo proporção 16:9
const targetRatio = GAME_WIDTH / GAME_HEIGHT;
const screenRatio = w / h;
let canvasW, canvasH;
if (screenRatio > targetRatio) {
canvasH = h;
canvasW = h * targetRatio;
} else {
canvasW = w;
canvasH = w / targetRatio;
}
canvas.style.width = canvasW + 'px';
canvas.style.height = canvasH + 'px';
canvas.width = GAME_WIDTH;
canvas.height = GAME_HEIGHT;
ctx.imageSmoothingEnabled = false;
}
window.addEventListener('resize', resize);
resize();
// ============== LEVELS ==============
const levels = [
{
title: "Caetés - Pernambuco",
subtitle: "1945 - A Infância no Sertão",
intro: "Luiz Inácio da Silva nasceu em 27 de outubro de 1945, em Caetés (hoje Garanhuns), no agreste pernambucano. Sétimo de oito filhos, cresceu em profunda pobreza. Ajude o pequeno Lula a atravessar o sertão e coletar livros de escola!",
bgColor: '#7EC8E3',
groundColor: '#C19A6B',
groundTop: '#8B7355',
width: 3200,
platforms: [
{x:0, y:380, w:3200, h:70, type:'ground'},
{x:300, y:310, w:90, h:20, type:'platform'},
{x:450, y:280, w:80, h:20, type:'platform'},
{x:600, y:310, w:80, h:20, type:'platform'},
{x:750, y:280, w:80, h:20, type:'platform'},
{x:900, y:300, w:100, h:20, type:'platform'},
{x:1080, y:270, w:80, h:20, type:'platform'},
{x:1230, y:300, w:80, h:20, type:'platform'},
{x:1380, y:260, w:100, h:20, type:'platform'},
{x:1560, y:300, w:80, h:20, type:'platform'},
{x:1710, y:270, w:80, h:20, type:'platform'},
{x:1860, y:300, w:100, h:20, type:'platform'},
{x:2040, y:260, w:80, h:20, type:'platform'},
{x:2190, y:300, w:80, h:20, type:'platform'},
{x:2340, y:270, w:100, h:20, type:'platform'},
{x:2520, y:300, w:80, h:20, type:'platform'},
{x:2670, y:260, w:80, h:20, type:'platform'},
{x:2820, y:300, w:80, h:20, type:'platform'},
{x:2970, y:270, w:100, h:20, type:'platform'},
],
enemies: [
{x:500, y:350, range:[450,650], type:'cactus'},
{x:1200, y:350, range:[1100,1300], type:'cactus'},
{x:1900, y:350, range:[1800,2000], type:'cactus'},
{x:2600, y:350, range:[2500,2750], type:'cactus'},
],
collectibles: [
{x:325, y:265, type:'star'}, {x:475, y:235, type:'star'},
{x:625, y:265, type:'book'}, {x:775, y:235, type:'star'},
{x:925, y:255, type:'star'}, {x:1105, y:225, type:'book'},
{x:1255, y:255, type:'star'}, {x:1405, y:215, type:'book'},
{x:1585, y:255, type:'star'}, {x:1735, y:225, type:'star'},
{x:1885, y:255, type:'book'}, {x:2065, y:215, type:'star'},
{x:2215, y:255, type:'star'}, {x:2365, y:225, type:'book'},
{x:2545, y:255, type:'star'}, {x:2695, y:215, type:'star'},
{x:2845, y:255, type:'book'}, {x:2995, y:225, type:'star'},
],
npcs: [
{x:300, y:340, type:'mother', text:"Mãe Lindu: 'Filho, não tenha vergonha de ser pobre. Trabalhe, leia o que puder, e nunca deixe de aprender com a vida!'"},
{x:1500, y:340, type:'teacher', text:"Professora: 'A escola abre portas, mas o saber também vem das ruas, das fábricas e das conversas de quem vive.'"},
],
facts: [
"Lula nasceu em 27 de outubro de 1945 em Caetés, Pernambuco. Era o sétimo de oito filhos de Eurídice Ferreira de Melo (Dona Lindu) e Aristides Inácio da Silva.",
"O apelido 'Lula' vem de 'Luís', como era chamado na infância. Mais tarde, ao registrar o nome para candidato, adotou oficialmente 'Lula' como parte do nome.",
"Antes de aprender a ler, Lula vendia laranjas, amendoim e castanhas nas ruas para ajudar a família. Aprendeu muito cedo que o trabalho e a vivência ensinam o que nenhum livro ensina."
]
},
{
title: "A Grande Viagem",
subtitle: "1952 - Migração para São Paulo",
intro: "Em 1952, com apenas 7 anos, Lula viajou com a mãe e os irmãos durante 23 dias de pau-de-arara até São Paulo, para reencontrar o pai. Atravesse os obstáculos dessa longa jornada!",
bgColor: '#FFA07A',
groundColor: '#8B4513',
groundTop: '#CD853F',
width: 3400,
platforms: [
{x:0, y:380, w:3400, h:70, type:'ground'},
{x:280, y:310, w:80, h:20, type:'platform'},
{x:430, y:280, w:80, h:20, type:'platform'},
{x:580, y:310, w:80, h:20, type:'platform'},
{x:730, y:270, w:80, h:20, type:'platform'},
{x:880, y:300, w:80, h:20, type:'platform'},
{x:1030, y:260, w:80, h:20, type:'platform'},
{x:1180, y:300, w:100, h:20, type:'platform'},
{x:1360, y:260, w:80, h:20, type:'platform'},
{x:1510, y:300, w:80, h:20, type:'platform'},
{x:1660, y:270, w:80, h:20, type:'platform'},
{x:1810, y:300, w:100, h:20, type:'platform'},
{x:1990, y:260, w:80, h:20, type:'platform'},
{x:2140, y:300, w:80, h:20, type:'platform'},
{x:2290, y:260, w:80, h:20, type:'platform'},
{x:2440, y:300, w:100, h:20, type:'platform'},
{x:2620, y:260, w:80, h:20, type:'platform'},
{x:2770, y:300, w:80, h:20, type:'platform'},
{x:2920, y:260, w:80, h:20, type:'platform'},
{x:3070, y:300, w:100, h:20, type:'platform'},
{x:3250, y:270, w:100, h:20, type:'platform'},
],
enemies: [
{x:550, y:350, range:[500,700], type:'truck'},
{x:1300, y:350, range:[1250,1400], type:'truck'},
{x:2200, y:350, range:[2150,2350], type:'truck'},
{x:2900, y:350, range:[2850,3000], type:'truck'},
],
collectibles: [
{x:305, y:265, type:'star'}, {x:455, y:235, type:'star'},
{x:605, y:265, type:'book'}, {x:755, y:225, type:'star'},
{x:905, y:255, type:'star'}, {x:1055, y:215, type:'book'},
{x:1205, y:255, type:'star'}, {x:1385, y:215, type:'book'},
{x:1535, y:255, type:'star'}, {x:1685, y:225, type:'star'},
{x:1835, y:255, type:'book'}, {x:2015, y:215, type:'star'},
{x:2165, y:255, type:'star'}, {x:2315, y:215, type:'book'},
{x:2465, y:255, type:'star'}, {x:2645, y:215, type:'star'},
{x:2795, y:255, type:'book'}, {x:2945, y:215, type:'star'},
{x:3095, y:255, type:'star'}, {x:3275, y:225, type:'book'},
],
npcs: [
{x:200, y:340, type:'mother', text:"Mãe Lindu: 'Vamos, filhos! Em São Paulo teremos uma vida melhor.'"},
{x:1800, y:340, type:'driver', text:"Motorista: 'Pau-de-arava' - 23 dias de viagem até São Paulo!"},
],
facts: [
"Em dezembro de 1952, Lula, sua mãe Dona Lindu e os irmãos viajaram 23 dias no pau-de-arara (caminhão) de Pernambuco até o litoral de São Paulo.",
"Dona Lindu decidiu migrar para reencontrar o marido Aristides, que havia saído de Pernambuco antes para trabalhar no litoral paulista.",
"A família se instalou em Vicente de Carvalho, bairro pobre de Guarujá. Lula aprendeu a ler aos 10 anos e, desde cedo, somou estudo, trabalho e observação da vida ao seu redor."
]
},
{
title: "O Metalúrgico",
subtitle: "Anos 60 - Trabalho nas Fábricas",
intro: "Em São Bernardo do Campo, Lula trabalhou como office-boy, engraxate e finalmente metalúrgico. Aos 19 anos, perdeu o dedo mínimo num acidente de trabalho. Colete ferramentas e evite os perigos da fábrica!",
bgColor: '#708090',
groundColor: '#696969',
groundTop: '#A9A9A9',
bgType: 'industrial',
width: 3600,
platforms: [
{x:0, y:380, w:3600, h:70, type:'ground'},
{x:280, y:310, w:90, h:20, type:'platform'},
{x:430, y:280, w:80, h:20, type:'platform'},
{x:580, y:310, w:80, h:20, type:'platform'},
{x:730, y:270, w:80, h:20, type:'platform'},
{x:880, y:300, w:90, h:20, type:'platform'},
{x:1050, y:260, w:80, h:20, type:'platform'},
{x:1200, y:300, w:80, h:20, type:'platform'},
{x:1350, y:260, w:100, h:20, type:'platform'},
{x:1530, y:300, w:80, h:20, type:'platform'},
{x:1680, y:270, w:80, h:20, type:'platform'},
{x:1830, y:300, w:100, h:20, type:'platform'},
{x:2010, y:260, w:80, h:20, type:'platform'},
{x:2160, y:300, w:80, h:20, type:'platform'},
{x:2310, y:270, w:100, h:20, type:'platform'},
{x:2490, y:300, w:80, h:20, type:'platform'},
{x:2640, y:260, w:80, h:20, type:'platform'},
{x:2790, y:300, w:80, h:20, type:'platform'},
{x:2940, y:260, w:100, h:20, type:'platform'},
{x:3120, y:300, w:80, h:20, type:'platform'},
{x:3270, y:270, w:100, h:20, type:'platform'},
{x:3450, y:300, w:100, h:20, type:'platform'},
],
enemies: [
{x:600, y:350, range:[550,750], type:'machine'},
{x:1300, y:350, range:[1250,1400], type:'machine'},
{x:2000, y:350, range:[1950,2150], type:'machine'},
{x:2700, y:350, range:[2650,2850], type:'machine'},
{x:3300, y:350, range:[3250,3450], type:'machine'},
],
collectibles: [
{x:310, y:265, type:'tool'}, {x:460, y:235, type:'star'},
{x:610, y:265, type:'tool'}, {x:760, y:225, type:'star'},
{x:910, y:255, type:'tool'}, {x:1080, y:215, type:'star'},
{x:1230, y:255, type:'tool'}, {x:1380, y:215, type:'star'},
{x:1555, y:255, type:'tool'}, {x:1705, y:225, type:'star'},
{x:1855, y:255, type:'tool'}, {x:2035, y:215, type:'star'},
{x:2185, y:255, type:'tool'}, {x:2335, y:225, type:'star'},
{x:2515, y:255, type:'tool'}, {x:2665, y:215, type:'star'},
{x:2815, y:255, type:'tool'}, {x:2965, y:215, type:'star'},
{x:3145, y:255, type:'tool'}, {x:3295, y:225, type:'star'},
{x:3475, y:255, type:'tool'},
],
npcs: [
{x:300, y:340, type:'worker', text:"Instrutor do SENAI: 'Aqui você aprende um ofício. Torneiro mecânico é ofício de quem trabalha com a mão e com a cabeça!'"},
{x:2200, y:340, type:'doctor', text:"Médico: 'Em 1964, Lula perdeu o dedo mínimo num acidente na fábrica. Aquilo o marcou e o fez entender que o trabalhador precisava de direitos.'"},
],
facts: [
"Em São Bernardo do Campo, Lula trabalhou como engraxate, office-boy e aprendiz de torneiro mecânico. Formou-se torneiro mecânico pelo SENAI em 1963 — um curso técnico que abriu suas primeiras portas no mundo do trabalho.",
"Sua formação política veio de leituras, conversas com colegas operários, sindicalistas e intelectuais que se aproximaram dele ao longo dos anos.",
"Em 1964, aos 19 anos, Lula sofreu um acidente de trabalho que amputou seu dedo mínimo esquerdo. Sem proteção trabalhista, percebeu que só a organização coletiva poderia mudar isso."
]
},
{
title: "Líder Sindical",
subtitle: "1970s - As Grandes Greves do ABC",
intro: "Nos anos 70, Lula liderou as maiores greves da história do Brasil, enfrentando a ditadura militar. Conduza o líder operário pelas fábricas e organize os trabalhadores!",
bgColor: '#B0C4DE',
groundColor: '#4A4A4A',
groundTop: '#6B6B6B',
bgType: 'industrial',
width: 3800,
platforms: [
{x:0, y:380, w:3800, h:70, type:'ground'},
{x:280, y:310, w:80, h:20, type:'platform'},
{x:430, y:280, w:80, h:20, type:'platform'},
{x:580, y:310, w:80, h:20, type:'platform'},
{x:730, y:270, w:80, h:20, type:'platform'},
{x:880, y:300, w:100, h:20, type:'platform'},
{x:1060, y:260, w:80, h:20, type:'platform'},
{x:1210, y:300, w:80, h:20, type:'platform'},
{x:1360, y:260, w:100, h:20, type:'platform'},
{x:1540, y:300, w:80, h:20, type:'platform'},
{x:1690, y:270, w:80, h:20, type:'platform'},
{x:1840, y:300, w:100, h:20, type:'platform'},
{x:2020, y:260, w:80, h:20, type:'platform'},
{x:2170, y:300, w:80, h:20, type:'platform'},
{x:2320, y:270, w:100, h:20, type:'platform'},
{x:2500, y:300, w:80, h:20, type:'platform'},
{x:2650, y:260, w:80, h:20, type:'platform'},
{x:2800, y:300, w:80, h:20, type:'platform'},
{x:2950, y:260, w:100, h:20, type:'platform'},
{x:3130, y:300, w:80, h:20, type:'platform'},
{x:3280, y:270, w:80, h:20, type:'platform'},
{x:3430, y:300, w:80, h:20, type:'platform'},
{x:3580, y:270, w:140, h:20, type:'platform'},
],
enemies: [
{x:500, y:350, range:[450,600], type:'soldier'},
{x:1100, y:350, range:[1050,1200], type:'soldier'},
{x:1700, y:350, range:[1650,1850], type:'soldier'},
{x:2300, y:350, range:[2250,2400], type:'soldier'},
{x:2900, y:350, range:[2850,3000], type:'soldier'},
{x:3400, y:350, range:[3350,3500], type:'soldier'},
],
collectibles: [
{x:305, y:265, type:'megaphone'}, {x:455, y:235, type:'star'},
{x:605, y:265, type:'megaphone'}, {x:755, y:225, type:'star'},
{x:905, y:255, type:'megaphone'}, {x:1090, y:215, type:'star'},
{x:1240, y:255, type:'megaphone'}, {x:1390, y:215, type:'star'},
{x:1565, y:255, type:'megaphone'}, {x:1715, y:225, type:'star'},
{x:1865, y:255, type:'megaphone'}, {x:2045, y:215, type:'star'},
{x:2195, y:255, type:'megaphone'}, {x:2345, y:225, type:'star'},
{x:2525, y:255, type:'megaphone'}, {x:2675, y:215, type:'star'},
{x:2825, y:255, type:'megaphone'}, {x:2975, y:215, type:'star'},
{x:3155, y:255, type:'megaphone'}, {x:3305, y:225, type:'star'},
{x:3455, y:255, type:'megaphone'}, {x:3605, y:225, type:'star'},
],
npcs: [
{x:250, y:340, type:'worker', text:"Operário: 'Lula, fale por nós! Não aguentamos mais!'"},
{x:2400, y:340, type:'journalist', text:"Jornalista: 'Em 1979, 170 mil metalúrgicos pararam o ABC!'"},
],
facts: [
"Em 1975, Lula foi eleito presidente do Sindicato dos Metalúrgicos de São Bernardo do Campo e Diadema, com 92% dos votos.",
"Lula estudava política, história e direito sindical por conta própria. Lia jornais, livros e debatia com intelectuais — sua outra universidade foi o movimento operário.",
"Preso em 1980 pela Lei de Segurança Nacional, Lula ficou 31 dias detido. O movimento internacional pressionou o regime pela sua libertação."
]
},
{
title: "Fundação do PT",
subtitle: "1980 - O Partido dos Trabalhadores",
intro: "Em 10 de fevereiro de 1980, Lula ajudou a fundar o Partido dos Trabalhadores (PT), unindo operários, intelectuais e movimentos sociais. Conduza o movimento pela democracia!",
bgColor: '#FF6347',
groundColor: '#8B4513',
groundTop: '#D2691E',
width: 4000,
platforms: [
{x:0, y:380, w:4000, h:70, type:'ground'},
{x:280, y:310, w:90, h:20, type:'platform'},
{x:430, y:280, w:80, h:20, type:'platform'},
{x:580, y:310, w:80, h:20, type:'platform'},
{x:730, y:270, w:80, h:20, type:'platform'},
{x:880, y:300, w:90, h:20, type:'platform'},
{x:1050, y:260, w:80, h:20, type:'platform'},
{x:1200, y:300, w:80, h:20, type:'platform'},
{x:1350, y:260, w:100, h:20, type:'platform'},
{x:1530, y:300, w:80, h:20, type:'platform'},
{x:1680, y:270, w:80, h:20, type:'platform'},
{x:1830, y:300, w:100, h:20, type:'platform'},
{x:2010, y:260, w:80, h:20, type:'platform'},
{x:2160, y:300, w:80, h:20, type:'platform'},
{x:2310, y:270, w:100, h:20, type:'platform'},
{x:2490, y:300, w:80, h:20, type:'platform'},
{x:2640, y:260, w:80, h:20, type:'platform'},
{x:2790, y:300, w:80, h:20, type:'platform'},
{x:2940, y:260, w:100, h:20, type:'platform'},
{x:3120, y:300, w:80, h:20, type:'platform'},
{x:3270, y:270, w:80, h:20, type:'platform'},
{x:3420, y:300, w:80, h:20, type:'platform'},
{x:3570, y:270, w:80, h:20, type:'platform'},
{x:3720, y:300, w:140, h:20, type:'platform'},
],
enemies: [
{x:500, y:350, range:[450,600], type:'soldier'},
{x:1100, y:350, range:[1050,1200], type:'soldier'},
{x:1700, y:350, range:[1650,1850], type:'soldier'},
{x:2400, y:350, range:[2350,2500], type:'soldier'},
{x:3000, y:350, range:[2950,3100], type:'soldier'},
{x:3500, y:350, range:[3450,3600], type:'soldier'},
],
collectibles: [
{x:310, y:265, type:'flag'}, {x:460, y:235, type:'star'},
{x:610, y:265, type:'flag'}, {x:760, y:225, type:'star'},
{x:910, y:255, type:'flag'}, {x:1080, y:215, type:'star'},
{x:1230, y:255, type:'flag'}, {x:1380, y:215, type:'star'},
{x:1555, y:255, type:'flag'}, {x:1705, y:225, type:'star'},
{x:1855, y:255, type:'flag'}, {x:2035, y:215, type:'star'},
{x:2185, y:255, type:'flag'}, {x:2335, y:225, type:'star'},
{x:2515, y:255, type:'flag'}, {x:2665, y:215, type:'star'},
{x:2815, y:255, type:'flag'}, {x:2965, y:215, type:'star'},
{x:3145, y:255, type:'flag'}, {x:3295, y:225, type:'star'},
{x:3445, y:255, type:'flag'}, {x:3595, y:225, type:'star'},
{x:3745, y:255, type:'flag'},
],
npcs: [
{x:300, y:340, type:'intellectual', text:"Intelectual: 'Vamos criar um partido dos trabalhadores, sem patrões no comando!'"},
{x:2500, y:340, type:'worker', text:"Operário: 'PT nasceu da luta real do povo brasileiro!'"},
],
facts: [
"Em 10 de fevereiro de 1980, cerca de 1,5 mil pessoas reunidas no Colégio Sion, em São Paulo, fundaram o Partido dos Trabalhadores (PT).",
"O PT nasceu como partido de massas, unindo operários e intelectuais, sindicatos, movimentos sociais e cristãos progressistas ligados à Teologia da Libertação.",
"Lula se candidatou a presidente em 1982, 1989, 1994, 1998, 2002, 2006, 2018 e 2022 — vencendo em 2002, 2006 e 2022."
]
},
{
title: "Presidente do Brasil",
subtitle: "2003 e 2023 - Liderando a Nação",
intro: "Lula se tornou presidente do Brasil em 1º de janeiro de 2003 e novamente em 2023. Implementou programas como Bolsa Família, Fome Zero e Minha Casa Minha Vida. Leve o Brasil à bandeira final!",
bgColor: '#87CEEB',
groundColor: '#228B22',
groundTop: '#32CD32',
width: 4200,
platforms: [
{x:0, y:380, w:4200, h:70, type:'ground'},
{x:280, y:310, w:100, h:20, type:'platform'},
{x:430, y:280, w:80, h:20, type:'platform'},
{x:580, y:310, w:80, h:20, type:'platform'},
{x:730, y:270, w:80, h:20, type:'platform'},
{x:880, y:300, w:100, h:20, type:'platform'},
{x:1060, y:260, w:80, h:20, type:'platform'},
{x:1210, y:300, w:80, h:20, type:'platform'},
{x:1360, y:260, w:100, h:20, type:'platform'},
{x:1540, y:300, w:80, h:20, type:'platform'},
{x:1690, y:270, w:80, h:20, type:'platform'},
{x:1840, y:300, w:100, h:20, type:'platform'},
{x:2020, y:260, w:80, h:20, type:'platform'},
{x:2170, y:300, w:80, h:20, type:'platform'},
{x:2320, y:270, w:100, h:20, type:'platform'},
{x:2500, y:300, w:80, h:20, type:'platform'},
{x:2650, y:260, w:80, h:20, type:'platform'},
{x:2800, y:300, w:80, h:20, type:'platform'},
{x:2950, y:260, w:100, h:20, type:'platform'},
{x:3130, y:300, w:80, h:20, type:'platform'},
{x:3280, y:270, w:80, h:20, type:'platform'},
{x:3430, y:300, w:80, h:20, type:'platform'},
{x:3580, y:260, w:80, h:20, type:'platform'},
{x:3730, y:290, w:80, h:20, type:'platform'},
{x:3880, y:260, w:140, h:20, type:'platform'},
],
enemies: [
{x:500, y:350, range:[450,600], type:'corruption'},
{x:1100, y:350, range:[1050,1200], type:'corruption'},
{x:1700, y:350, range:[1650,1850], type:'corruption'},
{x:2400, y:350, range:[2350,2500], type:'corruption'},
{x:3000, y:350, range:[2950,3100], type:'corruption'},
{x:3500, y:350, range:[3450,3600], type:'corruption'},
],
collectibles: [
{x:310, y:265, type:'house'}, {x:460, y:235, type:'star'},
{x:610, y:265, type:'house'}, {x:760, y:225, type:'star'},
{x:910, y:255, type:'bolsa'}, {x:1080, y:215, type:'star'},
{x:1230, y:255, type:'house'}, {x:1380, y:215, type:'bolsa'},
{x:1555, y:255, type:'star'}, {x:1705, y:225, type:'house'},
{x:1855, y:255, type:'bolsa'}, {x:2035, y:215, type:'star'},
{x:2185, y:255, type:'house'}, {x:2335, y:225, type:'bolsa'},
{x:2515, y:255, type:'star'}, {x:2665, y:215, type:'house'},
{x:2815, y:255, type:'bolsa'}, {x:2965, y:215, type:'star'},
{x:3145, y:255, type:'house'}, {x:3295, y:225, type:'bolsa'},
{x:3445, y:255, type:'star'}, {x:3595, y:215, type:'house'},
{x:3745, y:245, type:'bolsa'}, {x:3900, y:215, type:'star'},
],
npcs: [
{x:300, y:340, type:'citizen', text:"Cidadã: 'Bolsa Família mudou minha vida! Meus filhos podem estudar!'"},
{x:2500, y:340, type:'citizen', text:"Cidadão: 'Minha Casa Minha Vida me deu teto próprio!'"},
{x:3800, y:340, type:'citizen', text:"Cidadão mundial: 'Lula é um símbolo global da luta contra a fome!'"},
],
facts: [
"Como presidente (2003-2010), Lula implementou o Fome Zero e o Bolsa Família, tirando milhões de brasileiros da miséria extrema.",
"Sob o governo Lula, o Brasil criou o Minha Casa Minha Vida, o PROUNI e ampliou o acesso ao ensino superior — abrindo a universidade para os filhos dos trabalhadores.",
"Em 2022, Lula se tornou o primeiro brasileiro eleito presidente por três vezes (2002, 2006 e 2022). Voltou ao cargo em 1º de janeiro de 2023."
]
}
];
// ============== PLAYER ==============
function createPlayer(x, y) {
return {
x: x, y: y,
vx: 0, vy: 0,
width: 24, height: 36,
onGround: false,
facing: 1,
animFrame: 0,
animTimer: 0,
invulnerable: 0,
};
}
// ============== LEVEL LOADER ==============
function loadLevel(levelIndex) {
currentLevel = levelIndex;
const lv = levels[levelIndex];
levelData = lv;
cameraX = 0;
factsShown = [];
platforms = lv.platforms.map(p => ({...p}));
enemies = lv.enemies.map(e => ({...e, alive: true, dir: 1, speed: 0.8 + Math.random() * 0.4}));
collectibles = lv.collectibles.map(c => ({...c, collected: false}));
npcs = lv.npcs.map(n => ({...n, talked: false}));
goal = {x: lv.width - 180, y: 200, w: 80, h: 180};
totalStarsInLevel = collectibles.length;
collectedStars = 0;
particles = [];
floatingTexts = [];
player = createPlayer(60, 300);
document.getElementById('levelDisplay').textContent = 'Fase ' + (levelIndex + 1);
document.getElementById('introNumber').textContent = levelIndex + 1;
document.getElementById('introTitle').textContent = lv.title;
document.getElementById('introText').textContent = lv.intro;
showScreen('levelIntro');
}
// ============== SCREEN MANAGEMENT ==============
function showScreen(name) {
['startScreen', 'levelIntro', 'factScreen', 'winScreen', 'gameOverScreen'].forEach(s => {
document.getElementById(s).style.display = 'none';
});
if (name !== 'game') {
document.getElementById(name).style.display = 'flex';
document.getElementById('hud').style.display = 'none';
} else {
document.getElementById('hud').style.display = 'flex';
}
}
function showFact(index) {
if (index >= levelData.facts.length) {
// Todos os fatos foram exibidos — avança para próxima fase ou vitória
if (currentLevel < levels.length - 1) {
loadLevel(currentLevel + 1);
} else {
// Última fase concluída!
gameState = 'win';
document.getElementById('winText').textContent =
'Você completou a jornada de Luiz Inácio Lula da Silva — da infância no sertão ' +
'de Pernambuco à Presidência da República! Pontuação final: ' + score + '. ' +
'Lula é um dos líderes mais influentes da história do Brasil, tendo sido eleito ' +
'presidente por três vezes (2002, 2006 e 2022). Sua história mostra que o saber ' +
'vem de muitas fontes: a escola, o trabalho manual, a leitura, a vivência sindical ' +
'e a organização coletiva. Com coragem, persistência e diálogo, é possível ' +
'transformar não apenas a própria vida, mas o país inteiro.';
showScreen('winScreen');
}
return;
}
const isLast = index === levelData.facts.length - 1;
document.getElementById('factTitle').textContent = 'Você sabia?';
document.getElementById('factText').textContent = levelData.facts[index];
document.getElementById('factBtn').textContent = isLast
? (currentLevel < levels.length - 1 ? 'PRÓXIMA FASE ▶' : 'CONCLUIR JORNADA ▶')
: 'CONTINUAR ▶';
document.getElementById('factBtn').onclick = () => {
document.getElementById('factScreen').style.display = 'none';
showFact(index + 1);
};
gameState = 'fact';
document.getElementById('factScreen').style.display = 'flex';
document.getElementById('hud').style.display = 'none';
}
// ============== INPUT ==============
document.addEventListener('keydown', e => {
if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') keys.left = true;
if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') keys.right = true;
if (e.key === ' ' || e.key === 'ArrowUp' || e.key === 'w' || e.key === 'W' || e.key === 'z' || e.key === 'Z') {
if (!keys.jump) jumpJustPressed = true;
keys.jump = true;
e.preventDefault();
}
});
document.addEventListener('keyup', e => {
if (e.key === 'ArrowLeft' || e.key === 'a' || e.key === 'A') keys.left = false;
if (e.key === 'ArrowRight' || e.key === 'd' || e.key === 'D') keys.right = false;
if (e.key === ' ' || e.key === 'ArrowUp' || e.key === 'w' || e.key === 'W' || e.key === 'z' || e.key === 'Z') keys.jump = false;
});
// Touch controls
function bindTouch(elId, key) {
const el = document.getElementById(elId);
el.addEventListener('touchstart', e => { e.preventDefault(); keys[key] = true; if (key === 'jump') jumpJustPressed = true; }, {passive:false});
el.addEventListener('touchend', e => { e.preventDefault(); keys[key] = false; }, {passive:false});
el.addEventListener('touchcancel', e => { e.preventDefault(); keys[key] = false; }, {passive:false});
el.addEventListener('mousedown', e => { e.preventDefault(); keys[key] = true; if (key === 'jump') jumpJustPressed = true; });
el.addEventListener('mouseup', e => { e.preventDefault(); keys[key] = false; });
el.addEventListener('mouseleave', e => { keys[key] = false; });
}
bindTouch('btnLeft', 'left');
bindTouch('btnRight', 'right');
bindTouch('btnJump', 'jump');
// ============== BUTTONS ==============
document.getElementById('startBtn').onclick = () => {
score = 0;
lives = 3;
loadLevel(0);
};
document.getElementById('introBtn').onclick = () => {
gameState = 'playing';
showScreen('game');
updateHUD();
};
document.getElementById('retryBtn').onclick = () => {
lives = 3;
score = 0;
loadLevel(0);
};
document.getElementById('restartBtn').onclick = () => {
lives = 3;
score = 0;
loadLevel(0);
};
// ============== UPDATE HUD ==============
function updateHUD() {
document.getElementById('scoreDisplay').textContent = '★ ' + score;
document.getElementById('livesDisplay').textContent = '❤ ' + lives;
document.getElementById('levelDisplay').textContent = 'Fase ' + (currentLevel + 1);
}
// ============== PHYSICS & UPDATE ==============
function update() {
if (gameState !== 'playing') return;
// Player movement
if (keys.left) { player.vx -= 0.5; player.facing = -1; }
if (keys.right) { player.vx += 0.5; player.facing = 1; }
player.vx *= FRICTION;
if (Math.abs(player.vx) > MOVE_SPEED) player.vx = MOVE_SPEED * Math.sign(player.vx);
if (Math.abs(player.vx) < 0.1) player.vx = 0;
if (jumpJustPressed && player.onGround) {
player.vy = JUMP_POWER;
player.onGround = false;
}
jumpJustPressed = false;
player.vy += GRAVITY;
if (player.vy > 15) player.vy = 15;
// Move X
player.x += player.vx;
if (player.x < 0) player.x = 0;
if (player.x + player.width > levelData.width) player.x = levelData.width - player.width;
// Collide X
for (const p of platforms) {
if (rectOverlap(player, p)) {
if (player.vx > 0) player.x = p.x - player.width;
else if (player.vx < 0) player.x = p.x + p.w;
player.vx = 0;
}
}
// Move Y
player.y += player.vy;
player.onGround = false;
// Collide Y
for (const p of platforms) {
if (rectOverlap(player, p)) {
if (player.vy > 0) {
player.y = p.y - player.height;
player.vy = 0;
player.onGround = true;
} else if (player.vy < 0) {
player.y = p.y + p.h;
player.vy = 0;
}
}
}
// Anim
player.animTimer++;
if (player.animTimer > 6) {
player.animTimer = 0;
player.animFrame = (player.animFrame + 1) % 4;
}
if (player.invulnerable > 0) player.invulnerable--;
// Fall off the world
if (player.y > GAME_HEIGHT + 100) {
damagePlayer();
}
// Update enemies
for (const e of enemies) {
if (!e.alive) continue;
e.x += e.dir * e.speed;
if (e.x < e.range[0]) { e.x = e.range[0]; e.dir = 1; }
if (e.x > e.range[1]) { e.x = e.range[1]; e.dir = -1; }
// Collision with player
const enemyRect = {x: e.x, y: e.y, w: 36, h: 36};
if (player.invulnerable === 0 && rectOverlap(player, enemyRect)) {
// Stomp check
if (player.vy > 0 && player.y + player.height < e.y + 18) {
e.alive = false;
player.vy = JUMP_POWER * 0.6;
score += 50;
createParticles(e.x + 18, e.y + 18, '#ff0000', 12);
createFloatingText(e.x + 18, e.y, '+50', '#ff3333');
updateHUD();
} else {
damagePlayer();
}
}
}
// Collectibles
for (const c of collectibles) {
if (c.collected) continue;
const cr = {x: c.x, y: c.y, w: 24, h: 24};
if (rectOverlap(player, cr)) {
c.collected = true;
collectedStars++;
const points = c.type === 'star' ? 10 : 25;
score += points;
createParticles(c.x + 12, c.y + 12, '#ff2020', 10);
createFloatingText(c.x + 12, c.y, '+' + points, '#ff5555');
updateHUD();
}
}
// NPCs
for (const n of npcs) {
if (n.talked) continue;
const nr = {x: n.x, y: n.y, w: 30, h: 40};
if (rectOverlap(player, nr)) {
n.talked = true;
document.getElementById('factTitle').textContent = 'Conversa';
document.getElementById('factText').textContent = n.text;
document.getElementById('factBtn').onclick = () => {
gameState = 'playing';
document.getElementById('factScreen').style.display = 'none';
document.getElementById('hud').style.display = 'flex';
};
gameState = 'fact';
document.getElementById('factScreen').style.display = 'flex';
document.getElementById('hud').style.display = 'none';
return;
}
}
// Goal reached
if (rectOverlap(player, goal)) {
// Show next fact (one per level completion)
showFact(0);
return;
}
// Camera follows player
const targetCameraX = player.x - GAME_WIDTH / 2 + player.width / 2;
cameraX += (targetCameraX - cameraX) * 0.1;
if (cameraX < 0) cameraX = 0;
if (cameraX > levelData.width - GAME_WIDTH) cameraX = levelData.width - GAME_WIDTH;
// Update particles
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.vy += 0.2;
p.life--;
if (p.life <= 0) particles.splice(i, 1);
}
// Update floating texts
for (let i = floatingTexts.length - 1; i >= 0; i--) {
const t = floatingTexts[i];
t.y += t.vy;
t.vy *= 0.95;
t.life--;
if (t.life <= 0) floatingTexts.splice(i, 1);
}
}
function rectOverlap(a, b) {
return a.x < b.x + b.w && a.x + a.width > b.x && a.y < b.y + b.h && a.y + a.height > b.y;
}
function damagePlayer() {
if (player.invulnerable > 0) return;
lives--;
player.invulnerable = 90;
player.x = 60;
player.y = 300;
player.vx = 0;
player.vy = 0;
cameraX = 0;
updateHUD();
if (lives <= 0) {
gameState = 'gameover';
document.getElementById('gameOverText').textContent = 'Você perdeu todas as vidas. Tente novamente!';
showScreen('gameOverScreen');
}
}
function createParticles(x, y, color, count) {
for (let i = 0; i < count; i++) {
particles.push({
x: x, y: y,
vx: (Math.random() - 0.5) * 5,
vy: -Math.random() * 4 - 1,
life: 30 + Math.random() * 20,
color: color,
size: 2 + Math.random() * 3
});
}
}
let floatingTexts = [];
function createFloatingText(x, y, text, color) {
floatingTexts.push({ x: x, y: y, text: text, color: color, life: 60, vy: -1 });
}
// ============== RENDERING ==============
function render() {
ctx.fillStyle = levelData ? levelData.bgColor : '#5c94fc';
ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
if (!levelData) return;
// Draw clouds/background elements
drawBackground();
ctx.save();
ctx.translate(-Math.floor(cameraX), 0);
// Draw platforms
for (const p of platforms) {
drawPlatform(p);
}
// Draw goal
drawGoal();
// Draw collectibles
for (const c of collectibles) {
if (!c.collected) drawCollectible(c);
}
// Draw NPCs
for (const n of npcs) {
drawNPC(n);
}
// Draw enemies
for (const e of enemies) {
if (e.alive) drawEnemy(e);
}
// Draw particles
for (const p of particles) {
ctx.fillStyle = p.color;
ctx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size);
}
// Draw player
drawPlayer();
// Draw floating texts (após o player, sobre tudo)
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'center';
for (const t of floatingTexts) {
const alpha = Math.min(1, t.life / 30);
ctx.fillStyle = `rgba(0,0,0,${alpha})`;
ctx.fillText(t.text, t.x + 1, t.y + 1);
ctx.fillStyle = t.color;
ctx.globalAlpha = alpha;
ctx.fillText(t.text, t.x, t.y);
ctx.globalAlpha = 1;
}
ctx.textAlign = 'left';
ctx.restore();
}
function drawBackground() {
const bgType = levelData.bgType || 'tropical';
// Camada 1: Morros/fábricas distantes (parallax lento)
const farOffset = cameraX * 0.2;
if (bgType === 'industrial') {
// Fábricas ao fundo para a fase industrial
ctx.fillStyle = 'rgba(80, 80, 90, 0.6)';
for (let i = -1; i < 10; i++) {
const hx = ((i * 380 - farOffset) % (levelData.width + 400) + (levelData.width + 400)) % (levelData.width + 400);
drawHill(hx, 340, 220, 70);
}
// Fábricas
const factoryOffset = cameraX * 0.25;
for (let i = -1; i < 8; i++) {
const fx = ((i * 480 - factoryOffset) % (levelData.width + 500) + (levelData.width + 500)) % (levelData.width + 500);
drawFactory(fx, 380);
}
} else {
// Morros verdes para as outras fases
ctx.fillStyle = 'rgba(60, 130, 70, 0.55)';
for (let i = -1; i < 16; i++) {
const hx = ((i * 260 - farOffset) % (levelData.width + 400) + (levelData.width + 400)) % (levelData.width + 400);
drawHill(hx, 340, 220, 90);
}
// Morros mais próximos (parallax médio)
const nearHillOffset = cameraX * 0.4;
ctx.fillStyle = 'rgba(40, 110, 50, 0.7)';
for (let i = -1; i < 14; i++) {
const hx = ((i * 280 - nearHillOffset) % (levelData.width + 300) + (levelData.width + 300)) % (levelData.width + 300);
drawHill(hx, 360, 180, 60);
}
// Palmeiras/coqueiros decorativos ao longe (parallax médio)
const treeOffset = cameraX * 0.5;
for (let i = -1; i < 18; i++) {
const tx = ((i * 200 - treeOffset) % (levelData.width + 200) + (levelData.width + 200)) % (levelData.width + 200);
drawPalmTree(tx, 380);
}
}
// Nuvens (parallax médio) - sempre presentes
ctx.fillStyle = 'rgba(255,255,255,0.85)';
const cloudOffset = cameraX * 0.3;
for (let i = -1; i < 8; i++) {
const cx = ((i * 400 - cloudOffset) % (levelData.width + 400) + (levelData.width + 400)) % (levelData.width + 400);
const cy = 60 + (i % 3) * 30;
drawCloud(cx, cy);
}
}
function drawHill(x, y, w, h) {
// Morro arredondado estilo Mata Atlântica / morros brasileiros
ctx.beginPath();
ctx.moveTo(x, y);
ctx.bezierCurveTo(x, y - h, x + w, y - h, x + w, y);
ctx.fill();
}
function drawPalmTree(x, y) {
// Tronco
ctx.fillStyle = '#6b4423';
ctx.fillRect(x + 8, y - 50, 4, 50);
// Folhas (palmeira)
ctx.fillStyle = '#1a6b1a';
ctx.beginPath();
ctx.ellipse(x + 10, y - 50, 18, 6, Math.PI / 4, 0, Math.PI * 2);
ctx.ellipse(x + 10, y - 50, 18, 6, -Math.PI / 4, 0, Math.PI * 2);
ctx.ellipse(x + 10, y - 50, 18, 6, 0, 0, Math.PI * 2);
ctx.fill();
// Cocôs
ctx.fillStyle = '#5c3317';
ctx.beginPath();
ctx.arc(x + 6, y - 47, 2.5, 0, Math.PI * 2);
ctx.arc(x + 14, y - 47, 2.5, 0, Math.PI * 2);
ctx.fill();
}
// Fábricas industriais (para a fase 4 - ABC)
function drawFactory(x, y) {
// Corpo da fábrica
ctx.fillStyle = '#3a3a3a';
ctx.fillRect(x, y - 60, 120, 60);
// Telhado serrilhado
ctx.fillStyle = '#2a2a2a';
for (let i = 0; i < 6; i++) {
ctx.beginPath();
ctx.moveTo(x + i * 20, y - 60);
ctx.lineTo(x + i * 20 + 10, y - 70);
ctx.lineTo(x + i * 20 + 20, y - 60);
ctx.fill();
}
// Chaminés
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(x + 30, y - 100, 12, 40);
ctx.fillRect(x + 70, y - 90, 10, 30);
// Faixa branca nas chaminés
ctx.fillStyle = '#fff';
ctx.fillRect(x + 30, y - 90, 12, 3);
ctx.fillRect(x + 70, y - 82, 10, 3);
// Janelas iluminadas
ctx.fillStyle = '#ffcc66';
for (let i = 0; i < 4; i++) {
ctx.fillRect(x + 10 + i * 28, y - 40, 12, 10);
}
// Fumaça saindo das chaminés
ctx.fillStyle = 'rgba(180, 180, 180, 0.6)';
const t = Date.now() / 1000;
for (let i = 0; i < 3; i++) {
const sy = y - 100 - 15 * i - Math.sin(t + i) * 5;
ctx.beginPath();
ctx.arc(x + 36 + Math.sin(t + i) * 4, sy, 8 + i * 3, 0, Math.PI * 2);
ctx.fill();
}
for (let i = 0; i < 3; i++) {
const sy = y - 90 - 15 * i - Math.sin(t + i + 1) * 5;
ctx.beginPath();
ctx.arc(x + 75 + Math.sin(t + i + 1) * 4, sy, 7 + i * 3, 0, Math.PI * 2);
ctx.fill();
}
}
function drawCloud(x, y) {
// Desenha cada círculo como um path separado para evitar linhas de conexão
ctx.beginPath();
ctx.arc(x, y, 18, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x + 20, y + 5, 22, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x + 45, y, 16, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.arc(x + 25, y - 12, 18, 0, Math.PI * 2);
ctx.fill();
}
function drawPlatform(p) {
if (p.type === 'ground') {
ctx.fillStyle = levelData.groundColor;
ctx.fillRect(p.x, p.y, p.w, p.h);
ctx.fillStyle = levelData.groundTop;
ctx.fillRect(p.x, p.y, p.w, 12);
// Texture
ctx.fillStyle = 'rgba(0,0,0,0.15)';
for (let i = 0; i < p.w; i += 40) {
ctx.fillRect(p.x + i, p.y + 20, 2, 20);
}
} else {
ctx.fillStyle = levelData.groundColor;
ctx.fillRect(p.x, p.y, p.w, p.h);
ctx.fillStyle = levelData.groundTop;
ctx.fillRect(p.x, p.y, p.w, 8);
ctx.strokeStyle = 'rgba(0,0,0,0.3)';
ctx.lineWidth = 1;
ctx.strokeRect(p.x, p.y, p.w, p.h);
}
}
function drawGoal() {
// Bandeira do Brasil
const gx = goal.x, gy = goal.y;
// Mastro
ctx.fillStyle = '#8B4513';
ctx.fillRect(gx, gy, 6, goal.h);
// Bandeira
ctx.fillStyle = '#009c3b';
ctx.fillRect(gx + 6, gy, 50, 35);
ctx.fillStyle = '#ffdf00';
ctx.beginPath();
ctx.moveTo(gx + 6, gy + 17);
ctx.lineTo(gx + 56, gy + 5);
ctx.lineTo(gx + 56, gy + 30);
ctx.lineTo(gx + 6, gy + 17);
ctx.fill();
ctx.fillStyle = '#002776';
ctx.beginPath();
ctx.arc(gx + 31, gy + 17, 7, 0, Math.PI * 2);
ctx.fill();
// Brilho
const pulse = Math.sin(Date.now() / 200) * 0.5 + 0.5;
ctx.strokeStyle = `rgba(255,255,255,${pulse})`;
ctx.lineWidth = 3;
ctx.strokeRect(gx - 5, gy - 5, 70, goal.h + 10);
}
function drawPlayer() {
const p = player;
if (p.invulnerable > 0 && Math.floor(p.invulnerable / 4) % 2 === 0) return;
const x = Math.floor(p.x);
const y = Math.floor(p.y);
const fx = p.facing;
// Sombra
ctx.fillStyle = 'rgba(0,0,0,0.2)';
ctx.beginPath();
ctx.ellipse(x + 12, y + 36, 14, 4, 0, 0, Math.PI * 2);
ctx.fill();
// Corpo (camisa branca) - estilo pixel art
ctx.fillStyle = '#ffffff';
ctx.fillRect(x + 4, y + 18, 16, 14);
// Calça (azul escuro)
ctx.fillStyle = '#1a3a5c';
if (p.onGround && Math.abs(p.vx) > 0.5) {
// Pernas animadas
if (p.animFrame === 0 || p.animFrame === 2) {
ctx.fillRect(x + 4, y + 30, 7, 6);
ctx.fillRect(x + 13, y + 30, 7, 6);
} else if (p.animFrame === 1) {
ctx.fillRect(x + 2, y + 30, 7, 6);
ctx.fillRect(x + 15, y + 30, 7, 6);
} else {
ctx.fillRect(x + 6, y + 30, 7, 6);
ctx.fillRect(x + 11, y + 30, 7, 6);
}
} else {
ctx.fillRect(x + 4, y + 30, 7, 6);
ctx.fillRect(x + 13, y + 30, 7, 6);
}
// Cabeça (pele)
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 5, y + 2, 14, 16);
// Cabelo (cinza - Lula mais velho) ou preto
ctx.fillStyle = '#3a3a3a';
ctx.fillRect(x + 4, y, 16, 6);
ctx.fillRect(x + 4, y + 4, 4, 4);
ctx.fillRect(x + 16, y + 4, 4, 4);
// Barba
ctx.fillStyle = '#3a3a3a';
ctx.fillRect(x + 5, y + 13, 14, 4);
ctx.fillRect(x + 6, y + 17, 12, 2);
// Olhos
ctx.fillStyle = '#000';
if (fx > 0) {
ctx.fillRect(x + 13, y + 8, 2, 2);
ctx.fillRect(x + 17, y + 8, 2, 2);
} else {
ctx.fillRect(x + 5, y + 8, 2, 2);
ctx.fillRect(x + 9, y + 8, 2, 2);
}
// Boca (sorriso)
ctx.fillStyle = '#000';
ctx.fillRect(x + 9, y + 12, 6, 1);
// Braços
ctx.fillStyle = '#d4a373';
if (fx > 0) {
ctx.fillRect(x + 18, y + 20, 4, 10);
ctx.fillRect(x + 2, y + 20, 4, 10);
} else {
ctx.fillRect(x + 2, y + 20, 4, 10);
ctx.fillRect(x + 18, y + 20, 4, 10);
}
// Sapatos
ctx.fillStyle = '#2a2a2a';
ctx.fillRect(x + 3, y + 34, 8, 3);
ctx.fillRect(x + 13, y + 34, 8, 3);
}
function drawEnemy(e) {
const x = Math.floor(e.x), y = Math.floor(e.y);
if (e.type === 'cactus') {
// Cacto verde do sertão
ctx.fillStyle = '#2d5016';
ctx.fillRect(x + 12, y, 12, 36);
ctx.fillStyle = '#3a6b1f';
ctx.fillRect(x + 8, y + 10, 8, 14);
ctx.fillRect(x + 20, y + 6, 8, 12);
// Espinhos
ctx.fillStyle = '#fff';
for (let i = 0; i < 4; i++) {
ctx.fillRect(x + 14, y + 4 + i * 8, 1, 2);
ctx.fillRect(x + 21, y + 4 + i * 8, 1, 2);
}
} else if (e.type === 'truck') {
// Caminhão (pau de arara)
ctx.fillStyle = '#8b0000';
ctx.fillRect(x, y + 10, 30, 26);
ctx.fillStyle = '#5a0000';
ctx.fillRect(x + 20, y + 4, 16, 32);
ctx.fillStyle = '#aaa';
ctx.fillRect(x + 22, y + 8, 12, 8);
// Rodas
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(x + 6, y + 36, 5, 0, Math.PI * 2);
ctx.arc(x + 18, y + 36, 5, 0, Math.PI * 2);
ctx.arc(x + 30, y + 36, 5, 0, Math.PI * 2);
ctx.fill();
} else if (e.type === 'machine') {
// Máquina industrial
ctx.fillStyle = '#666';
ctx.fillRect(x, y + 4, 36, 32);
ctx.fillStyle = '#444';
ctx.fillRect(x + 4, y + 8, 8, 8);
ctx.fillRect(x + 24, y + 8, 8, 8);
ctx.fillStyle = '#ff0';
ctx.fillRect(x + 6, y + 10, 4, 4);
ctx.fillRect(x + 26, y + 10, 4, 4);
// Engrenagens
ctx.fillStyle = '#222';
ctx.beginPath();
ctx.arc(x + 18, y + 26, 6, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#888';
ctx.lineWidth = 2;
const ang = Date.now() / 200;
for (let i = 0; i < 6; i++) {
const a = ang + i * Math.PI / 3;
ctx.beginPath();
ctx.moveTo(x + 18, y + 26);
ctx.lineTo(x + 18 + Math.cos(a) * 8, y + 26 + Math.sin(a) * 8);
ctx.stroke();
}
} else if (e.type === 'soldier') {
// Soldado (ditadura)
ctx.fillStyle = '#4a5d23';
ctx.fillRect(x + 8, y + 10, 20, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 12, y, 12, 12);
// Capacete
ctx.fillStyle = '#3a4a1c';
ctx.fillRect(x + 10, y - 2, 16, 6);
// Olhos
ctx.fillStyle = '#000';
if (e.dir > 0) {
ctx.fillRect(x + 18, y + 5, 2, 2);
} else {
ctx.fillRect(x + 16, y + 5, 2, 2);
}
// Rifle
ctx.fillStyle = '#222';
ctx.fillRect(x + (e.dir > 0 ? 28 : 0), y + 16, 8, 2);
} else if (e.type === 'corruption') {
// Inimigo abstrato (corrupção - dinheirinho vermelho)
ctx.fillStyle = '#cc0000';
ctx.fillRect(x + 4, y, 28, 28);
ctx.fillStyle = '#fff';
ctx.font = 'bold 18px Arial';
ctx.fillText('R$', x + 7, y + 20);
// Olhos raivosos
ctx.fillStyle = '#000';
ctx.fillRect(x + 10, y + 8, 4, 4);
ctx.fillRect(x + 22, y + 8, 4, 4);
}
}
function drawCollectible(c) {
const x = Math.floor(c.x), y = Math.floor(c.y);
const bob = Math.sin(Date.now() / 300 + x * 0.01) * 3;
const cy = y + bob;
if (c.type === 'star') {
drawStar(x + 12, cy + 12, 10, '#ff2020', '#8b0000');
} else if (c.type === 'book') {
ctx.fillStyle = '#1e3a8a';
ctx.fillRect(x, cy, 24, 20);
ctx.fillStyle = '#fff';
ctx.fillRect(x + 2, cy + 3, 20, 2);
ctx.fillRect(x + 2, cy + 7, 16, 2);
ctx.fillRect(x + 2, cy + 11, 18, 2);
ctx.fillRect(x + 2, cy + 15, 14, 2);
ctx.fillStyle = '#ffdf00';
ctx.fillRect(x, cy, 24, 3);
} else if (c.type === 'tool') {
// Chave de fenda
ctx.fillStyle = '#888';
ctx.fillRect(x + 10, cy, 4, 18);
ctx.fillStyle = '#8b4513';
ctx.fillRect(x + 8, cy + 14, 8, 10);
ctx.fillStyle = '#ccc';
ctx.fillRect(x + 11, cy + 2, 2, 14);
} else if (c.type === 'megaphone') {
// Megafone
ctx.fillStyle = '#cc0000';
ctx.beginPath();
ctx.moveTo(x + 4, cy + 4);
ctx.lineTo(x + 20, cy);
ctx.lineTo(x + 20, cy + 20);
ctx.lineTo(x + 4, cy + 16);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#444';
ctx.fillRect(x, cy + 6, 6, 8);
} else if (c.type === 'flag') {
// Bandeixinha do PT
ctx.fillStyle = '#8B4513';
ctx.fillRect(x + 2, cy, 2, 22);
ctx.fillStyle = '#cc0000';
ctx.fillRect(x + 4, cy, 18, 12);
ctx.fillStyle = '#fff';
ctx.font = 'bold 11px Arial';
ctx.fillText('PT', x + 8, cy + 10);
} else if (c.type === 'house') {
// Casinha
ctx.fillStyle = '#8b4513';
ctx.fillRect(x + 2, cy + 10, 20, 12);
ctx.fillStyle = '#cc0000';
ctx.beginPath();
ctx.moveTo(x, cy + 10);
ctx.lineTo(x + 12, cy);
ctx.lineTo(x + 24, cy + 10);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#ffdf00';
ctx.fillRect(x + 8, cy + 14, 8, 8);
} else if (c.type === 'bolsa') {
// Bolsa família
ctx.fillStyle = '#009c3b';
ctx.fillRect(x, cy, 24, 20);
ctx.fillStyle = '#ffdf00';
ctx.fillRect(x, cy, 24, 4);
ctx.fillStyle = '#fff';
ctx.font = 'bold 8px Arial';
ctx.fillText('R$', x + 6, cy + 14);
ctx.fillStyle = '#000';
ctx.font = 'bold 8px Arial';
ctx.fillText('$', x + 13, cy + 14);
}
}
function drawStar(cx, cy, size, color1, color2) {
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
for (let i = 0; i < 10; i++) {
const angle = (Math.PI / 5) * i - Math.PI / 2;
const r = i % 2 === 0 ? size : size / 2.5;
const px = Math.cos(angle) * r;
const py = Math.sin(angle) * r;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
ctx.fillStyle = color1;
ctx.fill();
ctx.strokeStyle = color2;
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.restore();
}
function drawNPC(n) {
const x = Math.floor(n.x), y = Math.floor(n.y);
// Indicador de "fale comigo"
if (!n.talked) {
const pulse = Math.sin(Date.now() / 200) * 4;
ctx.fillStyle = '#ffdf00';
ctx.font = 'bold 20px Arial';
ctx.fillText('!', x + 12, y - 10 + pulse);
}
// Corpo baseado no tipo
if (n.type === 'mother') {
// Mãe Lindu - vestido
ctx.fillStyle = '#8b3a62';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#3a3a3a';
ctx.fillRect(x + 7, y + 2, 16, 6);
ctx.fillStyle = '#000';
ctx.fillRect(x + 11, y + 7, 2, 2);
ctx.fillRect(x + 17, y + 7, 2, 2);
} else if (n.type === 'teacher') {
// Professora
ctx.fillStyle = '#2a4d8f';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#000';
ctx.fillRect(x + 11, y + 7, 2, 2);
ctx.fillRect(x + 17, y + 7, 2, 2);
// Óculos
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.strokeRect(x + 10, y + 6, 4, 4);
ctx.strokeRect(x + 16, y + 6, 4, 4);
} else if (n.type === 'worker') {
// Operário
ctx.fillStyle = '#ff8c00';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#8b4513';
ctx.fillRect(x + 6, y + 6, 18, 4);
// Capacete
ctx.fillStyle = '#ffdf00';
ctx.fillRect(x + 6, y, 18, 8);
} else if (n.type === 'doctor') {
// Médico - jaleco branco
ctx.fillStyle = '#fff';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#000';
ctx.fillRect(x + 11, y + 7, 2, 2);
ctx.fillRect(x + 17, y + 7, 2, 2);
// Cruz vermelha
ctx.fillStyle = '#cc0000';
ctx.fillRect(x + 13, y + 18, 4, 8);
ctx.fillRect(x + 9, y + 20, 12, 4);
} else if (n.type === 'driver') {
// Motorista
ctx.fillStyle = '#444';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
// Boné
ctx.fillStyle = '#222';
ctx.fillRect(x + 6, y + 2, 18, 4);
ctx.fillRect(x + 8, y - 2, 14, 6);
} else if (n.type === 'intellectual') {
// Intelectual - óculos
ctx.fillStyle = '#2a2a2a';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#3a3a3a';
ctx.fillRect(x + 7, y + 2, 16, 6);
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.strokeRect(x + 10, y + 6, 4, 4);
ctx.strokeRect(x + 16, y + 6, 4, 4);
} else if (n.type === 'journalist') {
// Jornalista
ctx.fillStyle = '#556b2f';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#8b4513';
ctx.fillRect(x + 8, y + 16, 22, 3);
// Caderno
ctx.fillStyle = '#fff';
ctx.fillRect(x + 6, y + 20, 20, 14);
ctx.fillStyle = '#000';
ctx.fillRect(x + 8, y + 23, 16, 1);
ctx.fillRect(x + 8, y + 26, 16, 1);
ctx.fillRect(x + 8, y + 29, 12, 1);
} else if (n.type === 'citizen') {
// Cidadão
ctx.fillStyle = '#3a6b1f';
ctx.fillRect(x + 4, y + 14, 22, 26);
ctx.fillStyle = '#d4a373';
ctx.fillRect(x + 8, y, 14, 16);
ctx.fillStyle = '#000';
ctx.fillRect(x + 11, y + 7, 2, 2);
ctx.fillRect(x + 17, y + 7, 2, 2);
// Sorriso
ctx.fillRect(x + 12, y + 11, 6, 2);
}
}
// ============== MAIN LOOP ==============
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
// Start render loop (will be idle until level loaded)
gameLoop();
// Initial screen
showScreen('startScreen');
</script>
</body>
</html>Game Source: Lula: Uma Jornada Brasileira
Creator: DriftOtter98
Libraries: none
Complexity: complex (1574 lines, 59.4 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: lula-uma-jornada-brasileira-driftotter98" to link back to the original. Then publish at arcadelab.ai/publish.