Compare commits

..

No commits in common. "ed94e946e9a6f8267f4cbe4acf68271c4e7618f6" and "7b5119d2661cd4d9bf6934fa9ac34fba586aa7b3" have entirely different histories.

9 changed files with 0 additions and 802 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,802 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NEON BLOCKS: Cyberpunk Tetris</title>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
<style>
:root {
--neon-cyan: #0ff;
--neon-pink: #f0f;
--neon-yellow: #ffeb3b;
--neon-green: #0f0;
--bg-color: #050510;
}
body {
margin: 0;
background-color: var(--bg-color);
color: white;
font-family: 'Orbitron', sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
overflow: hidden;
}
#game-container {
position: relative;
border: 2px solid #333;
box-shadow: 0 0 20px var(--neon-pink), inset 0 0 50px rgba(0,0,0,1);
padding: 10px;
background-image:
linear-gradient(rgba(18, 16, 16, 1) 50%, rgba(0, 0, 0, 1) 50%),
linear-gradient(90deg, rgba(255, 0, 255, .2), rgba(0, 255, 255, .1));
background-size: 100% 4px, 4px 100%;
}
canvas {
display: block;
background-color: #0a0a14;
box-shadow: inset 0 0 30px #000;
}
.ui-panel {
position: absolute;
right: -250px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
gap: 20px;
}
.stat-box {
background: rgba(0, 20, 40, 0.8);
border-left: 4px solid var(--neon-cyan);
padding: 15px;
box-shadow: -5px 5px 15px rgba(0,255,255,0.3);
}
.stat-label {
color: #aaa;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 2px;
}
.stat-value {
font-size: 2rem;
color: var(--neon-cyan);
text-shadow: 0 0 10px var(--neon-cyan);
margin-top: 5px;
}
#level-indicator {
border-left-color: var(--neon-yellow);
}
#level-value {
color: var(--neon-yellow);
text-shadow: 0 0 10px var(--neon-yellow);
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(5, 5, 16, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
h1 {
font-size: 4rem;
margin-bottom: 20px;
color: var(--neon-pink);
text-shadow:
3px 3px 0px #0ff,
-3px -3px 0px var(--neon-yellow);
animation: glitch-skew 1s infinite linear alternate-reverse;
}
button {
background: transparent;
color: var(--neon-green);
font-family: 'Orbitron', sans-serif;
font-size: 2rem;
padding: 15px 40px;
border: 2px solid var(--neon-green);
box-shadow: 0 0 15px var(--neon-green), inset 0 0 15px var(--neon-green);
cursor: pointer;
transition: all 0.3s;
text-transform: uppercase;
}
button:hover {
background: var(--neon-green);
color: black;
box-shadow: 0 0 30px var(--neon-green), inset 0 0 30px var(--neon-green);
}
.controls-hint {
margin-top: 40px;
color: #888;
font-size: 0.9rem;
text-align: center;
line-height: 1.6;
}
/* CRT Scanline Effect */
.scanlines {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: linear-gradient(
to bottom,
rgba(255,255,255,0),
rgba(255,255,255,0) 50%,
rgba(0,0,0,0.1) 50%,
rgba(0,0,0,0.1)
);
background-size: 100% 4px;
pointer-events: none;
z-index: 99;
}
@keyframes glitch-skew {
0% { transform: skewX(0); text-shadow: 2px 2px var(--neon-pink); }
25% { transform: skewX(-5deg); text-shadow: -2px -2px var(--neon-cyan); }
50% { transform: skewX(5deg); text-shadow: 2px -2px var(--neon-yellow); }
75% { transform: skewX(-3deg); text-shadow: -2px 2px var(--neon-green); }
100% { transform: skewX(0); text-shadow: 2px 2px var(--neon-pink); }
}
/* ------------------------------------------------------------------ */
/* Neonblue pulsing & glitch background (placed behind the game) */
/* ------------------------------------------------------------------ */
.bg-effect {
position: fixed;
inset: 0; /* fill the whole viewport */
background: #001f3f; /* deep blue base */
z-index: -1; /* sit behind everything */
overflow: hidden;
}
/* Pulsing neon glow */
.bg-effect::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(circle at 50% 50%,
rgba(0, 255, 255, 0.3),
transparent 70%);
animation: pulse 4s easeinout infinite;
}
/* Glitch “slice” effect */
.bg-effect::after {
content: "";
position: absolute;
inset: 0;
background: repeating-linear-gradient(
0deg,
rgba(0,255,255,0.15) 0,
rgba(0,255,255,0.15) 2px,
transparent 2px,
transparent 4px
);
mix-blend-mode: screen;
animation: glitch 2s steps(2, end) infinite;
}
/* Pulse animation expands/brightens then contracts */
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 0.6; }
50% { transform: scale(1.05); opacity: 1; }
}
/* Glitch animation quickly shifts horizontal slices */
@keyframes glitch {
0% { transform: translateX(0); }
20% { transform: translateX(-2px); }
40% { transform: translateX(2px); }
60% { transform: translateX(-2px); }
80% { transform: translateX(2px); }
100% { transform: translateX(0); }
}
/* ------------------------------------------------------------------ */
/* LevelUp text effect */
/* ------------------------------------------------------------------ */
.level-up {
position: absolute;
inset: 0;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Orbitron', sans-serif;
font-size: 4rem;
color: var(--neon-yellow);
text-shadow: 0 0 20px var(--neon-yellow), 0 0 40px var(--neon-yellow);
pointerevents: none;
opacity: 0;
transform: scale(0.5);
pointer-events: none;
z-index: 200;
}
/* animation: shake → grow → fade */
@keyframes levelUpAnim {
0% { opacity: 0; transform: scale(0.5) translateY(0); }
10% { opacity: 1; transform: scale(0.7) translateY(-5px) rotate(-2deg); }
20% { opacity: 1; transform: scale(0.8) translateY(5px) rotate(2deg); }
30% { opacity: 1; transform: scale(0.9) translateY(-5px) rotate(-1deg); }
40% { opacity: 1; transform: scale(1) translateY(0); }
70% { opacity: 1; transform: scale(1.5) translateY(-30px); }
100% { opacity: 0; transform: scale(2) translateY(-80px); }
}
/* class added by JS to start the animation */
.level-up.active {
animation: levelUpAnim 1.5s forwards;
}
</style>
</head>
<body>
<audio id="bg-music" loop></audio>
<audio id="sfx-place" src="fx/place.mp3" preload="auto"></audio>
<audio id="sfx-explode" src="fx/explode.mp3" preload="auto"></audio>
<audio id="sfx-levelUp" src="fx/levelUp.mp3" preload="auto"></audio>
<div class="bg-effect"></div>
<div id="level-up-overlay" class="level-up">Level Up!</div>
<div class="scanlines"></div>
<div id="game-container">
<canvas id="tetris" width="300" height="600"></canvas>
<div class="ui-panel">
<div class="stat-box">
<div class="stat-label">Score</div>
<div id="score" class="stat-value">0</div>
</div>
<div class="stat-box" id="level-indicator">
<div class="stat-label">System Level</div>
<div id="level-value" class="stat-value">1</div>
</div>
<div class="stat-box">
<div class="stat-label">Lines</div>
<div id="lines" class="stat-value">0</div>
</div>
</div>
<div id="start-screen" class="overlay">
<h1>NEON BLOCKS</h1>
<button onclick="startGame()">Initialize System</button>
<div class="controls-hint">
ARROWS to Move & Rotate<br>
DOWN to Accelerate<br>
SPACE to Hard Drop
</div>
</div>
<div id="game-over-screen" class="overlay" style="display: none;">
<h1 style="color: red; text-shadow: 0 0 20px red;">SYSTEM FAILURE</h1>
<button onclick="resetGame()">Reboot System</button>
</div>
</div>
<script>
const canvas = document.getElementById('tetris');
const context = canvas.getContext('2d');
// Scale up everything by 30 for the block size
context.scale(30, 30);
// Neon Palette
const COLORS = [
null,
'#FF0D72', // T - Magenta
'#0DC2FF', // I - Cyan
'#0DFF72', // S - Green
'#F538FF', // Z - Purple
'#FF8E0D', // L - Orange
'#FFE138', // O - Yellow
'#3877FF', // J - Blue
];
const GLOW_COLORS = [
null,
'magenta',
'cyan',
'lime',
'violet',
'orange',
'yellow',
'blue',
];
// Game State
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
let score = 0;
let linesClearedTotal = 0;
let level = 1;
// Particle System Array
let particles = [];
const arena = createMatrix(10, 20);
const player = {
pos: {x: 0, y: 0},
matrix: null,
score: 0,
};
// --- PIECES GENERATORS ---
function createPiece(type) {
if (type === 'I') {
return [
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
];
} else if (type === 'L') {
return [
[0, 2, 0],
[0, 2, 0],
[0, 2, 2],
];
} else if (type === 'J') {
return [
[0, 3, 0],
[0, 3, 0],
[3, 3, 0],
];
} else if (type === 'O') {
return [
[4, 4],
[4, 4],
];
} else if (type === 'Z') {
return [
[5, 5, 0],
[0, 5, 5],
[0, 0, 0],
];
} else if (type === 'S') {
return [
[0, 6, 6],
[6, 6, 0],
[0, 0, 0],
];
} else if (type === 'T') {
return [
[0, 7, 0],
[7, 7, 7],
[0, 0, 0],
];
}
}
// ----- MUSIC PLAYLIST -------------------------------------------------
const MUSIC_PLAYLIST = [
// Add or remove URLs as you like they can be relative paths or full URLs
'music/track01.mp3',
'music/track02.mp3',
'music/track03.mp3',
'music/track04.mp3',
];
let currentTrackIndex = 0;
const musicPlayer = document.getElementById('bg-music');
// Load the first track (but dont start playing until the game begins)
function loadCurrentTrack() {
musicPlayer.src = MUSIC_PLAYLIST[currentTrackIndex];
musicPlayer.load();
}
// Advance to the next track in the playlist (wrap around)
function nextTrack() {
currentTrackIndex = (currentTrackIndex + 1) % MUSIC_PLAYLIST.length;
loadCurrentTrack();
musicPlayer.play();
}
// Call this once when the game starts
function startMusic() {
loadCurrentTrack();
musicPlayer.play();
}
// Call this when the game ends (optional mute/stop)
function stopMusic() {
musicPlayer.pause();
musicPlayer.currentTime = 0;
}
// Play the “piece placed” sound effect
function playSFX(effect) {
const sfx = document.getElementById(effect);
// Restart the sound even if its still playing
sfx.currentTime = 0;
sfx.play().catch(() => {}); // silence any autoplay errors
}
// --- CORE LOGIC ---
function createMatrix(w, h) {
const matrix = [];
while (h--) {
matrix.push(new Array(w).fill(0));
}
return matrix;
}
function collide(arena, player) {
const m = player.matrix;
const o = player.pos;
for (let y = 0; y < m.length; ++y) {
for (let x = 0; x < m[y].length; ++x) {
if (m[y][x] !== 0 &&
(arena[y + o.y] && arena[y + o.y][x + o.x]) !== 0) {
return true;
}
}
}
return false;
}
function draw() {
// Clear Canvas
context.fillStyle = '#050510';
context.fillRect(0, 0, canvas.width, canvas.height);
// Draw Arena (Locked pieces)
drawMatrix(arena, {x: 0, y: 0});
// Draw Active Piece
drawMatrix(player.matrix, player.pos);
// Draw Particles
updateAndDrawParticles();
}
function drawMatrix(matrix, offset) {
matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
// The Core Block
context.fillStyle = COLORS[value];
context.fillRect(x + offset.x, y + offset.y, 1, 1);
// The Neon Glow Effect (Inner)
context.shadowBlur = 15;
context.shadowColor = GLOW_COLORS[value];
// Bevel effect simulation
context.strokeStyle = 'rgba(255,255,255,0.5)';
}
});
});
// Reset shadow for performance/cleanliness
context.shadowBlur = 0;
}
function merge(arena, player) {
player.matrix.forEach((row, y) => {
row.forEach((value, x) => {
if (value !== 0) {
arena[y + player.pos.y][x + player.pos.x] = value;
}
});
});
}
function rotate(matrix, dir) {
for (let y = 0; y < matrix.length; ++y) {
for (let x = 0; x < y; ++x) {
[matrix[x][y], matrix[y][x]] = [matrix[y][x], matrix[x][y]];
}
}
if (dir > 0) {
matrix.forEach(row => row.reverse());
} else {
matrix.reverse();
}
}
function playerDrop() {
player.pos.y++;
if (collide(arena, player)) {
player.pos.y--;
merge(arena, player);
playSFX('sfx-place');
playerReset();
arenaSweep();
updateScore();
}
dropCounter = 0;
}
function playerMove(offset) {
player.pos.x += offset;
if (collide(arena, player)) {
player.pos.x -= offset;
}
}
function playerReset() {
const pieces = 'ILJOTSZ';
player.matrix = createPiece(pieces[pieces.length * Math.random() | 0]);
player.pos.y = 0;
player.pos.x = (arena[0].length / 2 | 0) - (player.matrix[0].length / 2 | 0);
if (collide(arena, player)) {
gameOver();
}
}
function playerRotate(dir) {
const pos = player.pos.x;
let offset = 1;
rotate(player.matrix, dir);
while (collide(arena, player)) {
player.pos.x += offset;
offset = -(offset + (offset > 0 ? 1 : -1));
if (offset > player.matrix[0].length) {
rotate(player.matrix, -dir);
player.pos.x = pos;
return;
}
}
}
// --- GAME MECHANICS ---
function arenaSweep() {
let rowCount = 0;
outer: for (let y = arena.length - 1; y > 0; --y) {
for (let x = 0; x < arena[y].length; ++x) {
if (arena[y][x] === 0) {
continue outer;
}
}
// ROW CLEARED
const row = arena.splice(y, 1)[0];
// Create Explosion Effect for the cleared row
createExplosion(y, row);
playSFX('sfx-explode');
arena.unshift(row.fill(0));
++y;
rowCount++;
}
if (rowCount > 0) {
// Scoring: 100, 300, 500, 800
const points = [100, 300, 500, 800];
score += points[rowCount - 1] * level;
linesClearedTotal += rowCount;
// Level Up Logic: Every 8 rows
if (linesClearedTotal % 6 === 0) {
levelUp();
}
}
}
function levelUp() {
level++;
// Increase Speed (Decrease interval)
// Cap at minimum 100ms
dropInterval = Math.max(100, 1000 - (level * 120));
// Visual feedback on UI
document.getElementById('level-value').innerText = level;
// Flash border effect
const container = document.getElementById('game-container');
container.style.borderColor = '#fff';
setTimeout(() => {
container.style.borderColor = '#333';
}, 200);
playSFX('sfx-levelUp');
// *** advance to next music track ***
nextTrack();
// ----- NEW: show “Level Up!” animation -----
const overlay = document.getElementById('level-up-overlay');
overlay.classList.remove('active'); // reset (in case its still there)
// Force reflow so the removal takes effect
void overlay.offsetWidth;
overlay.classList.add('active');
// Remove the class after the animation finishes (1.5s)
setTimeout(() => overlay.classList.remove('active'), 1500);
}
function updateScore() {
document.getElementById('score').innerText = score;
document.getElementById('lines').innerText = linesClearedTotal;
}
// --- PARTICLE SYSTEM (The Cool Stuff) ---
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.vx = (Math.random() - 0.5) * 1; // Velocity X
this.vy = (Math.random() - 0.5) * 1 - 0.5; // Velocity Y (upward bias)
this.life = 1.0; // Opacity/Life
this.color = color;
this.size = Math.random() * 0.8 + 0.2;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.05; // Gravity
this.life -= 0.02; // Fade out
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.life;
ctx.fillStyle = this.color;
// Glow for particles
ctx.shadowBlur = 10;
ctx.shadowColor = this.color;
// Draw Particle (Square or Circle)
if (Math.random() > 0.5) {
ctx.fillRect(this.x, this.y, this.size, this.size);
} else {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size/2, 0, Math.PI * 2);
ctx.fill();
}
// Draw "Tech" line fragments
if (this.life > 0.5) {
context.strokeStyle = '#fff';
context.lineWidth = 0.1;
context.beginPath();
context.moveTo(this.x, this.y);
context.lineTo(this.x + (Math.random()-0.5), this.y + (Math.random()-0.5));
context.stroke();
}
ctx.restore();
}
}
function createExplosion(rowY, rowData) {
// Iterate through the row that was cleared
for (let x = 0; x < rowData.length; x++) {
// Create multiple particles per block
for (let i = 0; i < 5; i++) {
// Convert grid coordinates to pixels (approximate)
const colorIndex = rowData[x];
if(colorIndex !== 0) {
const color = COLORS[colorIndex];
particles.push(new Particle(x + Math.random()*0.5, rowY + Math.random()*0.5, color));
}
}
}
// Add some white sparks
for(let i=0; i<10; i++) {
particles.push(new Particle(Math.random() * 10, rowY + 0.5, '#ffffff'));
}
}
function updateAndDrawParticles() {
// Iterate backwards to remove dead particles safely
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.update();
p.draw(context);
if (p.life <= 0) {
particles.splice(i, 1);
}
}
}
// --- GAME LOOP ---
function update(time = 0) {
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
playerDrop();
}
draw();
requestAnimationFrame(update);
}
// --- CONTROLS & STATE MANAGEMENT ---
document.addEventListener('keydown', event => {
if(event.keyCode === 37) { // Left
playerMove(-1);
} else if (event.keyCode === 39) { // Right
playerMove(1);
} else if (event.keyCode === 40) { // Down
playerDrop();
} else if (event.keyCode === 81) { // Q (Rotate Left - optional)
playerRotate(-1);
} else if (event.keyCode === 87 || event.keyCode === 38) { // W or Up (Rotate Right)
playerRotate(1);
} else if (event.keyCode === 32) { // Space (Hard Drop - instant to bottom)
while (!collide(arena, player)) {
player.pos.y++;
}
player.pos.y--;
merge(arena, player);
playerReset();
arenaSweep();
updateScore();
dropCounter = 0;
}
});
function startGame() {
document.getElementById('start-screen').style.display = 'none';
document.getElementById('game-over-screen').style.display = 'none';
// Reset Game State
arena.forEach(row => row.fill(0));
score = 0;
linesClearedTotal = 0;
level = 1;
dropInterval = 1000;
particles = [];
updateScore();
playerReset();
startMusic();
update();
}
function gameOver() {
document.getElementById('game-over-screen').style.display = 'flex';
cancelAnimationFrame(update);
}
function resetGame() {
startGame();
}
</script>
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.