| <!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 { |
| 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 { |
| 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%); |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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; |
| } |
| |
| |
| #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-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-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; } |
| } |
| |
| |
| .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; } |
| } |
| |
| |
| #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; |
| } |
| |
| |
| .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); } |
| } |
| |
| |
| @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 { |
| 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.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 { |
| 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-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 { |
| 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> |
| |
| |
| <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> |
| |
| |
| <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> |
| |
| |
| <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> |
| |
| |
| <div id="hitEffect"></div> |
| <div id="bloodEffect"></div> |
| |
| |
| <div id="killFeed"></div> |
| |
| |
| <div id="weaponViewmodel"> |
| <img id="weaponImage" src="https://cdn-icons-png.flaticon.com/512/821/821356.png" alt="Weapon"> |
| </div> |
| |
| |
| <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> |
| |
| 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, |
| reloadTime: 2000, |
| accuracy: 0.95, |
| 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, |
| 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 |
| } |
| }; |
| |
| |
| const state = { |
| enemies: [], |
| bullets: [], |
| explosions: [], |
| bloodSplats: [], |
| bulletImpacts: [], |
| bulletShells: [], |
| killFeedItems: [], |
| weaponSwayInterval: null, |
| mouseMovementX: 0, |
| mouseMovementY: 0 |
| }; |
| |
| |
| 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'); |
| |
| |
| canvas.width = config.width; |
| canvas.height = config.height; |
| config.game.playerX = canvas.width / 2; |
| config.game.playerY = canvas.height / 2; |
| |
| |
| 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); |
| |
| |
| initGame(); |
| |
| function initGame() { |
| |
| changeWeapon(0); |
| |
| |
| startWeaponSway(); |
| |
| |
| startScreen.style.display = 'flex'; |
| } |
| |
| function startGame() { |
| |
| resetGameState(); |
| |
| |
| startScreen.style.display = 'none'; |
| gameOverScreen.style.display = 'none'; |
| pauseScreen.style.display = 'none'; |
| |
| |
| config.game.isRunning = true; |
| config.game.isPaused = false; |
| |
| |
| gameLoop(); |
| |
| |
| playSound('start'); |
| } |
| |
| function resetGameState() { |
| |
| config.player.health = config.player.maxHealth; |
| config.player.ammo = config.weapons[config.game.currentWeapon].ammo; |
| config.player.totalAmmo = config.weapons[config.game.currentWeapon].totalAmmo; |
| |
| |
| config.wave.current = 1; |
| config.wave.enemiesPerWave = 5; |
| config.game.enemiesSpawned = 0; |
| config.game.enemiesAlive = 0; |
| |
| |
| config.game.score = 0; |
| config.game.kills = 0; |
| |
| |
| config.game.lastShotTime = 0; |
| config.game.lastSpawnTime = 0; |
| config.game.lastWaveTime = Date.now(); |
| |
| |
| state.enemies = []; |
| state.bullets = []; |
| state.explosions = []; |
| state.bloodSplats = []; |
| state.bulletImpacts = []; |
| state.bulletShells = []; |
| state.killFeedItems = []; |
| |
| |
| document.querySelectorAll('.enemy, .bullet-tracer, .blood-splat, .bullet-impact, .explosion, .bullet-shell, .kill-feed-item').forEach(el => el.remove()); |
| |
| |
| 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; |
| |
| |
| finalScore.textContent = config.game.score; |
| finalWave.textContent = config.wave.current; |
| finalKills.textContent = config.game.kills; |
| |
| |
| gameOverScreen.style.display = 'flex'; |
| |
| |
| playSound('gameOver'); |
| } |
| |
| function nextWave() { |
| config.wave.current++; |
| config.wave.enemiesPerWave += config.wave.increasePerWave; |
| config.game.enemiesSpawned = 0; |
| config.game.lastWaveTime = Date.now(); |
| |
| |
| config.player.totalAmmo += config.weapons[config.game.currentWeapon].maxAmmo * 2; |
| updateAmmoDisplay(); |
| |
| |
| config.player.health = Math.min(config.player.maxHealth, config.player.health + 20); |
| updateHealthDisplay(); |
| |
| |
| if (config.wave.current % config.wave.bossWaveInterval === 0) { |
| spawnBoss(); |
| } |
| |
| |
| updateWaveDisplay(); |
| |
| |
| playSound('waveComplete'); |
| } |
| |
| function spawnBoss() { |
| |
| 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); |
| |
| |
| 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`; |
| |
| |
| 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++; |
| |
| |
| 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; |
| |
| |
| 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); |
| |
| |
| 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`; |
| |
| |
| 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]; |
| |
| |
| const dx = config.game.playerX - enemy.x; |
| const dy = config.game.playerY - enemy.y; |
| const distance = Math.sqrt(dx * dx + dy * dy); |
| |
| |
| const vx = (dx / distance) * enemy.speed; |
| const vy = (dy / distance) * enemy.speed; |
| |
| |
| enemy.x += vx; |
| enemy.y += vy; |
| |
| enemy.style.left = `${enemy.x}px`; |
| enemy.style.top = `${enemy.y}px`; |
| |
| |
| const healthBar = enemy.querySelector('.enemy-health-bar'); |
| if (healthBar) { |
| const healthPercent = (enemy.health / enemy.maxHealth) * 100; |
| healthBar.style.width = `${healthPercent}%`; |
| } |
| |
| |
| if (distance < 50) { |
| |
| config.player.health -= enemy.damage; |
| updateHealthDisplay(); |
| |
| |
| showHitEffect(); |
| |
| |
| enemy.remove(); |
| state.enemies.splice(i, 1); |
| i--; |
| config.game.enemiesAlive--; |
| |
| |
| playSound('playerHit'); |
| |
| |
| if (config.player.health <= 0) { |
| gameOver(); |
| } |
| } |
| } |
| } |
| |
| function handleMouseMove(e) { |
| if (!config.game.isRunning || config.game.isPaused) return; |
| |
| |
| config.game.mouseX = e.clientX; |
| config.game.mouseY = e.clientY; |
| |
| |
| 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]; |
| |
| |
| if (now - config.game.lastShotTime < weapon.fireRate) return; |
| |
| |
| if (config.player.ammo <= 0) { |
| playSound('empty'); |
| return; |
| } |
| |
| |
| weaponImage.classList.add('recoil'); |
| setTimeout(() => weaponImage.classList.remove('recoil'), 100); |
| |
| |
| config.player.ammo--; |
| updateAmmoDisplay(); |
| config.game.lastShotTime = now; |
| |
| |
| ejectShell(); |
| |
| |
| playSound('shoot'); |
| |
| |
| createBulletTracer(); |
| |
| |
| checkHit(); |
| } |
| |
| function checkHit() { |
| const weapon = config.weapons[config.game.currentWeapon]; |
| |
| |
| const accuracy = weapon.accuracy - Math.min(0.1, Math.abs(state.mouseMovementX) / 100 + Math.abs(state.mouseMovementY) / 100); |
| |
| |
| if (Math.random() > accuracy) { |
| |
| createBulletImpact(config.game.mouseX + (Math.random() * 40 - 20), config.game.mouseY + (Math.random() * 40 - 20)); |
| return; |
| } |
| |
| |
| let hitEnemy = false; |
| for (let i = 0; i < state.enemies.length; i++) { |
| const enemy = state.enemies[i]; |
| |
| |
| const distance = Math.sqrt( |
| Math.pow(config.game.mouseX - (enemy.x + 30), 2) + |
| Math.pow(config.game.mouseY - (enemy.y + 15), 2) |
| ); |
| |
| |
| 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) { |
| |
| enemy.health -= weapon.damage * 2; |
| hitEnemy = true; |
| |
| |
| showHitMarker('headshot'); |
| |
| |
| createBloodSplat(enemy.x + 30, enemy.y + 85, true); |
| |
| break; |
| } else if (distance < 40) { |
| |
| enemy.health -= weapon.damage; |
| hitEnemy = true; |
| |
| |
| showHitMarker(); |
| |
| |
| createBloodSplat(enemy.x + 30, enemy.y + 50); |
| |
| break; |
| } |
| } |
| |
| if (!hitEnemy) { |
| |
| 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(); |
| } |
| } |
| |
| |
| 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) { |
| |
| } |
| |
| 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'); |
| |
| |
| 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]; |
| |
| |
| config.player.ammo = weapon.ammo; |
| config.player.totalAmmo = weapon.totalAmmo; |
| |
| |
| weaponImage.src = weapon.image; |
| |
| |
| weaponName.textContent = weapon.name; |
| weaponType.textContent = weapon.type; |
| updateAmmoDisplay(); |
| |
| |
| playSound('weaponSwitch'); |
| } |
| |
| function createBulletTracer() { |
| const tracer = document.createElement('div'); |
| tracer.className = 'bullet-tracer'; |
| |
| |
| const startX = canvas.width - 100; |
| const startY = canvas.height - 50; |
| |
| |
| const angle = Math.atan2(config.game.mouseY - startY, config.game.mouseX - startX); |
| const deviation = (Math.random() - 0.5) * 0.1; |
| const endX = startX + Math.cos(angle + deviation) * 2000; |
| const endY = startY + Math.sin(angle + deviation) * 2000; |
| |
| |
| 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); |
| |
| |
| 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); |
| |
| |
| 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); |
| |
| |
| setTimeout(() => impact.remove(), 1000); |
| } |
| |
| function ejectShell() { |
| const shell = document.createElement('div'); |
| shell.className = 'bullet-shell'; |
| |
| |
| shell.style.left = `${canvas.width - 80}px`; |
| shell.style.top = `${canvas.height - 60}px`; |
| |
| document.body.appendChild(shell); |
| |
| |
| setTimeout(() => shell.remove(), 1000); |
| } |
| |
| function showHitMarker(type = 'normal') { |
| hitMarker.style.opacity = '1'; |
| |
| if (type === 'headshot') { |
| |
| hitMarker.querySelectorAll('.hit-line').forEach(line => { |
| line.style.backgroundColor = 'red'; |
| line.style.boxShadow = '0 0 10px red'; |
| }); |
| } |
| |
| setTimeout(() => { |
| hitMarker.style.opacity = '0'; |
| |
| |
| if (type === 'headshot') { |
| hitMarker.querySelectorAll('.hit-line').forEach(line => { |
| line.style.backgroundColor = 'white'; |
| line.style.boxShadow = '0 0 5px var(--primary)'; |
| }); |
| } |
| }, 200); |
| } |
| |
| function showHitEffect() { |
| |
| hitEffect.style.opacity = '0.5'; |
| setTimeout(() => hitEffect.style.opacity = '0', 200); |
| |
| |
| bloodEffect.style.opacity = '0.3'; |
| setTimeout(() => bloodEffect.style.opacity = '0', 1000); |
| |
| |
| 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); |
| |
| |
| setTimeout(() => item.classList.add('show'), 10); |
| |
| |
| if (killFeed.children.length > 5) { |
| killFeed.removeChild(killFeed.children[0]); |
| } |
| |
| |
| setTimeout(() => { |
| item.classList.remove('show'); |
| setTimeout(() => item.remove(), 300); |
| }, 5000); |
| } |
| |
| function updateHealthDisplay() { |
| healthFill.style.width = `${config.player.health}%`; |
| |
| |
| 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; |
| |
| |
| 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; |
| |
| |
| waveValue.style.transform = 'scale(1.2)'; |
| setTimeout(() => waveValue.style.transform = 'scale(1)', 500); |
| } |
| |
| function startWeaponSway() { |
| |
| 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) { |
| |
| 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) { |
| |
| console.log(`Playing sound: ${type}`); |
| } |
| |
| function gameLoop() { |
| if (!config.game.isRunning || config.game.isPaused) return; |
| |
| |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| drawEnvironment(); |
| |
| |
| spawnEnemy(); |
| moveEnemies(); |
| |
| |
| checkEnemyDeaths(); |
| |
| |
| if (config.game.enemiesSpawned >= config.wave.enemiesPerWave && |
| state.enemies.length === 0 && |
| config.game.enemiesAlive === 0) { |
| nextWave(); |
| } |
| |
| |
| requestAnimationFrame(gameLoop); |
| } |
| |
| function drawEnvironment() { |
| |
| 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); |
| |
| |
| 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(); |
| |
| |
| 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(); |
| |
| |
| ctx.strokeStyle = 'rgba(100, 100, 100, 0.3)'; |
| ctx.lineWidth = 1; |
| |
| |
| 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(); |
| } |
| |
| |
| 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) { |
| |
| enemy.classList.add('dead'); |
| |
| |
| 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); |
| |
| |
| config.game.score += enemy.points; |
| config.game.kills++; |
| updateScoreDisplay(); |
| |
| |
| config.game.enemiesAlive--; |
| |
| |
| const isHeadshot = enemy.health < -enemy.maxHealth * 0.5; |
| addKillFeed(enemy.type, isHeadshot); |
| |
| |
| setTimeout(() => { |
| enemy.remove(); |
| state.enemies.splice(i, 1); |
| i--; |
| }, 500); |
| |
| |
| playSound('explosion'); |
| } |
| } |
| } |
| |
| |
| 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> |