<!DOCTYPE html>
<html>
<head>
<title>Alien 8-Bit Fighter</title>
<style>
body {
margin: 0;
padding: 0;
background: #000;
overflow: hidden;
font-family: 'Courier New', monospace;
}
canvas {
display: block;
margin: 0 auto;
background: #111;
}
.player-info {
position: absolute;
color: #0f0;
padding: 10px;
font-size: 16px;
}
#player1 {
top: 10px;
left: 10px;
}
#player2 {
top: 10px;
right: 10px;
}
#round-info {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 24px;
text-align: center;
pointer-events: none;
}
.health-bar {
height: 10px;
margin-top: 5px;
background: #333;
}
.health-fill {
height: 100%;
background: #0f0;
}
#player2 .health-fill {
background: #f0f;
}
.powerup-timer {
margin-top: 5px;
height: 3px;
background: #333;
}
.powerup-fill {
height: 100%;
}
</style>
</head>
<body>
<div id="player1" class="player-info">
Player 1<br>
<div class="health-bar"><div class="health-fill" id="p1-health"></div></div>
Boost: <span id="p1-boost">100%</span>
<div id="p1-powerup" style="display:none">
<span id="p1-powerup-name"></span>
<div class="powerup-timer"><div class="powerup-fill" id="p1-powerup-timer"></div></div>
</div>
</div>
<div id="player2" class="player-info">
Player 2<br>
<div class="health-bar"><div class="health-fill" id="p2-health"></div></div>
Boost: <span id="p2-boost">100%</span>
<div id="p2-powerup" style="display:none">
<span id="p2-powerup-name"></span>
<div class="powerup-timer"><div class="powerup-fill" id="p2-powerup-timer"></div></div>
</div>
</div>
<div id="round-info"></div>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script src="game.js"></script>
</body>
</html>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game state
const state = {
players: [
{
x: 200, y: 300, width: 40, height: 30,
color: '#0f0', speedX: 0, speedY: 0,
maxSpeed: 5, health: 100, boost: 100,
facing: 1, lastShot: 0, score: 0,
powerup: null, powerupEndTime: 0,
originalColor: '#0f0'
},
{
x: 600, y: 300, width: 40, height: 30,
color: '#f0f', speedX: 0, speedY: 0,
maxSpeed: 5, health: 100, boost: 100,
facing: -1, lastShot: 0, score: 0,
powerup: null, powerupEndTime: 0,
originalColor: '#f0f'
}
],
projectiles: [],
powerups: [],
round: 1,
roundActive: true,
roundTimer: 0,
roundEndTime: 0,
gameStarted: false,
lastPowerupSpawn: 0,
powerupSpawnInterval: 10000
};
// Enhanced powerup types
const POWERUPS = {
HEALTH: {
name: 'HEALTH+',
color: '#0f0',
duration: 0,
effect: (player) => { player.health = Math.min(player.health + 30, 100); }
},
BOOST: {
name: 'BOOST+',
color: '#0af',
duration: 10000,
effect: (player) => {
player.maxSpeed = 7;
player.boost = 100;
},
cleanup: (player) => { player.maxSpeed = 5; }
},
RAPID_FIRE: {
name: 'RAPID FIRE',
color: '#f80',
duration: 8000,
effect: (player) => { player.rapidFire = true; },
cleanup: (player) => { player.rapidFire = false; }
},
SHIELD: {
name: 'SHIELD',
color: '#0ff',
duration: 6000,
effect: (player) => { player.shield = true; },
cleanup: (player) => { player.shield = false; }
},
INVISIBILITY: {
name: 'INVISIBLE',
color: '#888',
duration: 5000,
effect: (player) => {
player.invisible = true;
player.color = '#111';
},
cleanup: (player) => {
player.invisible = false;
player.color = player.originalColor;
}
},
TIME_SLOW: {
name: 'TIME WARP',
color: '#f0f',
duration: 4000,
effect: (player) => {
player.timeSlow = true;
state.powerupSpawnInterval = 15000;
},
cleanup: (player) => {
player.timeSlow = false;
state.powerupSpawnInterval = 10000;
}
},
MULTI_SHOT: {
name: 'MULTI SHOT',
color: '#8f0',
duration: 7000,
effect: (player) => { player.multiShot = true; },
cleanup: (player) => { player.multiShot = false; }
},
BOOST_OVERDRIVE: {
name: 'OVERDRIVE',
color: '#f00',
duration: 5000,
effect: (player) => {
player.maxSpeed = 9;
player.boost = 200;
player.boostOverdrive = true;
},
cleanup: (player) => {
player.maxSpeed = 5;
player.boostOverdrive = false;
}
},
BLACK_HOLE: {
name: 'BLACK HOLE',
color: '#000',
duration: 0,
effect: (player, index) => {
const otherPlayer = state.players[index === 0 ? 1 : 0];
otherPlayer.x = player.x + (index === 0 ? 100 : -100);
otherPlayer.y = player.y;
}
},
CONFUSION: {
name: 'CONFUSION',
color: '#a0a',
duration: 6000,
effect: (player, index) => {
state.players[index === 0 ? 1 : 0].confused = true;
},
cleanup: (player, index) => {
state.players[index === 0 ? 1 : 0].confused = false;
}
}
};
// Input handling
const keys = {
w: false, a: false, s: false, d: false, f: false,
ArrowUp: false, ArrowLeft: false, ArrowDown: false, ArrowRight: false, Enter: false
};
document.addEventListener('keydown', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = true;
if (!state.gameStarted) {
state.gameStarted = true;
startRound();
}
}
});
document.addEventListener('keyup', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = false;
}
});
// Game loop
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
// Update game state
function update() {
if (!state.roundActive) {
if (state.roundEndTime > 0 && Date.now() - state.roundEndTime > 2000) {
resetRound();
}
return;
}
// Spawn powerups randomly
if (Date.now() - state.lastPowerupSpawn > state.powerupSpawnInterval && Math.random() < 0.03) {
spawnPowerup();
state.lastPowerupSpawn = Date.now();
}
// Player 1 controls (WASD + F to shoot)
if (keys.w && state.players[0].boost > 0) {
state.players[0].speedY = -state.players[0].maxSpeed;
state.players[0].boost -= state.players[0].boostOverdrive ? 0.8 : 0.5;
}
if (keys.s) state.players[0].speedY = state.players[0].maxSpeed/2;
if (keys.a) {
state.players[0].speedX = -state.players[0].maxSpeed;
state.players[0].facing = -1;
}
if (keys.d) {
state.players[0].speedX = state.players[0].maxSpeed;
state.players[0].facing = 1;
}
if (keys.f) {
const cooldown = state.players[0].rapidFire ? 200 : 500;
if (Date.now() - state.players[0].lastShot > cooldown) {
shoot(0);
if (state.players[0].multiShot) {
setTimeout(() => shoot(0), 100);
setTimeout(() => shoot(0), 200);
}
state.players[0].lastShot = Date.now();
}
}
// Player 2 controls (Arrow keys + Enter to shoot)
const upKey = state.players[1].confused ? 'ArrowDown' : 'ArrowUp';
const downKey = state.players[1].confused ? 'ArrowUp' : 'ArrowDown';
const leftKey = state.players[1].confused ? 'ArrowRight' : 'ArrowLeft';
const rightKey = state.players[1].confused ? 'ArrowLeft' : 'ArrowRight';
if (keys[upKey] && state.players[1].boost > 0) {
state.players[1].speedY = -state.players[1].maxSpeed;
state.players[1].boost -= state.players[1].boostOverdrive ? 0.8 : 0.5;
}
if (keys[downKey]) state.players[1].speedY = state.players[1].maxSpeed/2;
if (keys[leftKey]) {
state.players[1].speedX = -state.players[1].maxSpeed;
state.players[1].facing = -1;
}
if (keys[rightKey]) {
state.players[1].speedX = state.players[1].maxSpeed;
state.players[1].facing = 1;
}
if (keys.Enter) {
const cooldown = state.players[1].rapidFire ? 200 : 500;
if (Date.now() - state.players[1].lastShot > cooldown) {
shoot(1);
if (state.players[1].multiShot) {
setTimeout(() => shoot(1), 100);
setTimeout(() => shoot(1), 200);
}
state.players[1].lastShot = Date.now();
}
}
// Update player positions and powerups
state.players.forEach((player, index) => {
player.x += player.speedX;
player.y += player.speedY;
// Apply friction
player.speedX *= 0.9;
player.speedY *= 0.9;
// Recharge boost when not in use
if (!(keys.w && index === 0) && !(keys[upKey] && index === 1)) {
player.boost = Math.min(player.boost + (player.boostOverdrive ? 0.3 : 0.2), player.boostOverdrive ? 200 : 100);
}
// Check powerup expiration
if (player.powerup && Date.now() > player.powerupEndTime) {
if (player.powerup.cleanup) player.powerup.cleanup(player, index);
player.powerup = null;
document.getElementById(`p${index+1}-powerup`).style.display = 'none';
}
// Update powerup timer display
if (player.powerup) {
const remaining = player.powerupEndTime - Date.now();
const percent = Math.max(0, (remaining / player.powerup.duration) * 100);
document.getElementById(`p${index+1}-powerup-timer`).style.width = `${percent}%`;
document.getElementById(`p${index+1}-powerup-timer`).style.background = player.powerup.color;
}
// Boundary checks
player.x = Math.max(0, Math.min(canvas.width - player.width, player.x));
player.y = Math.max(0, Math.min(canvas.height - player.height, player.y));
});
// Update projectiles
for (let i = state.projectiles.length - 1; i >= 0; i--) {
const proj = state.projectiles[i];
proj.x += proj.speedX;
proj.y += proj.speedY;
// Check if projectile is out of bounds
if (proj.x < 0 || proj.x > canvas.width || proj.y < 0 || proj.y > canvas.height) {
state.projectiles.splice(i, 1);
continue;
}
// Check for collisions with players
for (let j = 0; j < state.players.length; j++) {
if (proj.owner !== j && checkCollision(proj, state.players[j]) && !state.players[j].invisible) {
if (!state.players[j].shield) {
state.players[j].health -= 10;
updateHealthBars();
}
state.projectiles.splice(i, 1);
// Check if player is defeated
if (state.players[j].health <= 0) {
const winner = j === 0 ? 1 : 0;
state.players[winner].score++;
endRound(winner);
}
break;
}
}
}
// Check powerup collisions
for (let i = state.powerups.length - 1; i >= 0; i--) {
const powerup = state.powerups[i];
for (let j = 0; j < state.players.length; j++) {
if (checkCollision(powerup, state.players[j])) {
applyPowerup(j, powerup.type);
state.powerups.splice(i, 1);
break;
}
}
}
}
function spawnPowerup() {
const types = Object.keys(POWERUPS);
const weights = [10, 8, 7, 6, 5, 4, 3, 2, 1, 1]; // Weighted probabilities
let totalWeight = weights.reduce((a, b) => a + b, 0);
let random = Math.random() * totalWeight;
let weightSum = 0;
let typeIndex = 0;
for (let i = 0; i < weights.length; i++) {
weightSum += weights[i];
if (random <= weightSum) {
typeIndex = i;
break;
}
}
const type = types[typeIndex];
state.powerups.push({
x: Math.random() * (canvas.width - 30),
y: Math.random() * (canvas.height - 30),
width: 30,
height: 30,
type: type,
color: POWERUPS[type].color,
flashTimer: 0
});
}
function applyPowerup(playerIndex, type) {
const player = state.players[playerIndex];
const powerup = POWERUPS[type];
// Clean up previous powerup if any
if (player.powerup && player.powerup.cleanup) {
player.powerup.cleanup(player, playerIndex);
}
// Apply new powerup
powerup.effect(player, playerIndex);
player.powerup = powerup;
player.powerupEndTime = Date.now() + powerup.duration;
// Update UI
const powerupEl = document.getElementById(`p${playerIndex+1}-powerup`);
powerupEl.style.display = 'block';
document.getElementById(`p${playerIndex+1}-powerup-name`).textContent = powerup.name;
document.getElementById(`p${playerIndex+1}-powerup-name`).style.color = powerup.color;
}
function shoot(playerIndex) {
const player = state.players[playerIndex];
const angleVariation = player.multiShot ? [0, -0.2, 0.2] : [0];
angleVariation.forEach(angle => {
state.projectiles.push({
x: player.x + (player.facing > 0 ? player.width : -10),
y: player.y + player.height/2 - 5,
width: 10,
height: 5,
speedX: 10 * player.facing * (player.timeSlow ? 0.7 : 1),
speedY: angle * 10,
color: player.color,
owner: playerIndex
});
});
}
function checkCollision(obj1, obj2) {
return obj1.x < obj2.x + obj2.width &&
obj1.x + obj1.width > obj2.x &&
obj1.y < obj2.y + obj2.height &&
obj1.y + obj1.height > obj2.y;
}
function updateHealthBars() {
document.getElementById('p1-health').style.width = `${state.players[0].health}%`;
document.getElementById('p2-health').style.width = `${state.players[1].health}%`;
document.getElementById('p1-boost').textContent = `${Math.floor(state.players[0].boost)}%`;
document.getElementById('p2-boost').textContent = `${Math.floor(state.players[1].boost)}%`;
}
function startRound() {
state.roundActive = true;
document.getElementById('round-info').textContent = `Round ${state.round}\nFIGHT!`;
setTimeout(() => {
document.getElementById('round-info').textContent = '';
}, 1000);
}
function endRound(winner) {
state.roundActive = false;
state.roundEndTime = Date.now();
document.getElementById('round-info').textContent = `Player ${winner + 1} wins!\nRound ${state.round}`;
}
function resetRound() {
state.round++;
state.players[0].x = 200;
state.players[0].y = 300;
state.players[1].x = 600;
state.players[1].y = 300;
state.players[0].health = 100;
state.players[1].health = 100;
state.players[0].boost = 100;
state.players[1].boost = 100;
state.projectiles = [];
state.powerups = [];
// Clear powerups
state.players.forEach((player, index) => {
if (player.powerup && player.powerup.cleanup) {
player.powerup.cleanup(player, index);
}
player.powerup = null;
player.color = player.originalColor;
document.getElementById(`p${index+1}-powerup`).style.display = 'none';
});
updateHealthBars();
startRound();
}
// Render everything
function render() {
// Clear canvas
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw players
state.players.forEach((player, index) => {
if (player.invisible) return;
// Draw shield if active
if (player.shield) {
ctx.beginPath();
ctx.arc(
player.x + player.width/2,
player.y + player.height/2,
player.width + 10,
0,
Math.PI * 2
);
ctx.strokeStyle = '#0ff';
ctx.lineWidth = 2;
ctx.stroke();
}
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
// Draw alien eyes
ctx.fillStyle = '#000';
ctx.fillRect(player.x + 10, player.y + 10, 5, 5);
ctx.fillRect(player.x + 25, player.y + 10, 5, 5);
// Draw boosters when active
const upKey = index === 0 ? 'w' : (state.players[1].confused ? 'ArrowDown' : 'ArrowUp');
if (keys[upKey]) {
ctx.fillStyle = player.boostOverdrive ? '#f00' : '#ff0';
ctx.fillRect(player.x + 5, player.y + player.height, 10, 10);
ctx.fillRect(player.x + 25, player.y + player.height, 10, 10);
}
});
// Draw projectiles
state.projectiles.forEach(proj => {
ctx.fillStyle = proj.color;
ctx.fillRect(proj.x, proj.y, proj.width, proj.height);
});
// Draw powerups with flashing effect
const now = Date.now();
state.powerups.forEach(powerup => {
const flashPhase = Math.sin(now * 0.005) > 0;
ctx.fillStyle = flashPhase ? powerup.color : '#fff';
ctx.fillRect(powerup.x, powerup.y, powerup.width, powerup.height);
ctx.fillStyle = '#000';
ctx.font = '10px Courier New';
ctx.textAlign = 'center';
ctx.fillText(POWERUPS[powerup.type].name, powerup.x + powerup.width/2, powerup.y + powerup.height/2 + 3);
});
// Draw score
ctx.fillStyle = '#fff';
ctx.font = '20px Courier New';
ctx.textAlign = 'center';
ctx.fillText(`${state.players[0].score} - ${state.players[1].score}`, canvas.width/2, 30);
}
// Initialize game
updateHealthBars();
gameLoop();