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
This commit is contained in:
parent
0f566b02ac
commit
25fc6fb345
8 changed files with 1064 additions and 798 deletions
127
game.js
Normal file
127
game.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/* ============================================================
|
||||
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();
|
||||
Loading…
Add table
Add a link
Reference in a new issue