fix: update styles, use single font
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
</div>
|
||||
|
||||
<div id="start-screen" class="hidden">
|
||||
<div class="screen-subtitle" id="start-subtitle">Введи !play чтобы начать</div>
|
||||
<div class="screen-subtitle" id="start-subtitle">Введи !play чтобы играть</div>
|
||||
</div>
|
||||
|
||||
<div id="gameover-screen" class="hidden">
|
||||
@@ -40,7 +40,7 @@
|
||||
commands: { play: "!play" },
|
||||
texts: {
|
||||
startTitle: "SHOOTING WORD",
|
||||
startSubtitle: "Введи !play чтобы начать",
|
||||
startSubtitle: "Введи !play чтобы играть",
|
||||
gameOverTitle: "ЧАТ, ВЫ ПРОИГРАЛИ!",
|
||||
gameOverSubtitle: "Введи !play чтобы играть",
|
||||
scoreLabel: "SCORE:",
|
||||
@@ -119,6 +119,21 @@
|
||||
playerBase: "rgba(0,255,65,0.15)",
|
||||
scanline: "rgba(0,255,65,0.03)",
|
||||
},
|
||||
intro: {
|
||||
fontSize: 44,
|
||||
textYRatio: 0.35,
|
||||
subtitleYOffset: 60,
|
||||
fadeInFrames: 40,
|
||||
shot1Frame: 45,
|
||||
shot2Frame: 70,
|
||||
shot3Frame: 95,
|
||||
shotSpeed: 0.045,
|
||||
particleCountPerHit: 18,
|
||||
slideStartFrame: 122,
|
||||
slideDuration: 20,
|
||||
totalFrames: 180,
|
||||
startDelay: 300,
|
||||
},
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
@@ -598,7 +613,7 @@
|
||||
}
|
||||
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.font = 'bold 14px "Courier New", monospace';
|
||||
ctx.font = 'bold 14px "Terminator Cyr", "Courier New", monospace';
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = color;
|
||||
@@ -652,7 +667,7 @@
|
||||
this.text = wordData;
|
||||
}
|
||||
|
||||
this.y = -40;
|
||||
this.y = 0;
|
||||
this.speed = isHeavy
|
||||
? CONFIG.game.heavyEnemySpeedBase + Math.random() * CONFIG.game.heavyEnemySpeedRandom
|
||||
: CONFIG.game.simpleEnemySpeedBase +
|
||||
@@ -669,7 +684,7 @@
|
||||
const padding = CONFIG.visual.enemyPaddingX;
|
||||
|
||||
ctx.save();
|
||||
ctx.font = `${this.isHeavy ? "bold " : ""}${this.isHeavy ? CONFIG.visual.enemyHeavyFont : CONFIG.visual.enemySimpleFont}px 'Courier New', monospace`;
|
||||
ctx.font = `${this.isHeavy ? "bold " : ""}${this.isHeavy ? CONFIG.visual.enemyHeavyFont : CONFIG.visual.enemySimpleFont}px 'Terminator Cyr', 'Courier New', monospace`;
|
||||
|
||||
let maxTextWidth = 0;
|
||||
for (let word of this.words) {
|
||||
@@ -709,7 +724,7 @@
|
||||
const flash = this.flashTimer > 0;
|
||||
|
||||
ctx.save();
|
||||
ctx.font = `${this.isHeavy ? "bold " : ""}${this.isHeavy ? CONFIG.visual.enemyHeavyFont : CONFIG.visual.enemySimpleFont}px 'Courier New', monospace`;
|
||||
ctx.font = `${this.isHeavy ? "bold " : ""}${this.isHeavy ? CONFIG.visual.enemyHeavyFont : CONFIG.visual.enemySimpleFont}px 'Terminator Cyr', 'Courier New', monospace`;
|
||||
const textW = ctx.measureText(this.text).width;
|
||||
|
||||
const boxW = this.width;
|
||||
@@ -750,7 +765,7 @@
|
||||
barH,
|
||||
);
|
||||
|
||||
ctx.font = '10px "Courier New", monospace';
|
||||
ctx.font = '10px "Terminator Cyr", "Courier New", monospace';
|
||||
ctx.fillStyle = flash ? CONFIG.colors.white : CONFIG.colors.heavy;
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(
|
||||
@@ -763,7 +778,7 @@
|
||||
}
|
||||
|
||||
ctx.shadowBlur = 0;
|
||||
ctx.font = `${this.isHeavy ? "bold " : ""}${this.isHeavy ? CONFIG.visual.enemyHeavyFont : CONFIG.visual.enemySimpleFont}px 'Courier New', monospace`;
|
||||
ctx.font = `${this.isHeavy ? "bold " : ""}${this.isHeavy ? CONFIG.visual.enemyHeavyFont : CONFIG.visual.enemySimpleFont}px 'Terminator Cyr', 'Courier New', monospace`;
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
|
||||
@@ -873,7 +888,7 @@
|
||||
enemies = [];
|
||||
particles = [];
|
||||
projectiles = [];
|
||||
spawnTimer = 0;
|
||||
spawnTimer = CONFIG.game.spawnIntervalBase;
|
||||
spawnInterval = CONFIG.game.spawnIntervalBase;
|
||||
waveTimer = 0;
|
||||
InputModule.clear();
|
||||
@@ -1050,8 +1065,9 @@
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
function runIntro() {
|
||||
introPlaying = true;
|
||||
const FONT_SIZE = 44;
|
||||
const TEXT_Y = Math.round(H * 0.35);
|
||||
const I = CONFIG.intro;
|
||||
const FONT_SIZE = I.fontSize;
|
||||
const TEXT_Y = Math.round(H * I.textYRatio);
|
||||
|
||||
ctx.save();
|
||||
ctx.font = `bold ${FONT_SIZE}px "Courier New", monospace`;
|
||||
@@ -1059,11 +1075,9 @@
|
||||
const worW = ctx.measureText("SHOOTING WOR").width;
|
||||
const lW = ctx.measureText("L").width;
|
||||
const dW = ctx.measureText("D").width;
|
||||
const finalW = ctx.measureText("SHOOTING WORD").width;
|
||||
ctx.restore();
|
||||
|
||||
const cx = W / 2;
|
||||
// Center of the 'L' character in "SHOOTING WORLD"
|
||||
const lTargetX = cx - totalW / 2 + worW + lW / 2;
|
||||
const lTargetY = TEXT_Y;
|
||||
const shooterX = cx;
|
||||
@@ -1071,8 +1085,8 @@
|
||||
|
||||
let frame = 0;
|
||||
let textAlpha = 0;
|
||||
let p1 = []; // explosion particles
|
||||
let shots = []; // { progress, trail[], done }
|
||||
let p1 = [];
|
||||
let shots = [];
|
||||
|
||||
function drawPartialWorld(alpha, dSlide) {
|
||||
ctx.save();
|
||||
@@ -1080,21 +1094,18 @@
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
|
||||
// "SHOOTING WOR" - stays in place
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.fillStyle = CONFIG.colors.primary;
|
||||
ctx.shadowColor = CONFIG.colors.primaryGlow;
|
||||
ctx.shadowBlur = 25;
|
||||
ctx.fillText("SHOOTING WOR", cx - totalW / 2 + worW / 2, TEXT_Y);
|
||||
|
||||
// "L" - fades out as D slides in
|
||||
const lFade = Math.max(0, 1 - dSlide);
|
||||
if (lFade > 0) {
|
||||
ctx.globalAlpha = alpha * lFade;
|
||||
ctx.fillText("L", cx - totalW / 2 + worW + lW / 2, TEXT_Y);
|
||||
}
|
||||
|
||||
// "D" - slides left as L disappears
|
||||
const dOffset = lW * dSlide;
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.fillText("D", cx - totalW / 2 + worW + lW + dW / 2 - dOffset, TEXT_Y);
|
||||
@@ -1107,7 +1118,6 @@
|
||||
const y = shooterY + (lTargetY - shooterY) * s.progress;
|
||||
ctx.save();
|
||||
|
||||
// Trail
|
||||
for (let t of s.trail) {
|
||||
ctx.globalAlpha = t.life * 0.5;
|
||||
ctx.fillStyle = CONFIG.colors.primary;
|
||||
@@ -1116,7 +1126,6 @@
|
||||
ctx.fillRect(t.x - 2, t.y - 2, 4, 4);
|
||||
}
|
||||
|
||||
// Projectile tip
|
||||
if (!s.done) {
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.fillStyle = CONFIG.colors.primary;
|
||||
@@ -1132,20 +1141,17 @@
|
||||
function introLoop() {
|
||||
frame++;
|
||||
|
||||
// --- Phase 1: text fades in (frames 1-40) ---
|
||||
if (frame <= 40) {
|
||||
textAlpha = Math.min(1, frame / 40);
|
||||
if (frame <= I.fadeInFrames) {
|
||||
textAlpha = Math.min(1, frame / I.fadeInFrames);
|
||||
}
|
||||
|
||||
// --- Phase 2: three shots at the 'L' ---
|
||||
// each shot travels ~22 frames at 0.045 progress/frame
|
||||
if (frame === 45) shots.push({ progress: 0, trail: [], done: false });
|
||||
if (frame === 70) shots.push({ progress: 0, trail: [], done: false });
|
||||
if (frame === 95) shots.push({ progress: 0, trail: [], done: false });
|
||||
if (frame === I.shot1Frame) shots.push({ progress: 0, trail: [], done: false });
|
||||
if (frame === I.shot2Frame) shots.push({ progress: 0, trail: [], done: false });
|
||||
if (frame === I.shot3Frame) shots.push({ progress: 0, trail: [], done: false });
|
||||
|
||||
for (let s of shots) {
|
||||
if (s.done) continue;
|
||||
s.progress = Math.min(1, s.progress + 0.045);
|
||||
s.progress = Math.min(1, s.progress + I.shotSpeed);
|
||||
|
||||
const x = shooterX + (lTargetX - shooterX) * s.progress;
|
||||
const y = shooterY + (lTargetY - shooterY) * s.progress;
|
||||
@@ -1153,64 +1159,52 @@
|
||||
s.trail.push({ x, y, life: 1 });
|
||||
if (s.trail.length > 10) s.trail.shift();
|
||||
for (let t of s.trail) t.life -= 0.12;
|
||||
s.trail = s.trail.filter(t => t.life > 0);
|
||||
s.trail = s.trail.filter((t) => t.life > 0);
|
||||
|
||||
if (s.progress >= 1) {
|
||||
s.done = true;
|
||||
for (let i = 0; i < 18; i++) {
|
||||
for (let i = 0; i < I.particleCountPerHit; i++) {
|
||||
p1.push(new Particle(lTargetX, lTargetY, CONFIG.colors.primary));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Phase 3: L fades out + D slides left (frames 122-172) ---
|
||||
let dSlide = 0;
|
||||
if (frame >= 122) {
|
||||
dSlide = Math.min(1, (frame - 122) / 50);
|
||||
if (frame >= I.slideStartFrame) {
|
||||
dSlide = Math.min(1, (frame - I.slideStartFrame) / I.slideDuration);
|
||||
}
|
||||
|
||||
// Update particles
|
||||
for (let p of p1) p.update();
|
||||
p1 = p1.filter(p => p.life > 0);
|
||||
p1 = p1.filter((p) => p.life > 0);
|
||||
|
||||
// --- Draw ---
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
drawGrid();
|
||||
|
||||
// "SHOOTING WORLD" → "SHOOTING WORD" (L fades, D slides)
|
||||
drawPartialWorld(textAlpha, dSlide);
|
||||
|
||||
// Shots
|
||||
for (let s of shots) drawShotProjectile(s);
|
||||
|
||||
// Explosion particles
|
||||
for (let p of p1) p.draw();
|
||||
|
||||
// --- Next frame or transition to game ---
|
||||
if (frame < 180) {
|
||||
if (frame < I.totalFrames) {
|
||||
requestAnimationFrame(introLoop);
|
||||
} else {
|
||||
// Cleanup intro particles
|
||||
p1 = [];
|
||||
introPlaying = false;
|
||||
// Final freeze frame: "SHOOTING WORD" + subtitle on canvas
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
drawGrid();
|
||||
drawPartialWorld(1, 1);
|
||||
ctx.save();
|
||||
ctx.font = '16px "Courier New", monospace';
|
||||
ctx.font = '16px "Terminator Cyr", "Courier New", monospace';
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = CONFIG.colors.primaryDim;
|
||||
ctx.shadowColor = CONFIG.colors.primary;
|
||||
ctx.shadowBlur = 10;
|
||||
ctx.fillText(CONFIG.texts.startSubtitle, cx, TEXT_Y + 60);
|
||||
ctx.fillText(CONFIG.texts.startSubtitle, cx, TEXT_Y + I.subtitleYOffset);
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
// Small delay then launch intro
|
||||
setTimeout(introLoop, 300);
|
||||
setTimeout(introLoop, I.startDelay);
|
||||
}
|
||||
|
||||
runIntro();
|
||||
|
||||
Reference in New Issue
Block a user