Merge pull request #19 from OlaketalAmigo/better_tetris

Merge better_tetris into TETRIS
This commit is contained in:
H3XploR
2026-03-17 21:29:03 +01:00
committed by GitHub
3 changed files with 199 additions and 52 deletions
+20 -13
View File
@@ -3,7 +3,7 @@
// ───────────────────────────────────────────── // ─────────────────────────────────────────────
const CELL = 30; const CELL = 30;
const COLORS = ['#070712','#a855f7','#f97316','#3b82f6','#06b6d4','#ef4444','#22c55e','#eab308','#555577']; const COLORS = ['#000500','#00ff41','#39ff14','#00e676','#76ff03','#b2ff59','#00ffaa','#ccff00','#2d5a2d'];
const ctxMain = document.getElementById('canvas-main').getContext('2d'); const ctxMain = document.getElementById('canvas-main').getContext('2d');
const ctxNext = document.getElementById('canvas-next').getContext('2d'); const ctxNext = document.getElementById('canvas-next').getContext('2d');
@@ -12,25 +12,32 @@ const ctxOpponent = document.getElementById('canvas-opponent').getContext('2d');
function drawCell(ctx, x, y, colorIndex, size) { function drawCell(ctx, x, y, colorIndex, size) {
const p = 1; const p = 1;
ctx.fillStyle = COLORS[colorIndex]; const color = COLORS[colorIndex];
ctx.fillStyle = color;
ctx.fillRect(x * size + p, y * size + p, size - p * 2, size - p * 2); ctx.fillRect(x * size + p, y * size + p, size - p * 2, size - p * 2);
// Highlight // Glow inner
ctx.fillStyle = 'rgba(255,255,255,0.25)'; ctx.shadowColor = color;
ctx.fillRect(x * size + p, y * size + p, size - p * 2, 3); ctx.shadowBlur = 6;
ctx.fillRect(x * size + p, y * size + p, 3, size - p * 2); ctx.fillStyle = color;
// Ombre ctx.fillRect(x * size + p + 2, y * size + p + 2, size - p * 2 - 4, size - p * 2 - 4);
ctx.fillStyle = 'rgba(0,0,0,0.35)'; ctx.shadowBlur = 0;
ctx.fillRect(x * size + p, (y + 1) * size - p - 3, size - p * 2, 3); // Highlight top/left
ctx.fillRect((x + 1) * size - p - 3, y * size + p, 3, size - p * 2); ctx.fillStyle = 'rgba(200,255,200,0.2)';
ctx.fillRect(x * size + p, y * size + p, size - p * 2, 2);
ctx.fillRect(x * size + p, y * size + p, 2, size - p * 2);
// Shadow bottom/right
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.fillRect(x * size + p, (y + 1) * size - p - 2, size - p * 2, 2);
ctx.fillRect((x + 1) * size - p - 2, y * size + p, 2, size - p * 2);
} }
function clearCanvas(ctx, w, h) { function clearCanvas(ctx, w, h) {
ctx.fillStyle = '#070712'; ctx.fillStyle = '#000500';
ctx.fillRect(0, 0, w, h); ctx.fillRect(0, 0, w, h);
} }
function drawGridLines(ctx, cols, rows, size) { function drawGridLines(ctx, cols, rows, size) {
ctx.strokeStyle = 'rgba(255,255,255,0.04)'; ctx.strokeStyle = 'rgba(0,255,65,0.06)';
ctx.lineWidth = 1; ctx.lineWidth = 1;
for (let x = 0; x <= cols; x++) { for (let x = 0; x <= cols; x++) {
ctx.beginPath(); ctx.moveTo(x * size, 0); ctx.lineTo(x * size, rows * size); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x * size, 0); ctx.lineTo(x * size, rows * size); ctx.stroke();
@@ -60,7 +67,7 @@ function drawGhost(ctx, piece, grid) {
if (ghost.y === piece.getPosition().y) return; if (ghost.y === piece.getPosition().y) return;
ctx.strokeStyle = 'rgba(255,255,255,0.15)'; ctx.strokeStyle = 'rgba(0,255,65,0.25)';
ctx.lineWidth = 1; ctx.lineWidth = 1;
for (let row = 0; row < shape.length; row++) for (let row = 0; row < shape.length; row++)
for (let col = 0; col < shape[row].length; col++) for (let col = 0; col < shape[row].length; col++)
+120 -38
View File
@@ -1,11 +1,42 @@
:root { :root {
--bg: #070712; --bg: #000500;
--panel: #0d0d1f; --panel: #000d00;
--border: #1a1a3e; --border: #004400;
--accent: #00ffe7; --accent: #00ff41;
--accent2:#ff00aa; --accent2:#39ff14;
--dim: #3a3a6a; --dim: #1a5c1a;
--text: #c0c0e0; --text: #00cc26;
}
@keyframes flicker {
0%, 89%, 91%, 93%, 95%, 100% { opacity: 1; }
90%, 92%, 94% { opacity: 0.82; }
}
@keyframes glitch-before {
0%, 100% { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); transform: translate(0); }
5% { clip-path: polygon(0 15%, 100% 15%, 100% 25%, 0 25%); transform: translate(-4px, 0); color: #ff003c; }
10% { clip-path: polygon(0 60%, 100% 60%, 100% 70%, 0 70%); transform: translate(4px, 0); color: #ff003c; }
15%, 85% { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); transform: translate(0); }
90% { clip-path: polygon(0 40%, 100% 40%, 100% 55%, 0 55%); transform: translate(-3px, 0); color: #ff003c; }
}
@keyframes glitch-after {
0%, 100% { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); transform: translate(0); }
5% { clip-path: polygon(0 70%, 100% 70%, 100% 80%, 0 80%); transform: translate(4px, 0); color: #00ffff; }
10% { clip-path: polygon(0 30%, 100% 30%, 100% 45%, 0 45%); transform: translate(-4px, 0); color: #00ffff; }
15%, 85% { clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); transform: translate(0); }
90% { clip-path: polygon(0 10%, 100% 10%, 100% 25%, 0 25%); transform: translate(3px, 0); color: #00ffff; }
}
@keyframes cursor-blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
@keyframes scan {
0% { background-position: 0 0; }
100% { background-position: 0 100%; }
} }
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
@@ -18,34 +49,83 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: flex-start;
overflow: hidden; overflow: hidden;
animation: flicker 8s infinite;
} }
#scale-container {
display: flex;
flex-direction: column;
align-items: center;
width: max-content;
position: relative;
z-index: 1;
/* transform et margin-bottom gérés par JS */
}
/* Grid lines */
body::before { body::before {
content: ''; content: '';
position: fixed; position: fixed;
inset: 0; inset: 0;
background-image: background-image:
linear-gradient(rgba(0,255,231,0.03) 1px, transparent 1px), linear-gradient(rgba(0,255,65,0.04) 1px, transparent 1px),
linear-gradient(90deg, rgba(0,255,231,0.03) 1px, transparent 1px); linear-gradient(90deg, rgba(0,255,65,0.04) 1px, transparent 1px);
background-size: 40px 40px; background-size: 40px 40px;
pointer-events: none; pointer-events: none;
z-index: 0; z-index: 0;
} }
/* Scanlines CRT */
body::after {
content: '';
position: fixed;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.12) 2px,
rgba(0, 0, 0, 0.12) 4px
);
pointer-events: none;
z-index: 9998;
}
h1 { h1 {
font-family: 'Orbitron', monospace; font-family: 'Share Tech Mono', monospace;
font-weight: 900; font-weight: 900;
font-size: 2.2rem; font-size: 2.2rem;
letter-spacing: 0.4em; letter-spacing: 0.4em;
color: var(--accent); color: var(--accent);
text-shadow: 0 0 20px var(--accent), 0 0 40px var(--accent); text-shadow: 0 0 10px var(--accent), 0 0 30px var(--accent), 0 0 60px rgba(0,255,65,0.4);
margin-bottom: 20px; margin-bottom: 20px;
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
h1::before {
content: attr(data-text);
position: absolute;
top: 0; left: 0; width: 100%;
color: var(--accent);
animation: glitch-before 6s infinite;
}
h1::after {
content: attr(data-text);
position: absolute;
top: 0; left: 0; width: 100%;
color: var(--accent);
animation: glitch-after 6s infinite;
}
.cursor {
animation: cursor-blink 1s step-end infinite;
color: var(--accent);
}
/* ── Zone de jeu globale ── */ /* ── Zone de jeu globale ── */
#game-area { #game-area {
display: flex; display: flex;
@@ -86,10 +166,10 @@ h1 {
.panel { .panel {
background: var(--panel); background: var(--panel);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 0;
padding: 14px; padding: 14px;
width: 130px; width: 130px;
box-shadow: 0 0 20px rgba(0,255,231,0.05); box-shadow: 0 0 20px rgba(0,255,65,0.07), inset 0 0 20px rgba(0,0,0,0.5);
} }
.panel-title { .panel-title {
@@ -102,11 +182,11 @@ h1 {
text-align: center; text-align: center;
} }
canvas { display: block; border-radius: 4px; } canvas { display: block; border-radius: 0; }
#canvas-main { #canvas-main {
border: 1px solid var(--border); border: 1px solid var(--accent);
box-shadow: 0 0 30px rgba(0,255,231,0.08), inset 0 0 30px rgba(0,0,0,0.5); box-shadow: 0 0 20px rgba(0,255,65,0.15), 0 0 40px rgba(0,255,65,0.06), inset 0 0 30px rgba(0,0,0,0.7);
} }
#canvas-next, #canvas-hold { #canvas-next, #canvas-hold {
@@ -117,7 +197,7 @@ canvas { display: block; border-radius: 4px; }
/* ── Canvas adversaire ── */ /* ── Canvas adversaire ── */
#canvas-opponent { #canvas-opponent {
border: 1px solid var(--accent2); border: 1px solid var(--accent2);
box-shadow: 0 0 30px rgba(255,0,170,0.08), inset 0 0 30px rgba(0,0,0,0.5); box-shadow: 0 0 20px rgba(57,255,20,0.12), inset 0 0 30px rgba(0,0,0,0.5);
} }
/* ── Score ── */ /* ── Score ── */
@@ -151,16 +231,16 @@ canvas { display: block; border-radius: 4px; }
} }
button { button {
font-family: 'Orbitron', monospace; font-family: 'Share Tech Mono', monospace;
font-size: 0.55rem; font-size: 0.6rem;
letter-spacing: 0.15em; letter-spacing: 0.12em;
font-weight: 700; font-weight: 700;
text-transform: uppercase; text-transform: uppercase;
padding: 10px 8px; padding: 10px 8px;
border: 1px solid; border: 1px solid;
border-radius: 4px; border-radius: 0;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.15s;
background: transparent; background: transparent;
width: 100%; width: 100%;
} }
@@ -222,8 +302,8 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
top: 0; left: 0; top: 0; left: 0;
width: 300px; width: 300px;
height: 600px; height: 600px;
background: rgba(7,7,18,0.88); background: rgba(0,5,0,0.9);
border-radius: 4px; border-radius: 0;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@@ -235,22 +315,23 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
#overlay-opponent.visible { display: flex; } #overlay-opponent.visible { display: flex; }
#overlay-title { #overlay-title {
font-family: 'Orbitron', monospace; font-family: 'Share Tech Mono', monospace;
font-size: 1.4rem; font-size: 1.4rem;
font-weight: 900; font-weight: 900;
letter-spacing: 0.2em; letter-spacing: 0.2em;
color: var(--accent2); color: #ff003c;
text-shadow: 0 0 20px var(--accent2); text-shadow: 0 0 20px #ff003c, 0 0 40px #ff003c;
} }
#overlay-score { #overlay-score {
font-family: 'Orbitron', monospace; font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem; font-size: 0.9rem;
color: var(--accent); color: var(--accent);
text-shadow: 0 0 10px var(--accent);
} }
#overlay-opponent-title { #overlay-opponent-title {
font-family: 'Orbitron', monospace; font-family: 'Share Tech Mono', monospace;
font-size: 1.4rem; font-size: 1.4rem;
font-weight: 900; font-weight: 900;
letter-spacing: 0.2em; letter-spacing: 0.2em;
@@ -259,7 +340,7 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
} }
#overlay-opponent-score { #overlay-opponent-score {
font-family: 'Orbitron', monospace; font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem; font-size: 0.9rem;
color: var(--accent2); color: var(--accent2);
} }
@@ -268,7 +349,7 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
#duel-panel { #duel-panel {
background: var(--panel); background: var(--panel);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 0;
padding: 12px 20px; padding: 12px 20px;
margin-bottom: 14px; margin-bottom: 14px;
position: relative; position: relative;
@@ -276,7 +357,7 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
display: flex; display: flex;
align-items: center; align-items: center;
gap: 14px; gap: 14px;
box-shadow: 0 0 20px rgba(255,0,170,0.04); box-shadow: 0 0 20px rgba(0,255,65,0.04);
} }
.duel-row { .duel-row {
@@ -332,9 +413,9 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
#settings-panel { #settings-panel {
background: var(--panel); background: var(--panel);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 6px; border-radius: 0;
padding: 14px; padding: 14px;
box-shadow: 0 0 20px rgba(0,255,231,0.05); box-shadow: 0 0 20px rgba(0,255,65,0.05);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
@@ -437,8 +518,9 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
margin: 20px auto 30px; margin: 20px auto 30px;
background: var(--panel); background: var(--panel);
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 0;
overflow: hidden; overflow: hidden;
box-shadow: 0 0 20px rgba(0,255,65,0.05);
} }
.leaderboard-tabs { .leaderboard-tabs {
@@ -464,7 +546,7 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
.lb-tab--active { .lb-tab--active {
color: var(--accent); color: var(--accent);
background: rgba(0,255,231,0.05); background: rgba(0,255,65,0.05);
border-bottom: 2px solid var(--accent); border-bottom: 2px solid var(--accent);
} }
@@ -533,4 +615,4 @@ button:disabled { opacity: 0.3; cursor: not-allowed; }
color: var(--accent2); color: var(--accent2);
} }
body { overflow-y: auto; } body { overflow: hidden; }
+59 -1
View File
@@ -9,7 +9,11 @@
</head> </head>
<body> <body>
<h1>TETRIS</h1> <canvas id="matrix-bg" style="position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:0;opacity:0.13;"></canvas>
<div id="scale-container">
<h1 data-text="TETRIS">TETRIS<span class="cursor">_</span></h1>
<!-- Bouton home --> <!-- Bouton home -->
<div id="btn-home"> <div id="btn-home">
@@ -164,6 +168,7 @@
</div> </div>
</div> </div>
</div><!-- #scale-container -->
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>
<script src="pieces.js"></script> <script src="pieces.js"></script>
@@ -171,5 +176,58 @@
<script src="renderer.js"></script> <script src="renderer.js"></script>
<script src="duel.js"></script> <script src="duel.js"></script>
<script src="ui.js"></script> <script src="ui.js"></script>
<script>
// ── Responsive scaling ──────────────────────────
(function() {
const container = document.getElementById('scale-container');
// Dimensions naturelles du contenu (single-player)
const NAT_W = 640;
const NAT_H = 1020;
function resize() {
const s = Math.min(
window.innerWidth / NAT_W,
window.innerHeight / NAT_H
);
container.style.transform = 'scale(' + s + ')';
container.style.transformOrigin = 'top center';
// Compense l'espace de layout non affecté par transform
container.style.marginBottom = ((s - 1) * NAT_H) + 'px';
}
resize();
window.addEventListener('resize', resize);
})();
</script>
<script>
// ── Matrix rain ──────────────────────────────────
(function() {
const canvas = document.getElementById('matrix-bg');
const ctx = canvas.getContext('2d');
function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }
resize();
window.addEventListener('resize', resize);
const chars = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン0123456789ABCDEF>_{}[]|\\/#@$%^&*01';
const fs = 14;
let drops = [];
function initDrops() { drops = Array(Math.floor(canvas.width / fs)).fill(1); }
initDrops();
window.addEventListener('resize', initDrops);
setInterval(function() {
ctx.fillStyle = 'rgba(0,5,0,0.05)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = fs + 'px monospace';
for (let i = 0; i < drops.length; i++) {
const ch = chars[Math.floor(Math.random() * chars.length)];
ctx.fillStyle = drops[i] * fs < 50 ? '#aaffaa' : '#00ff41';
ctx.fillText(ch, i * fs, drops[i] * fs);
if (drops[i] * fs > canvas.height && Math.random() > 0.975) drops[i] = 0;
drops[i]++;
}
}, 40);
})();
</script>
</body> </body>
</html> </html>