// ==UserScript==
// @name Drawaria Space Invaders Game
// @namespace http://tampermonkey.net/
// @version 1.7
// @description Juego clásico Space Invaders en Drawaria.online con gráficos pixel-art personalizados, panel arrastrable y optimización de rastros mejorada.
// @author YouTubeDrawaria
// @match https://drawaria.online/*
// @grant none
// @license MIT
// @icon https://www.google.com/s2/favicons?sz=64&domain=drawaria.online
// ==/UserScript==
(function() {
'use strict';
let drawariaSocket = null;
let drawariaCanvas = null;
let drawariaCtx = null;
const commandQueue = [];
let batchProcessor = null;
const BATCH_SIZE = 8;
const BATCH_INTERVAL = 60; // ms
const originalWebSocketSend = WebSocket.prototype.send;
WebSocket.prototype.send = function(...args) {
if (!drawariaSocket && this.url && this.url.includes('drawaria')) {
drawariaSocket = this;
console.log('🔗 Drawaria WebSocket capturado para Space Invaders.');
startBatchProcessor();
}
return originalWebSocketSend.apply(this, args);
};
function startBatchProcessor() {
if (batchProcessor) return;
batchProcessor = setInterval(() => {
if (!drawariaSocket || drawariaSocket.readyState !== WebSocket.OPEN || commandQueue.length === 0) {
return;
}
const batch = commandQueue.splice(0, BATCH_SIZE);
batch.forEach(cmd => {
try {
drawariaSocket.send(cmd);
} catch (e) {
console.warn('⚠️ Fallo al enviar comando:', e);
}
});
}, BATCH_INTERVAL);
}
// Color de fondo para "borrar" rastros y el escenario espacial
const BACKGROUND_COLOR = '#000000'; // Negro
/**
* Función unificada para encolar comandos de dibujo.
* Utiliza una línea muy corta con grosor negativo para dibujar formas rellenas (puntos/círculos)
* o líneas con grosor negativo para "simular" líneas gruesas o rellenos rectangulares.
* @param {number} x1 - Coordenada X inicial
* @param {number} y1 - Coordenada Y inicial
* @param {number} x2 - Coordenada X final
* @param {number} y2 - Coordenada Y final
* @param {string} color - Color del objeto (ej. '#FFFFFF')
* @param {number} thickness - Grosor efectivo (positivo para círculo, negativo para línea gruesa/relleno)
* @param {boolean} isArc - Si es true, dibuja un círculo con x1,y1 como centro y thickness como diámetro. Si es false, dibuja una línea o un rectángulo relleno (si es una línea corta con grosor negativo).
* @param {number} startAngle - Ángulo inicial para arcos (usado solo localmente si isArc es true)
* @param {number} endAngle - Ángulo final para arcos (usado solo localmente si isArc es true)
*/
function enqueueDrawCommand(x1, y1, x2, y2, color, thickness, isArc = false, startAngle = 0, endAngle = 0) {
if (!drawariaCanvas || !drawariaSocket) return;
// Renderizado local para retroalimentación visual inmediata (útil para depuración)
if (drawariaCtx) {
drawariaCtx.fillStyle = color; // Para formas rellenas
drawariaCtx.strokeStyle = color; // Para líneas
drawariaCtx.lineWidth = Math.abs(thickness); // Usa el valor absoluto para el grosor local
drawariaCtx.lineCap = 'butt'; // Para líneas cuadradas, no redondas
drawariaCtx.lineJoin = 'miter'; // Para esquinas cuadradas
if (isArc) { // Para dibujar círculos/píxeles rellenos localmente
drawariaCtx.beginPath();
drawariaCtx.arc(x1, y1, Math.abs(thickness) / 2, startAngle, endAngle);
drawariaCtx.fill(); // Rellenar el círculo
} else { // Para líneas o rectángulos rellenos (interpretando thickness como alto/ancho)
// Si x1 == x2 y y1 == y2 (o muy cercanos), Drawaria puede interpretar como punto/círculo.
// Para garantizar un rectángulo localmente, podemos usar fillRect para el feedback.
// Sin embargo, para Drawaria, una línea muy gruesa (grosor negativo) actuará como un relleno rectangular.
// Si la "línea" es horizontal (y1=y2), actuará como un rect.width = x2-x1, rect.height = thickness
// Si la "línea" es vertical (x1=x2), actuará como un rect.width = thickness, rect.height = y2-y1
// Dado que estamos enviando una línea de un punto a otro con un grosor,
// localmente esto dibujará una línea de ese grosor.
drawariaCtx.beginPath();
drawariaCtx.moveTo(x1, y1);
drawariaCtx.lineTo(x2, y2);
drawariaCtx.stroke(); // Dibuja la línea gruesa/rectángulo
}
}
const normX1 = (x1 / drawariaCanvas.width).toFixed(4);
const normY1 = (y1 / drawariaCanvas.height).toFixed(4);
const normX2 = (x2 / drawariaCanvas.width).toFixed(4);
const normY2 = (y2 / drawariaCanvas.height).toFixed(4);
let cmd;
// Para el servidor, los círculos/píxeles se envían como puntos con grosor negativo (si isArc es true).
// Las líneas o rectángulos rellenos se envían con grosor negativo (si isArc es false).
// El cliente de Drawaria interpreta un comando de línea con grosor negativo como un rectángulo relleno.
cmd = `42["drawcmd",0,[${normX1},${normY1},${normX2},${normY2},false,${-Math.abs(thickness)},"${color}",0,0,{}]]`;
commandQueue.push(cmd);
}
// --- CONSTANTES Y FUNCIÓN AUXILIAR PARA PIXEL ART ---
// ✅ Cambio para mayor GROSOR: Aumentar el valor de PIXEL_SCALE para píxeles más gruesos y cuadrados.
const PIXEL_SCALE = 8; // Cada "píxel" en nuestras matrices y el grosor de los elementos será de 8x8 en el canvas
// Forma del invasor (11x8 píxeles)
const INVADER_PIXELS = [
[0,1,1,0,0,0,0,0,1,1,0],
[1,1,1,1,1,1,1,1,1,1,1],
[1,1,0,0,1,1,1,0,0,1,1],
[1,1,0,0,1,1,1,0,0,1,1],
[0,1,1,1,1,1,1,1,1,1,0],
[0,0,1,1,1,1,1,1,1,0,0],
[0,1,0,0,0,0,0,0,0,1,0],
[0,0,1,0,0,0,0,0,1,0,0]
];
const INVADER_WIDTH_PX = 11;
const INVADER_HEIGHT_PX = 8;
const INVADER_COLOR = '#00FF00'; // Verde
// Forma del jugador (13x8 píxeles)
const PLAYER_PIXELS = [
[0,0,0,0,0,1,1,1,0,0,0,0,0],
[0,0,0,0,1,1,1,1,1,0,0,0,0],
[0,0,0,1,1,1,1,1,1,1,0,0,0],
[0,0,1,1,1,1,1,1,1,1,1,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,0],
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1]
];
const PLAYER_WIDTH_PX = 13;
const PLAYER_HEIGHT_PX = 8;
const PLAYER_COLOR = '#00FFFF'; // Azul vibrante / Cian
// Función auxiliar para dibujar formas de pixel art
function drawPixelShape(originX, originY, pixelMap, color, pixelSize) {
for (let y = 0; y < pixelMap.length; y++) {
for (let x = 0; x < pixelMap[y].length; x++) {
if (pixelMap[y][x] === 1) {
const px = originX + x * pixelSize;
const py = originY + y * pixelSize;
// ✅ CAMBIO CLAVE: Dibuja cada "píxel" como un rectángulo relleno
// Envía una línea horizontal de longitud `pixelSize` con grosor `pixelSize`.
// Esto crea un cuadrado relleno. isArc es false.
enqueueDrawCommand(px, py + pixelSize / 2, px + pixelSize, py + pixelSize / 2, color, pixelSize, false);
}
}
}
}
// --- FIN DE LAS NUEVAS CONSTANTES Y FUNCIÓN AUXILIAR ---
class SpaceInvadersGame {
constructor() {
this.isActive = false;
// Ajustar tamaños de jugador basados en el pixel art y la escala
this.playerWidth = PLAYER_WIDTH_PX * PIXEL_SCALE;
this.playerHeight = PLAYER_HEIGHT_PX * PIXEL_SCALE;
this.playerSpeed = 6 * (PIXEL_SCALE / 6); // Ajustar velocidad para la nueva escala
this.bullets = [];
// Velocidad de bala ajustada para intervalo de 5 segundos y "más arriba"
this.bulletSpeed = 50;
this.canShoot = true;
this.shootCooldown = 300; // ms
this.invaders = [];
this.invaderRows = 1; // Solo una fila de invasores
this.invaderCols = 5; // Un total de 5 invasores
// Ajustar tamaños de invasores basados en el pixel art y la escala
this.invaderWidth = INVADER_WIDTH_PX * PIXEL_SCALE;
this.invaderHeight = INVADER_HEIGHT_PX * PIXEL_SCALE;
this.invaderGapX = 20; // Espacio entre invasores para distribuirlos mejor
this.invaderGapY = 15; // Menos relevante con una sola fila, pero se mantiene
// Reintroducir velocidad del invader y ajustar para intervalo de 5 segundos
this.invaderSpeed = 7.5;
this.invaderDirection = 1; // 1 derecha, -1 izquierda
this.invaderStepDown = 20; // Bajada más pronunciada al cambiar de dirección
this.score = 0;
this.gameInterval = null;
// Limitar la velocidad de fotogramas del juego a 5 segundos (0.2 FPS)
this.gameSpeed = 5000; // ms por frame (antes 1000)
this.isGameOver = false;
// --- NUEVAS PROPIEDADES PARA ELIMINAR RASTROS (estado del frame anterior) ---
this.prevPlayerX = 0;
this.prevPlayerY = 0;
this.prevInvaders = [];
this.prevBullets = [];
// --- FIN NUEVAS PROPIEDADES ---
this.init();
}
init() {
const checkCanvasReady = () => {
const canvas = document.getElementById('canvas');
if (canvas) {
drawariaCanvas = canvas;
drawariaCtx = canvas.getContext('2d');
// Posicionar jugador al centro abajo
this.playerX = (drawariaCanvas.width - this.playerWidth) / 2;
this.playerY = drawariaCanvas.height - this.playerHeight - 10;
// Inicializar prevPlayerX/Y con la posición inicial del jugador
this.prevPlayerX = this.playerX;
this.prevPlayerY = this.playerY;
this.createGamePanel();
this.resetGame();
console.log('✅ Space Invaders inicializado.');
} else {
setTimeout(checkCanvasReady, 100);
}
};
checkCanvasReady();
}
createGamePanel() {
const existing = document.getElementById('spaceinvaders-game-panel');
if (existing) existing.remove();
const panel = document.createElement('div');
panel.id = 'spaceinvaders-game-panel';
panel.style.cssText = `
position: fixed !important;
top: 20px !important;
right: 20px !important;
width: 250px !important;
z-index: 2147483647 !important;
background: linear-gradient(135deg, #000011, #000006) !important;
border: 2px solid #00FF00 !important;
border-radius: 12px !important;
color: #00FF00 !important;
font-family: 'Press Start 2P', 'Segoe UI', Arial, sans-serif !important;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3) !important;
padding: 15px !important;
text-align: center !important;
`;
panel.innerHTML = `
<h3 style="margin-top: 0; color: #00FF00;">🚀 Space Invaders</h3>
<div style="margin-bottom: 10px;">
Score: <span id="si-score">0</span>
</div>
<button id="si-create-space-btn" style="
width: 100%;
padding: 10px;
background: #34495e; /* Un tono oscuro de azul/gris */
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
transition: background 0.3s ease;
">🌌 Create Space</button>
<button id="si-start-pause" style="
width: 100%;
padding: 10px;
background: #00FF00;
color: black;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
transition: background 0.3s ease;
">▶️ Iniciar Juego</button>
<div id="si-game-message" style="
margin-top: 10px;
color: #FF4444;
font-weight: bold;
display: none;
"></div>
<div style="
margin-top: 15px;
font-size: 10px;
color: rgba(0,255,0,0.6);
">
Usa ← → para mover.<br> Barra espaciadora para disparar.
<br><br>
*Nota: Animación de juego cada 5 segundos para reducir comandos al servidor.*
</div>
`;
document.body.appendChild(panel);
this.makePanelDraggable(panel);
document.getElementById('si-create-space-btn').addEventListener('click', () => this.createSpaceScenario());
document.getElementById('si-start-pause').addEventListener('click', () => this.toggleGame());
document.addEventListener('keydown', e => this.handleKeyInput(e));
document.addEventListener('keyup', e => this.handleKeyUp(e));
}
makePanelDraggable(panel) {
let isDragging = false;
let currentX, currentY, initialX, initialY;
let xOffset = 0, yOffset = 0;
const dragStart = e => {
e.preventDefault();
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
isDragging = true;
panel.style.cursor = 'grabbing';
document.addEventListener("mousemove", drag);
document.addEventListener("mouseup", dragEnd);
};
const dragEnd = () => {
isDragging = false;
panel.style.cursor = 'grab';
document.removeEventListener("mousemove", drag);
document.removeEventListener("mouseup", dragEnd);
};
const drag = e => {
if (isDragging) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
panel.style.transform = `translate3d(${currentX}px, ${currentY}px, 0)`;
}
};
const header = panel.querySelector('h3');
if (header) {
header.style.cursor = 'grab';
header.addEventListener("mousedown", dragStart);
}
}
resetGame() {
this.isActive = false;
this.isGameOver = false;
this.score = 0;
this.updateScore();
this.bullets = [];
this.invaderDirection = 1;
this.createInvaders();
this.playerX = (drawariaCanvas.width - this.playerWidth) / 2;
this.playerY = drawariaCanvas.height - this.playerHeight - 10;
this.prevPlayerX = this.playerX;
this.prevPlayerY = this.playerY;
this.prevInvaders = this.invaders.map(inv => ({ ...inv }));
this.prevBullets = [];
this.lastShotTime = 0;
this.keysPressed = {};
this.clearCanvas(); // Borrado completo
this.drawAll(); // Dibuja el estado inicial
}
// Modificado para crear solo 5 invasores centrados
createInvaders() {
this.invaders = [];
const numInvaders = 5;
const totalInvadersContentWidth = numInvaders * this.invaderWidth + (numInvaders - 1) * this.invaderGapX;
const startX = (drawariaCanvas.width - totalInvadersContentWidth) / 2;
const startY = 40;
for (let i = 0; i < numInvaders; i++) {
this.invaders.push({
x: startX + i * (this.invaderWidth + this.invaderGapX),
y: startY,
width: this.invaderWidth,
height: this.invaderHeight,
alive: true
});
}
}
toggleGame() {
if (!this.isActive) {
this.startGame();
document.getElementById('si-start-pause').textContent = '⏸️ Pausar Juego';
} else {
this.pauseGame();
document.getElementById('si-start-pause').textContent = '▶️ Reanudar Juego';
}
}
startGame() {
if (this.isActive) return;
this.isActive = true;
this.isGameOver = false;
this.gameLoop();
document.getElementById('si-game-message').style.display = 'none';
document.getElementById('si-start-pause').style.background = '#00FF00';
}
pauseGame() {
this.isActive = false;
if (this.gameInterval) clearTimeout(this.gameInterval);
this.gameInterval = null;
}
endGame(message) {
this.isGameOver = true;
this.pauseGame();
const msgEl = document.getElementById('si-game-message');
msgEl.textContent = message;
msgEl.style.display = 'block';
document.getElementById('si-start-pause').textContent = '🔄 Reiniciar Juego';
document.getElementById('si-start-pause').style.background = '#cc0000';
console.log(`💀 Juego terminado: ${message}. Puntuación: ${this.score}`);
}
gameLoop() {
if (!this.isActive) return;
this.updateGame();
this.drawAll();
if (!this.isGameOver) {
this.gameInterval = setTimeout(() => this.gameLoop(), this.gameSpeed);
}
}
updateGame() {
// --- ALMACENAR ESTADO ANTERIOR PARA ELIMINACIÓN DE RASTROS ---
this.prevPlayerX = this.playerX;
this.prevPlayerY = this.playerY;
this.prevInvaders = this.invaders.map(inv => ({ ...inv }));
this.prevBullets = this.bullets.map(b => ({ ...b }));
// --- FIN ALMACENAR ESTADO ANTERIOR ---
// Mover jugador según teclas (efecto visual cada 5 segundos)
if (this.keysPressed['ArrowLeft']) {
this.playerX = Math.max(this.playerX - this.playerSpeed, 0);
}
if (this.keysPressed['ArrowRight']) {
this.playerX = Math.min(this.playerX + this.playerSpeed, drawariaCanvas.width - this.playerWidth);
}
// Actualizar balas (efecto visual cada 5 segundos, con velocidad ajustada)
this.bullets.forEach(bullet => {
bullet.y -= this.bulletSpeed;
});
this.bullets = this.bullets.filter(bullet => bullet.y + bullet.height > 0);
// Verificar colisiones bala-invader
for (let b = this.bullets.length -1; b >= 0; b--) {
const bullet = this.bullets[b];
for (let i = 0; i < this.invaders.length; i++) {
const invader = this.invaders[i];
if (invader.alive && this.rectOverlap(bullet, invader)) {
invader.alive = false;
this.bullets.splice(b,1);
this.score += 100;
this.updateScore();
break;
}
}
}
// Reintroducir lógica de movimiento para invasores ("mueva de un lado a otro solo")
let invaderHitWall = false;
for (const invader of this.invaders) {
if (!invader.alive) continue;
invader.x += this.invaderSpeed * this.invaderDirection;
if (invader.x + invader.width > drawariaCanvas.width || invader.x < 0) {
invaderHitWall = true;
}
// Condición de derrota: si un invader llega al jugador
if (invader.y + invader.height > this.playerY) {
this.endGame('¡Los invasores te alcanzaron!');
return;
}
}
if (invaderHitWall) {
this.invaderDirection *= -1; // Cambia de dirección
for (const invader of this.invaders) {
invader.y += this.invaderStepDown; // Baja un paso
// Ajustar posición si se salió por el otro lado debido al cambio de dirección
if (invader.x + invader.width > drawariaCanvas.width) {
invader.x = drawariaCanvas.width - invader.width;
} else if (invader.x < 0) {
invader.x = 0;
}
}
}
// Condición de victoria
if (this.invaders.every(i => !i.alive)) {
this.endGame('¡Ganaste!');
}
}
rectOverlap(a, b) {
return a.x < b.x + b.width &&
a.x + a.width > b.x &&
a.y < b.y + b.height &&
a.y + a.height > b.y;
}
/**
* Borra los dibujos del frame anterior dibujando sobre ellos con el color de fondo.
* El área de borrado es ligeramente más grande para asegurar una limpieza completa.
*/
erasePreviousDrawings() {
// Borrar jugador de su posición anterior
const playerErasePadding = PIXEL_SCALE * 2; // Añadir margen para borrar bien
const playerEraseWidth = this.playerWidth + playerErasePadding;
const playerEraseHeight = this.playerHeight + playerErasePadding;
const playerEraseX = this.prevPlayerX - (playerErasePadding / 2);
const playerEraseY = this.prevPlayerY - (playerErasePadding / 2);
enqueueDrawCommand(playerEraseX, playerEraseY + playerEraseHeight / 2, playerEraseX + playerEraseWidth, playerEraseY + playerEraseHeight / 2, BACKGROUND_COLOR, playerEraseHeight, false);
// Borrar invasores de sus posiciones anteriores
for (const invader of this.prevInvaders) {
// Borrar siempre, incluso si ya no está vivo, para limpiar su último rastro
const invaderErasePadding = PIXEL_SCALE * 2;
const invaderEraseWidth = invader.width + invaderErasePadding;
const invaderEraseHeight = invader.height + invaderErasePadding;
const invaderEraseX = invader.x - (invaderErasePadding / 2);
const invaderEraseY = invader.y - (invaderErasePadding / 2);
enqueueDrawCommand(invaderEraseX, invaderEraseY + invaderEraseHeight / 2, invaderEraseX + invaderEraseWidth, invaderEraseY + invaderEraseHeight / 2, BACKGROUND_COLOR, invaderEraseHeight, false);
}
// Borrar balas de sus posiciones anteriores
this.prevBullets.forEach(bullet => {
// Para balas, que son rectángulos, dibujamos un rectángulo un poco más grande de color de fondo
const bulletEraseWidth = bullet.width + PIXEL_SCALE;
const bulletEraseHeight = bullet.height + PIXEL_SCALE;
const bulletEraseX = bullet.x - (PIXEL_SCALE / 2);
const bulletEraseY = bullet.y - (PIXEL_SCALE / 2);
enqueueDrawCommand(bulletEraseX, bulletEraseY + bulletEraseHeight / 2, bulletEraseX + bulletEraseWidth, bulletEraseY + bulletEraseHeight / 2, BACKGROUND_COLOR, bulletEraseHeight, false);
});
}
/**
* Dibuja todos los elementos del juego en sus posiciones actuales.
*/
drawAll() {
// Primero, borrar los dibujos del frame anterior
// Esto se asegura de que los elementos "desaparezcan" de su posición anterior
// y luego se "redibujen" en la nueva, cada 5 segundos.
this.erasePreviousDrawings();
// Dibujar jugador con la nueva forma pixel-art en su posición actual (ahora con píxeles cuadrados)
drawPixelShape(this.playerX, this.playerY, PLAYER_PIXELS, PLAYER_COLOR, PIXEL_SCALE);
// Dibujar invasores con la nueva forma pixel-art en sus posiciones actuales (ahora con píxeles cuadrados)
for (const invader of this.invaders) {
if (!invader.alive) continue; // Solo dibujar invasores vivos
drawPixelShape(invader.x, invader.y, INVADER_PIXELS, INVADER_COLOR, PIXEL_SCALE);
}
// Dibujar balas (rectángulos blancos) en sus posiciones actuales
this.bullets.forEach(bullet => {
// ✅ CAMBIO CLAVE: Balas dibujadas como rectángulos rellenos
// Dibuja una línea horizontal de la longitud de la bala y con el grosor de la altura de la bala.
enqueueDrawCommand(bullet.x, bullet.y + bullet.height / 2, bullet.x + bullet.width, bullet.y + bullet.height / 2, '#FFFFFF', bullet.height, false);
});
}
/**
* Realiza un borrado completo de todo el canvas.
* Se usa principalmente al inicio o reinicio del juego y para el escenario espacial.
*/
clearCanvas() {
enqueueDrawCommand(0, 0, drawariaCanvas.width, drawariaCanvas.height, BACKGROUND_COLOR, Math.max(1500, 1500), false); // Usar false para borrar con rectángulo grueso
}
handleKeyInput(e) {
// Permitir que las pulsaciones de teclas se registren incluso si el juego no está activo,
// pero el movimiento visual solo se aplicará en el gameLoop cada 5 segundos.
if (e.code === 'Space') {
e.preventDefault();
// El disparo ocurre inmediatamente, pero la bala solo se mueve cada 5 segundos.
// Esto podría sentirse un poco raro, pero es la consecuencia de la restricción de 5 segundos.
this.shoot();
} else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
this.keysPressed[e.key] = true;
e.preventDefault();
}
}
handleKeyUp(e) {
if(e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
this.keysPressed[e.key] = false;
}
}
// Modificado para que las balas se ajusten al nuevo tamaño del jugador pixel art y empiecen más arriba
shoot() {
if(!this.canShoot || this.isGameOver) return;
const now = Date.now();
if (this.lastShotTime && now - this.lastShotTime < this.shootCooldown) {
return;
}
this.lastShotTime = now;
// Agrega una bala en la parte superior del jugador
// Bala inicia aún más arriba, ajustado al nuevo PIXEL_SCALE
this.bullets.push({
x: this.playerX + this.playerWidth / 2 - (PIXEL_SCALE / 2), // Centrar la bala con respecto al jugador (ajuste fino)
y: this.playerY - (PIXEL_SCALE * 7), // La bala comienza significativamente más arriba
width: PIXEL_SCALE, // Ancho base de la bala (será un cuadrado)
height: PIXEL_SCALE * 3 // Alto base de la bala (para una forma rectangular si se desea)
});
}
updateScore() {
const scoreEl = document.getElementById('si-score');
if(scoreEl) scoreEl.textContent = this.score;
}
// --- FUNCIÓN PARA EL ESCENARIO ESPACIAL ---
createSpaceScenario() {
if (!drawariaCanvas || !drawariaSocket) {
console.log('Canvas or WebSocket not available.');
return;
}
this.pauseGame(); // Pausa el juego para dibujar el escenario
// Borra todo el canvas con negro
this.clearCanvas();
// Dibuja puntos blancos para simular estrellas
// Se usa un setTimeout para dar tiempo a que el borrado completo se procese
setTimeout(() => {
const numStars = 200; // Número de estrellas
const starSize = PIXEL_SCALE / 2; // Tamaño de los puntos (más pequeño que el PIXEL_SCALE general)
for (let i = 0; i < numStars; i++) {
const x = Math.random() * drawariaCanvas.width;
const y = Math.random() * drawariaCanvas.height;
// Dibuja una pequeña estrella como un pequeño cuadrado (pixel)
enqueueDrawCommand(x, y + starSize / 2, x + starSize, y + starSize / 2, '#FFFFFF', starSize, false);
}
console.log('✅ Escenario espacial dibujado con éxito.');
}, 200); // Pequeño retraso para asegurar que el borrado se complete primero
}
// --- FIN FUNCIÓN ---
}
// Inicialización del juego
const initSpaceInvaders = () => {
new SpaceInvadersGame();
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSpaceInvaders);
} else {
setTimeout(initSpaceInvaders, 500);
}
})();