jogo / index.html
Xacodavt's picture
Add 2 files
454f7b3 verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPEC OPS: Elite Combat | FPS Profissional</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Rajdhani:wght@500;700&display=swap');
:root {
--primary: #ff4655;
--secondary: #0f1923;
--accent: #ece8e1;
--dark: #111;
--light: #f8f9fa;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
font-family: 'Rajdhani', sans-serif;
background-color: var(--dark);
color: var(--light);
touch-action: none;
user-select: none;
}
#gameCanvas {
display: block;
background: radial-gradient(circle at center, #1a2a3a, #0a0a0a);
}
/* HUD Elements */
#hud {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
padding: 20px;
pointer-events: none;
z-index: 10;
}
#healthBar {
width: 300px;
height: 24px;
background-color: rgba(15, 25, 35, 0.7);
border: 2px solid var(--accent);
border-radius: 2px;
overflow: hidden;
margin-bottom: 15px;
position: relative;
}
#healthFill {
height: 100%;
width: 100%;
background: linear-gradient(90deg, #ff4655, #ff6b45);
transition: width 0.3s cubic-bezier(0.65, 0, 0.35, 1);
position: relative;
overflow: hidden;
}
#healthFill::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg,
rgba(255,255,255,0.1) 0%,
rgba(255,255,255,0.3) 50%,
rgba(255,255,255,0.1) 100%);
animation: healthPulse 2.5s infinite linear;
}
@keyframes healthPulse {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
#ammoContainer {
display: flex;
align-items: center;
gap: 15px;
}
#ammoCount {
font-size: 28px;
font-weight: 700;
color: var(--accent);
font-family: 'Orbitron', sans-serif;
text-shadow: 0 0 10px rgba(255,255,255,0.3);
}
#ammoCount .current {
color: var(--light);
font-size: 1.5em;
}
#ammoCount .separator {
opacity: 0.5;
}
#ammoCount .total {
opacity: 0.7;
}
#weaponInfo {
display: flex;
flex-direction: column;
}
#weaponName {
font-size: 18px;
font-weight: 700;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 1px;
}
#weaponType {
font-size: 12px;
color: rgba(236, 232, 225, 0.6);
text-transform: uppercase;
}
#reloadIndicator {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(15, 25, 35, 0.8);
color: var(--accent);
padding: 8px 20px;
border-radius: 20px;
font-size: 14px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
opacity: 0;
transition: opacity 0.3s;
display: flex;
align-items: center;
gap: 8px;
}
#reloadIndicator.show {
opacity: 1;
}
/* Crosshair */
#crosshair {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
pointer-events: none;
z-index: 5;
mix-blend-mode: difference;
}
.crosshair-dot {
position: absolute;
width: 6px;
height: 6px;
background-color: var(--primary);
border-radius: 50%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 0 10px var(--primary);
}
.crosshair-line {
position: absolute;
background-color: var(--primary);
}
.crosshair-line.top {
width: 2px;
height: 12px;
top: 0;
left: 50%;
transform: translateX(-50%);
}
.crosshair-line.bottom {
width: 2px;
height: 12px;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.crosshair-line.left {
width: 12px;
height: 2px;
left: 0;
top: 50%;
transform: translateY(-50%);
}
.crosshair-line.right {
width: 12px;
height: 2px;
right: 0;
top: 50%;
transform: translateY(-50%);
}
/* Score and Wave */
#scoreContainer {
position: absolute;
top: 30px;
left: 30px;
background-color: rgba(15, 25, 35, 0.7);
border: 1px solid var(--accent);
border-radius: 4px;
padding: 10px 15px;
display: flex;
flex-direction: column;
gap: 5px;
}
.hud-label {
font-size: 12px;
color: rgba(236, 232, 225, 0.7);
text-transform: uppercase;
letter-spacing: 1px;
}
.hud-value {
font-size: 24px;
font-weight: 700;
color: var(--accent);
font-family: 'Orbitron', sans-serif;
}
#waveContainer {
position: absolute;
top: 30px;
right: 30px;
background-color: rgba(15, 25, 35, 0.7);
border: 1px solid var(--accent);
border-radius: 4px;
padding: 10px 15px;
display: flex;
flex-direction: column;
gap: 5px;
text-align: right;
}
/* Hit Marker */
#hitMarker {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
pointer-events: none;
opacity: 0;
z-index: 20;
}
.hit-line {
position: absolute;
background-color: white;
box-shadow: 0 0 5px var(--primary);
}
.hit-line.top-left {
width: 15px;
height: 3px;
top: 0;
left: 0;
transform: rotate(45deg);
transform-origin: top left;
}
.hit-line.top-right {
width: 15px;
height: 3px;
top: 0;
right: 0;
transform: rotate(-45deg);
transform-origin: top right;
}
.hit-line.bottom-left {
width: 15px;
height: 3px;
bottom: 0;
left: 0;
transform: rotate(-45deg);
transform-origin: bottom left;
}
.hit-line.bottom-right {
width: 15px;
height: 3px;
bottom: 0;
right: 0;
transform: rotate(45deg);
transform-origin: bottom right;
}
/* Hit Effect */
#hitEffect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(255, 0, 0, 0.2);
pointer-events: none;
opacity: 0;
transition: opacity 0.1s;
z-index: 15;
}
/* Blood Screen Effect */
#bloodEffect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="4" stitchTiles="stitch"/><feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 50 -20"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.2"/></svg>');
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
z-index: 15;
mix-blend-mode: multiply;
}
/* Kill Feed */
#killFeed {
position: absolute;
top: 100px;
right: 30px;
width: 250px;
display: flex;
flex-direction: column;
gap: 8px;
pointer-events: none;
z-index: 10;
}
.kill-feed-item {
background-color: rgba(15, 25, 35, 0.8);
border-left: 3px solid var(--primary);
padding: 8px 12px;
color: var(--accent);
font-size: 14px;
font-weight: 600;
transform: translateX(100%);
opacity: 0;
transition: all 0.3s ease-out;
}
.kill-feed-item.show {
transform: translateX(0);
opacity: 1;
}
.kill-feed-item .weapon {
color: white;
font-weight: 700;
}
/* Weapon Viewmodel */
#weaponViewmodel {
position: absolute;
bottom: 0;
right: 0;
width: 800px;
height: 600px;
pointer-events: none;
z-index: 8;
transform-origin: bottom right;
transform: scale(0.5);
}
#weaponImage {
position: absolute;
bottom: 0;
right: 50px;
height: 80%;
object-fit: contain;
filter: drop-shadow(0 0 10px rgba(0,0,0,0.5));
transform-origin: bottom right;
}
/* Bullet Tracers */
.bullet-tracer {
position: absolute;
height: 2px;
background: linear-gradient(90deg, rgba(255,255,255,0.8), rgba(255,255,255,0));
transform-origin: left center;
pointer-events: none;
z-index: 7;
}
/* Blood Splats */
.blood-splat {
position: absolute;
width: 40px;
height: 40px;
background-image: radial-gradient(circle, rgba(200,0,0,0.8) 0%, rgba(100,0,0,0.8) 70%, transparent 100%);
border-radius: 50%;
pointer-events: none;
z-index: 6;
transform: scale(0);
animation: bloodSplat 0.5s forwards;
}
@keyframes bloodSplat {
0% { transform: scale(0); opacity: 0; }
50% { transform: scale(1.2); opacity: 1; }
100% { transform: scale(1); opacity: 0.8; }
}
/* Impact Effects */
.bullet-impact {
position: absolute;
width: 15px;
height: 15px;
background-image: radial-gradient(circle, rgba(255,255,255,0.8) 0%, rgba(150,150,150,0.5) 70%, transparent 100%);
border-radius: 50%;
pointer-events: none;
z-index: 6;
transform: scale(0);
animation: impactFade 1s forwards;
}
@keyframes impactFade {
0% { transform: scale(0); opacity: 0; }
30% { transform: scale(1); opacity: 1; }
100% { transform: scale(1); opacity: 0; }
}
/* Screens */
#startScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(11, 14, 17, 0.95);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="4" stitchTiles="stitch"/><feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 50 -20"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.1"/></svg>');
}
#gameOverScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(11, 14, 17, 0.95);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"><filter id="noise"><feTurbulence type="fractalNoise" baseFrequency="0.8" numOctaves="4" stitchTiles="stitch"/><feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 50 -20"/></filter><rect width="100%" height="100%" filter="url(%23noise)" opacity="0.1"/></svg>');
}
#pauseScreen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(11, 14, 17, 0.7);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 100;
}
.screen-title {
font-family: 'Orbitron', sans-serif;
font-size: 72px;
font-weight: 700;
color: var(--primary);
text-transform: uppercase;
letter-spacing: 5px;
margin-bottom: 20px;
text-shadow: 0 0 20px rgba(255, 70, 85, 0.5);
}
.screen-subtitle {
font-size: 24px;
color: var(--accent);
margin-bottom: 40px;
text-align: center;
max-width: 600px;
line-height: 1.5;
}
.screen-stats {
display: flex;
gap: 30px;
margin-bottom: 40px;
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-value {
font-family: 'Orbitron', sans-serif;
font-size: 36px;
font-weight: 700;
color: var(--primary);
margin-bottom: 5px;
}
.stat-label {
font-size: 14px;
color: var(--accent);
text-transform: uppercase;
letter-spacing: 1px;
}
.button {
position: relative;
padding: 15px 40px;
background: linear-gradient(135deg, var(--primary), #ff6b45);
color: white;
border: none;
border-radius: 4px;
font-size: 18px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 1px;
cursor: pointer;
overflow: hidden;
transition: all 0.3s;
margin: 10px;
box-shadow: 0 5px 15px rgba(255, 70, 85, 0.3);
}
.button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
transparent,
rgba(255,255,255,0.2),
transparent);
transition: all 0.5s;
}
.button:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(255, 70, 85, 0.4);
}
.button:hover::before {
left: 100%;
}
.button:active {
transform: translateY(0);
box-shadow: 0 5px 15px rgba(255, 70, 85, 0.3);
}
.controls {
display: flex;
flex-direction: column;
gap: 15px;
margin-top: 40px;
color: var(--accent);
}
.control-item {
display: flex;
align-items: center;
gap: 10px;
}
.control-key {
background-color: rgba(236, 232, 225, 0.1);
border: 1px solid var(--accent);
border-radius: 4px;
padding: 5px 10px;
min-width: 30px;
text-align: center;
font-family: 'Orbitron', sans-serif;
}
/* Weapon Recoil Animation */
.recoil {
animation: weaponRecoil 0.1s forwards;
}
@keyframes weaponRecoil {
0% { transform: scale(0.5) translateX(0) translateY(0) rotateZ(0); }
50% { transform: scale(0.5) translateX(-20px) translateY(30px) rotateZ(-5deg); }
100% { transform: scale(0.5) translateX(0) translateY(0) rotateZ(0); }
}
/* Weapon Sway Animation */
@keyframes weaponSway {
0% { transform: scale(0.5) translateX(0) translateY(0) rotateZ(0); }
25% { transform: scale(0.5) translateX(5px) translateY(3px) rotateZ(0.5deg); }
50% { transform: scale(0.5) translateX(0) translateY(0) rotateZ(0); }
75% { transform: scale(0.5) translateX(-5px) translateY(3px) rotateZ(-0.5deg); }
100% { transform: scale(0.5) translateX(0) translateY(0) rotateZ(0); }
}
/* Enemy Styles */
.enemy {
position: absolute;
width: 60px;
height: 100px;
background-color: #333;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
overflow: hidden;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
transform-style: preserve-3d;
transform: perspective(500px) rotateY(0deg);
transition: transform 0.1s;
}
.enemy-head {
width: 30px;
height: 30px;
background-color: #555;
border-radius: 50%;
margin-bottom: 5px;
position: relative;
}
.enemy-body {
width: 40px;
height: 50px;
background-color: #444;
border-radius: 4px;
margin-bottom: 5px;
}
.enemy-legs {
width: 40px;
height: 15px;
background-color: #333;
border-radius: 0 0 4px 4px;
}
.enemy-health-bar {
position: absolute;
top: 5px;
left: 5px;
right: 5px;
height: 3px;
background-color: #ff4655;
border-radius: 2px;
}
/* Enemy Death Animation */
.enemy.dead {
animation: enemyDeath 0.5s forwards;
}
@keyframes enemyDeath {
0% { transform: perspective(500px) rotateY(0deg) translateY(0); }
100% { transform: perspective(500px) rotateY(90deg) translateY(100px); opacity: 0; }
}
/* Explosion Effect */
.explosion {
position: absolute;
width: 100px;
height: 100px;
background-image: radial-gradient(circle, rgba(255,165,0,0.8) 0%, rgba(255,50,0,0.8) 70%, transparent 100%);
border-radius: 50%;
pointer-events: none;
z-index: 9;
transform: scale(0);
animation: explosion 0.5s forwards;
}
@keyframes explosion {
0% { transform: scale(0); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.8; }
100% { transform: scale(2); opacity: 0; }
}
/* Bullet Shells */
.bullet-shell {
position: absolute;
width: 10px;
height: 15px;
background: linear-gradient(to bottom, #d1a054, #b8860b);
border-radius: 2px;
transform: rotate(45deg);
pointer-events: none;
z-index: 6;
animation: shellEject 1s forwards;
}
@keyframes shellEject {
0% { transform: rotate(45deg) translate(0, 0); opacity: 1; }
100% { transform: rotate(45deg) translate(50px, -100px); opacity: 0; }
}
/* Screen Shake */
.screen-shake {
animation: screenShake 0.3s linear;
}
@keyframes screenShake {
0% { transform: translate(0, 0) rotate(0); }
20% { transform: translate(-5px, -5px) rotate(-1deg); }
40% { transform: translate(5px, 5px) rotate(1deg); }
60% { transform: translate(-5px, 5px) rotate(-1deg); }
80% { transform: translate(5px, -5px) rotate(1deg); }
100% { transform: translate(0, 0) rotate(0); }
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<!-- HUD Elements -->
<div id="hud">
<div id="healthBar">
<div id="healthFill"></div>
</div>
<div id="ammoContainer">
<div id="ammoCount">
<span class="current">30</span>
<span class="separator">/</span>
<span class="total">90</span>
</div>
<div id="weaponInfo">
<div id="weaponName">M4A1</div>
<div id="weaponType">Assault Rifle</div>
</div>
</div>
</div>
<div id="reloadIndicator">
<i class="fas fa-sync-alt fa-spin"></i>
<span>Recarregando...</span>
</div>
<div id="scoreContainer">
<div class="hud-label">Pontuação</div>
<div class="hud-value" id="scoreValue">0</div>
</div>
<div id="waveContainer">
<div class="hud-label">Onda</div>
<div class="hud-value" id="waveValue">1</div>
</div>
<!-- Crosshair -->
<div id="crosshair">
<div class="crosshair-dot"></div>
<div class="crosshair-line top"></div>
<div class="crosshair-line bottom"></div>
<div class="crosshair-line left"></div>
<div class="crosshair-line right"></div>
</div>
<!-- Hit Marker -->
<div id="hitMarker">
<div class="hit-line top-left"></div>
<div class="hit-line top-right"></div>
<div class="hit-line bottom-left"></div>
<div class="hit-line bottom-right"></div>
</div>
<!-- Visual Effects -->
<div id="hitEffect"></div>
<div id="bloodEffect"></div>
<!-- Kill Feed -->
<div id="killFeed"></div>
<!-- Weapon Viewmodel -->
<div id="weaponViewmodel">
<img id="weaponImage" src="https://cdn-icons-png.flaticon.com/512/821/821356.png" alt="Weapon">
</div>
<!-- Screens -->
<div id="startScreen">
<h1 class="screen-title">Spec Ops</h1>
<p class="screen-subtitle">Operação Tática de Combate | Sobreviva às hordas de inimigos e prove seu valor como soldado de elite</p>
<button id="startButton" class="button">
<i class="fas fa-play mr-2"></i> Iniciar Missão
</button>
<div class="controls">
<div class="control-item">
<div class="control-key">WASD</div>
<div>Movimentação</div>
</div>
<div class="control-item">
<div class="control-key">MOUSE</div>
<div>Mirar/Atirar</div>
</div>
<div class="control-item">
<div class="control-key">R</div>
<div>Recarregar</div>
</div>
<div class="control-item">
<div class="control-key">ESC</div>
<div>Pausar</div>
</div>
</div>
</div>
<div id="gameOverScreen">
<h1 class="screen-title">Missão Falhada</h1>
<p class="screen-subtitle">Você não sobreviveu para contar a história... mas a guerra continua</p>
<div class="screen-stats">
<div class="stat-item">
<div class="stat-value" id="finalScore">0</div>
<div class="stat-label">Pontuação</div>
</div>
<div class="stat-item">
<div class="stat-value" id="finalWave">1</div>
<div class="stat-label">Onda</div>
</div>
<div class="stat-item">
<div class="stat-value" id="finalKills">0</div>
<div class="stat-label">Eliminações</div>
</div>
</div>
<button id="restartButton" class="button">
<i class="fas fa-redo mr-2"></i> Tentar Novamente
</button>
</div>
<div id="pauseScreen">
<h1 class="screen-title">Jogo Pausado</h1>
<p class="screen-subtitle">A batalha espera por sua volta, soldado</p>
<button id="resumeButton" class="button">
<i class="fas fa-play mr-2"></i> Continuar
</button>
<button id="quitButton" class="button" style="background: linear-gradient(135deg, #555, #333);">
<i class="fas fa-sign-out-alt mr-2"></i> Sair
</button>
</div>
<script>
// Configurações avançadas do jogo
const config = {
width: window.innerWidth,
height: window.innerHeight,
player: {
health: 100,
maxHealth: 100,
damage: 15,
ammo: 30,
maxAmmo: 30,
totalAmmo: 90,
reloadTime: 2000,
moveSpeed: 5,
mouseSensitivity: 0.2
},
weapons: [
{
name: "M4A1",
type: "Assault Rifle",
damage: 15,
ammo: 30,
maxAmmo: 30,
totalAmmo: 90,
fireRate: 100, // ms between shots
reloadTime: 2000,
accuracy: 0.95, // 0-1 where 1 is perfect accuracy
recoil: 1.5,
image: "https://cdn-icons-png.flaticon.com/512/821/821356.png"
},
{
name: "Desert Eagle",
type: "Pistol",
damage: 40,
ammo: 7,
maxAmmo: 7,
totalAmmo: 35,
fireRate: 500,
reloadTime: 1500,
accuracy: 0.98,
recoil: 3,
image: "https://cdn-icons-png.flaticon.com/512/2555/2555754.png"
},
{
name: "MP5",
type: "SMG",
damage: 10,
ammo: 25,
maxAmmo: 25,
totalAmmo: 125,
fireRate: 50,
reloadTime: 1800,
accuracy: 0.85,
recoil: 1,
image: "https://cdn-icons-png.flaticon.com/512/2555/2555773.png"
}
],
enemy: {
baseHealth: 50,
baseSpeed: 1.5,
spawnRate: 1000, // ms between spawns
baseDamage: 10,
basePoints: 10,
types: [
{
name: "Soldier",
healthMultiplier: 1,
speedMultiplier: 1,
damageMultiplier: 1,
pointsMultiplier: 1,
color: "#555"
},
{
name: "Elite",
healthMultiplier: 1.5,
speedMultiplier: 1.2,
damageMultiplier: 1.3,
pointsMultiplier: 1.5,
color: "#333"
},
{
name: "Heavy",
healthMultiplier: 2.5,
speedMultiplier: 0.7,
damageMultiplier: 1.8,
pointsMultiplier: 2,
color: "#444"
}
]
},
wave: {
current: 1,
enemiesPerWave: 5,
increasePerWave: 3,
bossWaveInterval: 5,
bossHealthMultiplier: 3,
bossPointsMultiplier: 5
},
game: {
score: 0,
kills: 0,
isRunning: false,
isPaused: false,
isReloading: false,
currentWeapon: 0,
mouseX: 0,
mouseY: 0,
playerX: 0,
playerY: 0,
lastShotTime: 0,
lastSpawnTime: 0,
enemiesSpawned: 0,
enemiesAlive: 0,
lastWaveTime: 0
}
};
// Estado do jogo
const state = {
enemies: [],
bullets: [],
explosions: [],
bloodSplats: [],
bulletImpacts: [],
bulletShells: [],
killFeedItems: [],
weaponSwayInterval: null,
mouseMovementX: 0,
mouseMovementY: 0
};
// Elementos do DOM
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const healthFill = document.getElementById('healthFill');
const ammoCount = document.getElementById('ammoCount');
const weaponName = document.getElementById('weaponName');
const weaponType = document.getElementById('weaponType');
const reloadIndicator = document.getElementById('reloadIndicator');
const scoreValue = document.getElementById('scoreValue');
const waveValue = document.getElementById('waveValue');
const hitMarker = document.getElementById('hitMarker');
const hitEffect = document.getElementById('hitEffect');
const bloodEffect = document.getElementById('bloodEffect');
const killFeed = document.getElementById('killFeed');
const weaponImage = document.getElementById('weaponImage');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const pauseScreen = document.getElementById('pauseScreen');
const finalScore = document.getElementById('finalScore');
const finalWave = document.getElementById('finalWave');
const finalKills = document.getElementById('finalKills');
const startButton = document.getElementById('startButton');
const restartButton = document.getElementById('restartButton');
const resumeButton = document.getElementById('resumeButton');
const quitButton = document.getElementById('quitButton');
// Configurar canvas
canvas.width = config.width;
canvas.height = config.height;
config.game.playerX = canvas.width / 2;
config.game.playerY = canvas.height / 2;
// Event listeners
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('click', handleShoot);
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
startButton.addEventListener('click', startGame);
restartButton.addEventListener('click', restartGame);
resumeButton.addEventListener('click', resumeGame);
quitButton.addEventListener('click', quitGame);
// Inicializar jogo
initGame();
function initGame() {
// Configurar arma inicial
changeWeapon(0);
// Iniciar animação de balanço da arma
startWeaponSway();
// Mostrar tela inicial
startScreen.style.display = 'flex';
}
function startGame() {
// Resetar estado do jogo
resetGameState();
// Esconder telas
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
pauseScreen.style.display = 'none';
// Iniciar jogo
config.game.isRunning = true;
config.game.isPaused = false;
// Iniciar loop do jogo
gameLoop();
// Tocar som de início
playSound('start');
}
function resetGameState() {
// Resetar configurações do jogador
config.player.health = config.player.maxHealth;
config.player.ammo = config.weapons[config.game.currentWeapon].ammo;
config.player.totalAmmo = config.weapons[config.game.currentWeapon].totalAmmo;
// Resetar onda
config.wave.current = 1;
config.wave.enemiesPerWave = 5;
config.game.enemiesSpawned = 0;
config.game.enemiesAlive = 0;
// Resetar pontuação
config.game.score = 0;
config.game.kills = 0;
// Resetar estado
config.game.lastShotTime = 0;
config.game.lastSpawnTime = 0;
config.game.lastWaveTime = Date.now();
// Limpar arrays de estado
state.enemies = [];
state.bullets = [];
state.explosions = [];
state.bloodSplats = [];
state.bulletImpacts = [];
state.bulletShells = [];
state.killFeedItems = [];
// Limpar elementos DOM
document.querySelectorAll('.enemy, .bullet-tracer, .blood-splat, .bullet-impact, .explosion, .bullet-shell, .kill-feed-item').forEach(el => el.remove());
// Atualizar HUD
updateHealthDisplay();
updateAmmoDisplay();
updateScoreDisplay();
updateWaveDisplay();
}
function restartGame() {
startGame();
}
function pauseGame() {
if (!config.game.isRunning) return;
config.game.isPaused = true;
pauseScreen.style.display = 'flex';
}
function resumeGame() {
config.game.isPaused = false;
pauseScreen.style.display = 'none';
}
function quitGame() {
config.game.isRunning = false;
startScreen.style.display = 'flex';
pauseScreen.style.display = 'none';
}
function gameOver() {
config.game.isRunning = false;
// Atualizar estatísticas finais
finalScore.textContent = config.game.score;
finalWave.textContent = config.wave.current;
finalKills.textContent = config.game.kills;
// Mostrar tela de game over
gameOverScreen.style.display = 'flex';
// Tocar som de game over
playSound('gameOver');
}
function nextWave() {
config.wave.current++;
config.wave.enemiesPerWave += config.wave.increasePerWave;
config.game.enemiesSpawned = 0;
config.game.lastWaveTime = Date.now();
// Recompensa por completar onda
config.player.totalAmmo += config.weapons[config.game.currentWeapon].maxAmmo * 2;
updateAmmoDisplay();
// Aumentar vida máxima
config.player.health = Math.min(config.player.maxHealth, config.player.health + 20);
updateHealthDisplay();
// Verificar se é uma onda de chefe
if (config.wave.current % config.wave.bossWaveInterval === 0) {
spawnBoss();
}
// Atualizar HUD
updateWaveDisplay();
// Tocar som de nova onda
playSound('waveComplete');
}
function spawnBoss() {
// Criar um inimigo chefe mais forte
const type = config.enemy.types[Math.floor(Math.random() * config.enemy.types.length)];
const boss = document.createElement('div');
boss.className = 'enemy';
boss.style.width = '80px';
boss.style.height = '130px';
boss.style.backgroundColor = '#222';
const head = document.createElement('div');
head.className = 'enemy-head';
head.style.width = '40px';
head.style.height = '40px';
head.style.backgroundColor = '#333';
const body = document.createElement('div');
body.className = 'enemy-body';
body.style.width = '60px';
body.style.height = '70px';
body.style.backgroundColor = '#282828';
const legs = document.createElement('div');
legs.className = 'enemy-legs';
legs.style.width = '60px';
legs.style.height = '20px';
legs.style.backgroundColor = '#222';
const healthBar = document.createElement('div');
healthBar.className = 'enemy-health-bar';
boss.appendChild(head);
boss.appendChild(body);
boss.appendChild(legs);
boss.appendChild(healthBar);
document.body.appendChild(boss);
// Posição aleatória na borda da tela
let x, y;
if (Math.random() < 0.5) {
x = Math.random() < 0.5 ? 0 : config.width;
y = Math.random() * config.height;
} else {
x = Math.random() * config.width;
y = Math.random() < 0.5 ? 0 : config.height;
}
boss.x = x;
boss.y = y;
boss.style.left = `${x}px`;
boss.style.top = `${y}px`;
// Configurações do chefe
boss.health = config.enemy.baseHealth * config.wave.bossHealthMultiplier * type.healthMultiplier;
boss.maxHealth = boss.health;
boss.speed = config.enemy.baseSpeed * type.speedMultiplier;
boss.damage = config.enemy.baseDamage * type.damageMultiplier;
boss.points = config.enemy.basePoints * config.wave.bossPointsMultiplier * type.pointsMultiplier;
boss.type = 'Boss';
boss.enemyType = type;
state.enemies.push(boss);
config.game.enemiesSpawned++;
config.game.enemiesAlive++;
// Tocar som de chefe aparecendo
playSound('bossSpawn');
}
function spawnEnemy() {
const now = Date.now();
if (now - config.game.lastSpawnTime < config.enemy.spawnRate / Math.sqrt(config.wave.current)) return;
if (config.game.enemiesSpawned >= config.wave.enemiesPerWave) return;
// Escolher tipo de inimigo aleatório
const type = config.enemy.types[Math.floor(Math.random() * config.enemy.types.length)];
const enemy = document.createElement('div');
enemy.className = 'enemy';
const head = document.createElement('div');
head.className = 'enemy-head';
head.style.backgroundColor = type.color;
const body = document.createElement('div');
body.className = 'enemy-body';
body.style.backgroundColor = shadeColor(type.color, -20);
const legs = document.createElement('div');
legs.className = 'enemy-legs';
legs.style.backgroundColor = shadeColor(type.color, -40);
const healthBar = document.createElement('div');
healthBar.className = 'enemy-health-bar';
enemy.appendChild(head);
enemy.appendChild(body);
enemy.appendChild(legs);
enemy.appendChild(healthBar);
document.body.appendChild(enemy);
// Posição aleatória na borda da tela
let x, y;
if (Math.random() < 0.5) {
x = Math.random() < 0.5 ? 0 : config.width;
y = Math.random() * config.height;
} else {
x = Math.random() * config.width;
y = Math.random() < 0.5 ? 0 : config.height;
}
enemy.x = x;
enemy.y = y;
enemy.style.left = `${x}px`;
enemy.style.top = `${y}px`;
// Configurações do inimigo
enemy.health = config.enemy.baseHealth * type.healthMultiplier * (1 + (config.wave.current - 1) * 0.1);
enemy.maxHealth = enemy.health;
enemy.speed = config.enemy.baseSpeed * type.speedMultiplier * (1 + (config.wave.current - 1) * 0.05);
enemy.damage = config.enemy.baseDamage * type.damageMultiplier;
enemy.points = config.enemy.basePoints * type.pointsMultiplier;
enemy.type = type.name;
enemy.enemyType = type;
state.enemies.push(enemy);
config.game.lastSpawnTime = now;
config.game.enemiesSpawned++;
config.game.enemiesAlive++;
}
function moveEnemies() {
for (let i = 0; i < state.enemies.length; i++) {
const enemy = state.enemies[i];
// Calcular direção para o jogador (centro da tela)
const dx = config.game.playerX - enemy.x;
const dy = config.game.playerY - enemy.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// Normalizar e multiplicar pela velocidade
const vx = (dx / distance) * enemy.speed;
const vy = (dy / distance) * enemy.speed;
// Atualizar posição
enemy.x += vx;
enemy.y += vy;
enemy.style.left = `${enemy.x}px`;
enemy.style.top = `${enemy.y}px`;
// Atualizar barra de vida
const healthBar = enemy.querySelector('.enemy-health-bar');
if (healthBar) {
const healthPercent = (enemy.health / enemy.maxHealth) * 100;
healthBar.style.width = `${healthPercent}%`;
}
// Verificar colisão com jogador (raio de 50px do centro)
if (distance < 50) {
// Dano ao jogador
config.player.health -= enemy.damage;
updateHealthDisplay();
// Efeito de dano
showHitEffect();
// Remover inimigo
enemy.remove();
state.enemies.splice(i, 1);
i--;
config.game.enemiesAlive--;
// Tocar som de dano ao jogador
playSound('playerHit');
// Verificar se jogador morreu
if (config.player.health <= 0) {
gameOver();
}
}
}
}
function handleMouseMove(e) {
if (!config.game.isRunning || config.game.isPaused) return;
// Atualizar posição do mouse
config.game.mouseX = e.clientX;
config.game.mouseY = e.clientY;
// Calcular movimento do mouse para o balanço da arma
state.mouseMovementX = e.movementX;
state.mouseMovementY = e.movementY;
}
function handleShoot() {
if (!config.game.isRunning || config.game.isPaused || config.game.isReloading) return;
const now = Date.now();
const weapon = config.weapons[config.game.currentWeapon];
// Verificar taxa de disparo
if (now - config.game.lastShotTime < weapon.fireRate) return;
// Verificar munição
if (config.player.ammo <= 0) {
playSound('empty');
return;
}
// Efeito de recuo da arma
weaponImage.classList.add('recoil');
setTimeout(() => weaponImage.classList.remove('recoil'), 100);
// Disparar bala
config.player.ammo--;
updateAmmoDisplay();
config.game.lastShotTime = now;
// Ejetar cápsula
ejectShell();
// Tocar som de tiro
playSound('shoot');
// Criar traçante de bala
createBulletTracer();
// Verificar acerto
checkHit();
}
function checkHit() {
const weapon = config.weapons[config.game.currentWeapon];
// Calcular precisão com base na arma e movimento do mouse
const accuracy = weapon.accuracy - Math.min(0.1, Math.abs(state.mouseMovementX) / 100 + Math.abs(state.mouseMovementY) / 100);
// Verificar se o tiro acertou com base na precisão
if (Math.random() > accuracy) {
// Tiro errado - criar impacto na parede
createBulletImpact(config.game.mouseX + (Math.random() * 40 - 20), config.game.mouseY + (Math.random() * 40 - 20));
return;
}
// Verificar colisão com inimigos
let hitEnemy = false;
for (let i = 0; i < state.enemies.length; i++) {
const enemy = state.enemies[i];
// Calcular distância entre o cursor e o inimigo
const distance = Math.sqrt(
Math.pow(config.game.mouseX - (enemy.x + 30), 2) +
Math.pow(config.game.mouseY - (enemy.y + 15), 2)
);
// Verificar se acertou a cabeça (dano crítico)
const headDistance = Math.sqrt(
Math.pow(config.game.mouseX - (enemy.x + 30), 2) +
Math.pow(config.game.mouseY - (enemy.y + 85), 2)
);
if (headDistance < 15) { // Raio da cabeça
// Dano crítico
enemy.health -= weapon.damage * 2;
hitEnemy = true;
// Mostrar marcador de acerto
showHitMarker('headshot');
// Efeito de sangue
createBloodSplat(enemy.x + 30, enemy.y + 85, true);
break;
} else if (distance < 40) { // Raio do corpo
// Dano normal
enemy.health -= weapon.damage;
hitEnemy = true;
// Mostrar marcador de acerto
showHitMarker();
// Efeito de sangue
createBloodSplat(enemy.x + 30, enemy.y + 50);
break;
}
}
if (!hitEnemy) {
// Criar impacto na parede
createBulletImpact(config.game.mouseX, config.game.mouseY);
}
}
function handleKeyDown(e) {
if (e.key.toLowerCase() === 'r' && config.game.isRunning && !config.game.isPaused && !config.game.isReloading) {
reload();
}
if (e.key === 'Escape') {
if (config.game.isRunning && !config.game.isPaused) {
pauseGame();
} else if (config.game.isPaused) {
resumeGame();
}
}
// Trocar de arma
if (e.key >= '1' && e.key <= '3' && config.game.isRunning && !config.game.isPaused) {
const weaponIndex = parseInt(e.key) - 1;
if (weaponIndex < config.weapons.length) {
changeWeapon(weaponIndex);
}
}
}
function handleKeyUp(e) {
// Implementar movimentação se necessário
}
function reload() {
const weapon = config.weapons[config.game.currentWeapon];
if (config.player.ammo === weapon.maxAmmo || config.player.totalAmmo <= 0) return;
config.game.isReloading = true;
reloadIndicator.classList.add('show');
// Tocar som de recarregar
playSound('reload');
setTimeout(() => {
const ammoNeeded = weapon.maxAmmo - config.player.ammo;
const ammoToAdd = Math.min(ammoNeeded, config.player.totalAmmo);
config.player.ammo += ammoToAdd;
config.player.totalAmmo -= ammoToAdd;
updateAmmoDisplay();
config.game.isReloading = false;
reloadIndicator.classList.remove('show');
}, weapon.reloadTime);
}
function changeWeapon(index) {
if (index === config.game.currentWeapon || index >= config.weapons.length) return;
config.game.currentWeapon = index;
const weapon = config.weapons[index];
// Atualizar munição do jogador
config.player.ammo = weapon.ammo;
config.player.totalAmmo = weapon.totalAmmo;
// Atualizar imagem da arma
weaponImage.src = weapon.image;
// Atualizar HUD
weaponName.textContent = weapon.name;
weaponType.textContent = weapon.type;
updateAmmoDisplay();
// Tocar som de troca de arma
playSound('weaponSwitch');
}
function createBulletTracer() {
const tracer = document.createElement('div');
tracer.className = 'bullet-tracer';
// Posição inicial (saindo da arma)
const startX = canvas.width - 100;
const startY = canvas.height - 50;
// Posição final (direção do mouse com pequeno desvio aleatório)
const angle = Math.atan2(config.game.mouseY - startY, config.game.mouseX - startX);
const deviation = (Math.random() - 0.5) * 0.1; // Pequeno desvio aleatório
const endX = startX + Math.cos(angle + deviation) * 2000;
const endY = startY + Math.sin(angle + deviation) * 2000;
// Configurar elemento
tracer.style.left = `${startX}px`;
tracer.style.top = `${startY}px`;
tracer.style.width = `${Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2))}px`;
tracer.style.transform = `rotate(${angle + deviation}rad)`;
tracer.style.opacity = '1';
document.body.appendChild(tracer);
// Animação de desaparecimento
setTimeout(() => {
tracer.style.opacity = '0';
setTimeout(() => tracer.remove(), 100);
}, 50);
}
function createBloodSplat(x, y, isHeadshot = false) {
const splat = document.createElement('div');
splat.className = 'blood-splat';
splat.style.left = `${x - 20}px`;
splat.style.top = `${y - 20}px`;
if (isHeadshot) {
splat.style.width = '60px';
splat.style.height = '60px';
splat.style.backgroundImage = 'radial-gradient(circle, rgba(200,0,0,0.9) 0%, rgba(150,0,0,0.9) 70%, transparent 100%)';
}
document.body.appendChild(splat);
// Remover após animação
setTimeout(() => splat.remove(), 2000);
}
function createBulletImpact(x, y) {
const impact = document.createElement('div');
impact.className = 'bullet-impact';
impact.style.left = `${x - 7}px`;
impact.style.top = `${y - 7}px`;
document.body.appendChild(impact);
// Remover após animação
setTimeout(() => impact.remove(), 1000);
}
function ejectShell() {
const shell = document.createElement('div');
shell.className = 'bullet-shell';
// Posição inicial (saindo da arma)
shell.style.left = `${canvas.width - 80}px`;
shell.style.top = `${canvas.height - 60}px`;
document.body.appendChild(shell);
// Remover após animação
setTimeout(() => shell.remove(), 1000);
}
function showHitMarker(type = 'normal') {
hitMarker.style.opacity = '1';
if (type === 'headshot') {
// Marcador de headshot vermelho
hitMarker.querySelectorAll('.hit-line').forEach(line => {
line.style.backgroundColor = 'red';
line.style.boxShadow = '0 0 10px red';
});
}
setTimeout(() => {
hitMarker.style.opacity = '0';
// Resetar cor
if (type === 'headshot') {
hitMarker.querySelectorAll('.hit-line').forEach(line => {
line.style.backgroundColor = 'white';
line.style.boxShadow = '0 0 5px var(--primary)';
});
}
}, 200);
}
function showHitEffect() {
// Efeito de tela vermelha
hitEffect.style.opacity = '0.5';
setTimeout(() => hitEffect.style.opacity = '0', 200);
// Efeito de sangue na tela
bloodEffect.style.opacity = '0.3';
setTimeout(() => bloodEffect.style.opacity = '0', 1000);
// Agitar tela
document.body.classList.add('screen-shake');
setTimeout(() => document.body.classList.remove('screen-shake'), 300);
}
function addKillFeed(enemyType, isHeadshot = false) {
const weapons = ['M4A1', 'Desert Eagle', 'MP5'];
const weaponName = weapons[config.game.currentWeapon];
const item = document.createElement('div');
item.className = 'kill-feed-item';
if (isHeadshot) {
item.innerHTML = `<span class="enemy">${enemyType}</span> <i class="fas fa-skull"></i> <span class="weapon">${weaponName}</span>`;
} else {
item.innerHTML = `<span class="enemy">${enemyType}</span> <span class="weapon">${weaponName}</span>`;
}
killFeed.appendChild(item);
// Mostrar item
setTimeout(() => item.classList.add('show'), 10);
// Limitar número de itens no kill feed
if (killFeed.children.length > 5) {
killFeed.removeChild(killFeed.children[0]);
}
// Remover após alguns segundos
setTimeout(() => {
item.classList.remove('show');
setTimeout(() => item.remove(), 300);
}, 5000);
}
function updateHealthDisplay() {
healthFill.style.width = `${config.player.health}%`;
// Mudar cor conforme a vida
if (config.player.health > 60) {
healthFill.style.background = 'linear-gradient(90deg, #10b981, #34d399)';
} else if (config.player.health > 30) {
healthFill.style.background = 'linear-gradient(90deg, #f59e0b, #fbbf24)';
} else {
healthFill.style.background = 'linear-gradient(90deg, #ef4444, #f87171)';
}
}
function updateAmmoDisplay() {
const currentAmmo = ammoCount.querySelector('.current');
const totalAmmo = ammoCount.querySelector('.total');
currentAmmo.textContent = config.player.ammo;
totalAmmo.textContent = config.player.totalAmmo;
// Mudar cor se munição está baixa
if (config.player.ammo < config.weapons[config.game.currentWeapon].maxAmmo * 0.3) {
currentAmmo.style.color = '#ef4444';
} else {
currentAmmo.style.color = 'white';
}
if (config.player.totalAmmo < config.weapons[config.game.currentWeapon].maxAmmo) {
totalAmmo.style.color = '#f59e0b';
} else {
totalAmmo.style.color = 'rgba(255,255,255,0.7)';
}
}
function updateScoreDisplay() {
scoreValue.textContent = config.game.score;
}
function updateWaveDisplay() {
waveValue.textContent = config.wave.current;
// Efeito de destaque quando a onda muda
waveValue.style.transform = 'scale(1.2)';
setTimeout(() => waveValue.style.transform = 'scale(1)', 500);
}
function startWeaponSway() {
// Animação contínua de balanço da arma
if (state.weaponSwayInterval) clearInterval(state.weaponSwayInterval);
state.weaponSwayInterval = setInterval(() => {
if (!config.game.isRunning || config.game.isPaused) return;
weaponImage.style.transform = `translateX(${Math.sin(Date.now() / 1000) * 5}px) translateY(${Math.cos(Date.now() / 800) * 3}px)`;
}, 50);
}
function shadeColor(color, percent) {
// Escurecer ou clarear uma cor
let R = parseInt(color.substring(1,3), 16);
let G = parseInt(color.substring(3,5), 16);
let B = parseInt(color.substring(5,7), 16);
R = parseInt(R * (100 + percent) / 100);
G = parseInt(G * (100 + percent) / 100);
B = parseInt(B * (100 + percent) / 100);
R = R < 255 ? R : 255;
G = G < 255 ? G : 255;
B = B < 255 ? B : 255;
R = Math.round(R);
G = Math.round(G);
B = Math.round(B);
const RR = R.toString(16).length == 1 ? "0" + R.toString(16) : R.toString(16);
const GG = G.toString(16).length == 1 ? "0" + G.toString(16) : G.toString(16);
const BB = B.toString(16).length == 1 ? "0" + B.toString(16) : B.toString(16);
return "#" + RR + GG + BB;
}
function playSound(type) {
// Implementação simplificada - na prática você usaria arquivos de áudio
console.log(`Playing sound: ${type}`);
}
function gameLoop() {
if (!config.game.isRunning || config.game.isPaused) return;
// Limpar canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Desenhar ambiente (simulação 3D simplificada)
drawEnvironment();
// Atualizar inimigos
spawnEnemy();
moveEnemies();
// Verificar mortes de inimigos
checkEnemyDeaths();
// Verificar se a onda foi completada
if (config.game.enemiesSpawned >= config.wave.enemiesPerWave &&
state.enemies.length === 0 &&
config.game.enemiesAlive === 0) {
nextWave();
}
// Continuar loop
requestAnimationFrame(gameLoop);
}
function drawEnvironment() {
// Desenhar fundo (simulação 3D simplificada)
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0, '#1a2a3a');
gradient.addColorStop(1, '#0a0a0a');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Desenhar "paredes" (simulação de perspectiva)
ctx.fillStyle = '#2a3a4a';
ctx.beginPath();
ctx.moveTo(0, canvas.height);
ctx.lineTo(canvas.width / 3, canvas.height / 3);
ctx.lineTo(2 * canvas.width / 3, canvas.height / 3);
ctx.lineTo(canvas.width, canvas.height);
ctx.closePath();
ctx.fill();
// Desenhar "chão" com padrão de grade
ctx.fillStyle = '#1a1a1a';
ctx.beginPath();
ctx.moveTo(0, canvas.height);
ctx.lineTo(canvas.width, canvas.height);
ctx.lineTo(2 * canvas.width / 3, canvas.height / 3);
ctx.lineTo(canvas.width / 3, canvas.height / 3);
ctx.closePath();
ctx.fill();
// Grade no chão (perspectiva)
ctx.strokeStyle = 'rgba(100, 100, 100, 0.3)';
ctx.lineWidth = 1;
// Linhas horizontais
for (let i = 0; i < 10; i++) {
const y = canvas.height - i * 50;
const scale = 1 - i * 0.08;
ctx.beginPath();
ctx.moveTo(canvas.width / 2 - canvas.width * scale / 2, y);
ctx.lineTo(canvas.width / 2 + canvas.width * scale / 2, y);
ctx.stroke();
}
// Linhas verticais
for (let i = -5; i <= 5; i++) {
const x = canvas.width / 2 + i * 50;
const scale = 1 - Math.abs(i) * 0.05;
ctx.beginPath();
ctx.moveTo(x, canvas.height);
ctx.lineTo(canvas.width / 2, canvas.height / 3 * scale);
ctx.stroke();
}
}
function checkEnemyDeaths() {
for (let i = 0; i < state.enemies.length; i++) {
const enemy = state.enemies[i];
if (enemy.health <= 0) {
// Inimigo morto
enemy.classList.add('dead');
// Efeito de explosão
const explosion = document.createElement('div');
explosion.className = 'explosion';
explosion.style.left = `${enemy.x - 20}px`;
explosion.style.top = `${enemy.y - 20}px`;
document.body.appendChild(explosion);
// Adicionar pontos
config.game.score += enemy.points;
config.game.kills++;
updateScoreDisplay();
// Atualizar contagem de inimigos
config.game.enemiesAlive--;
// Adicionar ao kill feed
const isHeadshot = enemy.health < -enemy.maxHealth * 0.5; // Dano excessivo indica headshot
addKillFeed(enemy.type, isHeadshot);
// Remover inimigo após animação
setTimeout(() => {
enemy.remove();
state.enemies.splice(i, 1);
i--;
}, 500);
// Tocar som de explosão
playSound('explosion');
}
}
}
// Redimensionar canvas quando a janela for redimensionada
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
config.width = window.innerWidth;
config.height = window.innerHeight;
config.game.playerX = canvas.width / 2;
config.game.playerY = canvas.height / 2;
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Xacodavt/jogo" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>