tab-shooter/game.js
Janik Dietz 25fc6fb345
feat: add fullscreen mode and extract shared game engine
- Add new fullscreen game mode playable in a dedicated browser tab
- Extract shared game logic into `engine.js` to be used by both popup and fullscreen modes
- Implement self-protection to prevent closing the game tab or popup tab
- Update documentation and bump version to 1.1.0
2026-08-04 08:02:53 +02:00

127 lines
4.2 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ============================================================
TAB SHOOTER — Vollbild-Controller (game.html)
Läuft als eigener Tab. Dieser Tab wird über seine ID
von der Zielliste ausgeschlossen -> er lässt sich NICHT abschießen.
============================================================ */
import { TabShooterGame, loadTabs } from "./engine.js";
// ---------- DOM ----------
const canvas = document.getElementById("game-canvas");
const screens = {
start: document.getElementById("start-screen"),
game: document.getElementById("game-screen"),
gameover: document.getElementById("gameover-screen"),
empty: document.getElementById("empty-screen"),
};
const els = {
hsValue: document.getElementById("hs-value"),
hudScore: document.getElementById("hud-score"),
hudLives: document.getElementById("hud-lives"),
hudTabs: document.getElementById("hud-tabs"),
hudPowerups: document.getElementById("hud-powerups"),
resultTabs: document.getElementById("result-tabs"),
resultScore: document.getElementById("result-score"),
resultHighscore: document.getElementById("result-highscore"),
newRecord: document.getElementById("new-record"),
};
// ---------- STATE ----------
let game = null;
let highscore = 0;
let selectedScope = "current";
let myTabId = null;
// Eigene Tab-ID herausfinden damit wir sie aus der Zielliste streichen können.
(async () => {
try {
const [tab] = await chrome.tabs.getCurrent();
if (tab) myTabId = tab.id;
} catch (_) {
// getCurrent() schlägt fehlt, wenn wir gar kein Tab sind (sollte nicht passieren)
}
})();
// ---------- SCREEN HELPERS ----------
function showScreen(name) {
Object.values(screens).forEach((s) => s.classList.remove("visible"));
screens[name].classList.add("visible");
}
// ---------- HIGHSCORE ----------
async function loadHighscore() {
const data = await chrome.storage.local.get("highscore");
highscore = data.highscore || 0;
els.hsValue.textContent = highscore;
}
async function saveHighscore(value) {
await chrome.storage.local.set({ highscore: value });
highscore = value;
}
// ---------- HUD CALLBACKS ----------
const hudCallbacks = {
onScore: (s) => (els.hudScore.textContent = s),
onLives: (l) => (els.hudLives.textContent = "❤️".repeat(Math.max(0, l))),
onTabs: (t) => (els.hudTabs.textContent = t),
onPowerups: (g) => {
let html = "";
if (g.shield > 0) html += "🛡️";
if (g.tripleShot > 0) html += "🔫";
if (g.rapidFire > 0) html += "⚡";
els.hudPowerups.innerHTML = html;
},
onGameOver: ({ score }) => {
const isNewRecord = score > highscore;
if (isNewRecord) saveHighscore(score);
els.resultTabs.textContent = game.tabsDestroyed;
els.resultScore.textContent = score;
els.resultHighscore.textContent = Math.max(highscore, score);
els.newRecord.classList.toggle("hidden", !isNewRecord);
showScreen("gameover");
},
};
// ---------- GAME START ----------
async function startNewGame() {
// WICHTIG: myTabId wird ausgeschlossen, damit dieser Tab kein Ziel ist.
const tabs = await loadTabs(selectedScope, myTabId);
if (tabs.length === 0) {
showScreen("empty");
return;
}
showScreen("game");
els.hudScore.textContent = "0";
els.hudLives.textContent = "❤️❤️❤️";
els.hudTabs.textContent = tabs.length;
els.hudPowerups.innerHTML = "";
game = new TabShooterGame(canvas, {}, hudCallbacks);
await game.init(tabs);
game.start();
}
// ---------- EVENT WIRING ----------
document.querySelectorAll(".scope-btn").forEach((btn) => {
btn.addEventListener("click", () => {
document.querySelectorAll(".scope-btn").forEach((b) => b.classList.remove("active"));
btn.classList.add("active");
selectedScope = btn.dataset.scope;
});
});
document.getElementById("start-btn").addEventListener("click", startNewGame);
document.getElementById("replay-btn").addEventListener("click", startNewGame);
document.getElementById("menu-btn").addEventListener("click", () => {
if (game) game.stop();
loadHighscore();
showScreen("start");
});
document.getElementById("empty-menu-btn").addEventListener("click", () => {
loadHighscore();
showScreen("start");
});
// ---------- INIT ----------
loadHighscore();