diff --git a/game.html b/game.html
index 92348fb..d39275a 100644
--- a/game.html
+++ b/game.html
@@ -28,6 +28,7 @@
ЧАТ, ВЫ ПРОИГРАЛИ!
Final Score: 0
+
Введи !play чтобы играть
@@ -448,7 +449,7 @@
const InputModule = {
currentLine: "",
- submitLine(text) {
+ submitLine(text, username) {
const line = text.trim().toLowerCase();
if (!line) return;
if (introPlaying) return;
@@ -468,10 +469,10 @@
const enemy = this.findEnemy(line);
// АНИМАЦИЯ: слово вылетает из поля ввода, потом запускается снаряд
- this.animateShoot(line, enemy);
+ this.animateShoot(line, enemy, username);
},
- animateShoot(line, enemy) {
+ animateShoot(line, enemy, username) {
const el = document.getElementById("typed-text");
// Добавляем CSS-класс для анимации вылета
@@ -484,9 +485,9 @@
this.updateDisplay("");
if (enemy) {
- game.launchProjectile(line, enemy, true);
+ game.launchProjectile(line, enemy, true, username);
} else {
- game.launchProjectile(line, null, false);
+ game.launchProjectile(line, null, false, username);
}
}, S.inputShootDelay);
},
@@ -656,9 +657,11 @@
let gameLoopStarted = false;
let introPlaying = false;
+ let playerStats = {};
+
const game = {
- launchProjectile(word, targetEnemy, isHit) {
- projectiles.push(new Projectile(word, targetEnemy, isHit));
+ launchProjectile(word, targetEnemy, isHit, username) {
+ projectiles.push(new Projectile(word, targetEnemy, isHit, username));
},
onProjectileHit(projectile, enemy) {
@@ -667,6 +670,10 @@
if (dead) {
score += enemy.isHeavy ? CONFIG.game.scoreHeavy : CONFIG.game.scoreSimple;
document.getElementById("score").textContent = score;
+ if (projectile.username) {
+ if (!playerStats[projectile.username]) playerStats[projectile.username] = { kills: 0, misses: 0 };
+ playerStats[projectile.username].kills++;
+ }
this.destroyEnemy(enemy);
} else {
spawnParticles(enemy.x, enemy.y, CONFIG.colors.heavy, S.particleArmorBreak);
@@ -677,6 +684,10 @@
onProjectileMiss(projectile) {
spawnParticles(projectile.targetX, projectile.targetY, CONFIG.colors.miss, S.particleMiss);
triggerShake(S.shakeMiss, S.shakeMissDuration);
+ if (projectile.username) {
+ if (!playerStats[projectile.username]) playerStats[projectile.username] = { kills: 0, misses: 0 };
+ playerStats[projectile.username].misses++;
+ }
},
destroyEnemy(enemy) {
@@ -696,9 +707,10 @@
// PROJECTILE — вылетает из поля ввода (низ экрана, центр)
// ═══════════════════════════════════════════════════════════════
class Projectile {
- constructor(word, targetEnemy, isHit) {
+ constructor(word, targetEnemy, isHit, username) {
this.word = word;
this.isHit = isHit;
+ this.username = username;
this.x = W / 2;
this.y = H - S.shooterOffset;
this.speed = S.projectileSpeed;
@@ -1040,9 +1052,50 @@
let gameOverTimeout = null;
+ function renderPlayerStats() {
+ const container = document.getElementById("player-stats");
+ container.innerHTML = "";
+
+ const names = Object.keys(playerStats);
+ if (names.length === 0) return;
+
+ const byKills = names.slice().sort((a, b) => playerStats[b].kills - playerStats[a].kills);
+ const top3 = byKills.slice(0, 3);
+
+ const sectionTitle = document.createElement("div");
+ sectionTitle.className = "stats-section-title";
+ sectionTitle.textContent = "УБИЙЦЫ";
+ container.appendChild(sectionTitle);
+
+ top3.forEach((name, i) => {
+ const row = document.createElement("div");
+ row.className = "stats-row" + (i === 0 ? " stats-top1" : i === 1 ? " stats-top2" : " stats-top3");
+ const place = i === 0 ? "1" : i === 1 ? "2" : "3";
+ row.innerHTML = `#${place} ${name} ${playerStats[name].kills}`;
+ container.appendChild(row);
+ });
+
+ let maxMisses = 0;
+ let mazilaName = "";
+ for (const name of names) {
+ if (playerStats[name].misses > maxMisses) {
+ maxMisses = playerStats[name].misses;
+ mazilaName = name;
+ }
+ }
+
+ if (mazilaName && maxMisses > 0) {
+ const mazila = document.createElement("div");
+ mazila.className = "stats-mazila";
+ mazila.innerHTML = `Мазила: ${mazilaName} (${maxMisses})`;
+ container.appendChild(mazila);
+ }
+ }
+
function gameOver() {
gameState = "gameover";
document.getElementById("final-score").textContent = score;
+ renderPlayerStats();
document.getElementById("gameover-screen").classList.remove("hidden");
document.getElementById("gameover-subtitle").classList.add("hidden");
@@ -1070,6 +1123,7 @@
enemies = [];
particles = [];
projectiles = [];
+ playerStats = {};
spawnTimer = CONFIG.game.spawnIntervalBase;
spawnInterval = CONFIG.game.spawnIntervalBase;
waveTimer = 0;
@@ -1143,7 +1197,8 @@
client.connect();
client.on("message", (channel, tags, message, self) => {
- InputModule.submitLine(message);
+ const username = tags["display-name"] || tags.username || "anonymous";
+ InputModule.submitLine(message, username);
});
function update() {
diff --git a/style.css b/style.css
index a805614..eb78492 100644
--- a/style.css
+++ b/style.css
@@ -111,6 +111,70 @@ body {
margin-bottom: 30px;
opacity: 0.8;
}
+#player-stats {
+ margin-bottom: 20px;
+ width: 80%;
+ max-width: 400px;
+}
+.stats-section-title {
+ color: #00ff41;
+ font-size: 16px;
+ letter-spacing: 3px;
+ margin-bottom: 10px;
+ text-shadow: 0 0 10px #00ff41;
+}
+.stats-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 4px 12px;
+ margin-bottom: 4px;
+ border-left: 2px solid #00ff41;
+ opacity: 0.8;
+}
+.stats-top1 {
+ font-size: 18px;
+ font-weight: bold;
+ color: #00ff41;
+ border-left-width: 4px;
+ opacity: 1;
+ text-shadow: 0 0 12px #00ff41;
+}
+.stats-top2 {
+ font-size: 15px;
+ font-weight: 600;
+ color: #00dd33;
+ opacity: 0.9;
+}
+.stats-top3 {
+ font-size: 13px;
+ color: #00bb22;
+}
+.stats-place {
+ min-width: 28px;
+}
+.stats-name {
+ flex: 1;
+ text-align: left;
+ margin-left: 8px;
+}
+.stats-kills {
+ min-width: 24px;
+ text-align: right;
+}
+.stats-mazila {
+ margin-top: 14px;
+ font-size: 14px;
+ color: #ff6644;
+ opacity: 0.85;
+}
+.mazila-title {
+ font-weight: bold;
+ text-shadow: 0 0 8px #ff6644;
+}
+.mazila-count {
+ opacity: 0.6;
+}
.hidden {
display: none !important;
}