deepsite / index.html
Aaummaone's picture
Add 2 files
fac9d9f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NEON-TRIS | Cyberpunk Tetris</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
:root {
--neon-pink: #ff2a6d;
--neon-blue: #05d9e8;
--neon-purple: #d300c5;
--neon-green: #00ff9d;
--dark-bg: #0d0221;
--grid-bg: rgba(13, 2, 33, 0.7);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Press Start 2P', cursive;
background-color: var(--dark-bg);
color: var(--neon-blue);
overflow: hidden;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.cyber-grid {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
linear-gradient(rgba(5, 217, 232, 0.1) 1px, transparent 1px),
linear-gradient(90deg, rgba(5, 217, 232, 0.1) 1px, transparent 1px);
background-size: 30px 30px;
z-index: -1;
opacity: 0.3;
}
.cyber-lines {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
linear-gradient(135deg, var(--neon-purple) 0%, transparent 50%),
linear-gradient(-135deg, var(--neon-green) 0%, transparent 50%);
z-index: -2;
opacity: 0.2;
}
.game-container {
display: flex;
gap: 30px;
align-items: flex-start;
position: relative;
}
#game-board {
border: 5px solid var(--neon-purple);
border-radius: 3px;
box-shadow: 0 0 20px var(--neon-purple),
0 0 40px rgba(211, 0, 197, 0.3);
background-color: var(--grid-bg);
}
.side-panel {
display: flex;
flex-direction: column;
gap: 30px;
min-width: 200px;
}
.panel {
border: 3px solid var(--neon-blue);
border-radius: 3px;
padding: 15px;
box-shadow: 0 0 15px var(--neon-blue),
0 0 30px rgba(5, 217, 232, 0.3);
background-color: var(--grid-bg);
text-align: center;
}
h1 {
font-size: 24px;
margin-bottom: 15px;
color: var(--neon-pink);
text-shadow: 0 0 10px var(--neon-pink);
letter-spacing: 2px;
}
.score-display {
font-size: 22px;
margin: 15px 0;
color: var(--neon-green);
text-shadow: 0 0 8px var(--neon-green);
}
.level-display {
font-size: 18px;
margin: 10px 0;
color: var(--neon-blue);
text-shadow: 0 0 8px var(--neon-blue);
}
#next-piece {
width: 120px;
height: 120px;
margin: 0 auto;
position: relative;
}
button {
background-color: transparent;
border: 2px solid var(--neon-green);
color: var(--neon-green);
font-family: 'Press Start 2P', cursive;
padding: 10px;
margin: 5px 0;
cursor: pointer;
transition: all 0.3s;
text-shadow: 0 0 5px var(--neon-green);
}
button:hover {
background-color: var(--neon-green);
color: var(--dark-bg);
box-shadow: 0 0 15px var(--neon-green);
}
.controls {
margin-top: 20px;
}
.controls p {
font-size: 12px;
margin: 10px 0;
color: var(--neon-blue);
line-height: 1.6;
}
.neon-text {
animation: flicker 1.5s infinite alternate;
}
.game-over {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(13, 2, 33, 0.9);
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
}
.game-over h1 {
font-size: 36px;
margin-bottom: 30px;
color: var(--neon-pink);
text-shadow: 0 0 15px var(--neon-pink);
}
.scanline {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
transparent 0%,
rgba(0, 255, 157, 0.1) 50%,
transparent 100%
);
background-size: 100% 8px;
pointer-events: none;
z-index: 0;
animation: scanline 8s linear infinite;
}
@keyframes flicker {
0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% {
text-shadow: 0 0 10px var(--neon-pink),
0 0 20px var(--neon-pink),
0 0 30px var(--neon-purple),
0 0 40px rgba(255, 42, 109, 0.5);
}
20%, 24%, 55% {
text-shadow: none;
}
}
@keyframes scanline {
from {
transform: translateY(-100%);
}
to {
transform: translateY(100%);
}
}
.glitch {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2"><rect width="1" height="1" fill="%2305d9e8" opacity="0.05"/></svg>') repeat;
z-index: -1;
opacity: 0.2;
animation: glitch 10s infinite;
pointer-events: none;
mix-blend-mode: overlay;
}
@keyframes glitch {
0% { transform: translate(0); }
20% { transform: translate(-1px, 1px); }
40% { transform: translate(-1px, -1px); }
60% { transform: translate(1px, 1px); }
80% { transform: translate(1px, -1px); }
100% { transform: translate(0); }
}
/* Responsive adjustments */
@media (max-width: 768px) {
.game-container {
flex-direction: column;
align-items: center;
}
.side-panel {
min-width: 300px;
}
}
</style>
</head>
<body>
<div class="cyber-grid"></div>
<div class="cyber-lines"></div>
<div class="scanline"></div>
<div class="glitch"></div>
<div class="game-container">
<canvas id="game-board" width="300" height="600"></canvas>
<div class="side-panel">
<div class="panel">
<h1 class="neon-text">NEON-TRIS</h1>
<div class="score-display" id="score">00000</div>
<div class="level-display">LEVEL: <span id="level">1</span></div>
<div class="level-display">LINES: <span id="lines">0</span></div>
</div>
<div class="panel">
<h1>NEXT</h1>
<canvas id="next-piece" width="120" height="120"></canvas>
</div>
<div class="panel">
<button id="start-btn">NEW GAME</button>
<button id="pause-btn">PAUSE</button>
<div class="controls">
<p>← → : MOVE</p>
<p>↑ : ROTATE</p>
<p>↓ : SOFT DROP</p>
<p>SPACE : HARD DROP</p>
</div>
</div>
</div>
</div>
<div class="game-over" id="game-over">
<h1 class="neon-text">GAME OVER</h1>
<button id="restart-btn">RETRY</button>
</div>
<audio id="move-sound" src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU..."></audio>
<audio id="rotate-sound" src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0Y..."></audio>
<audio id="drop-sound" src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0Y..."></audio>
<audio id="clear-sound" src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0Y..."></audio>
<audio id="gameover-sound" src="data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0Y..."></audio>
<script>
// 8-bit sound effects as base64 encoded strings
function createSound(src) {
const audio = new Audio();
audio.src = src;
return audio;
}
document.addEventListener('DOMContentLoaded', () => {
// Game variables
const canvas = document.getElementById('game-board');
const ctx = canvas.getContext('2d');
const nextCanvas = document.getElementById('next-piece');
const nextCtx = nextCanvas.getContext('2d');
// Sound effects (simplified base64 strings - in a real app these would be full sound files)
const sounds = {
move: document.getElementById('move-sound'),
rotate: document.getElementById('rotate-sound'),
drop: document.getElementById('drop-sound'),
clear: document.getElementById('clear-sound'),
gameover: document.getElementById('gameover-sound'),
};
// For demonstration, we'll just play short beeps
function playSound(type) {
try {
sounds[type].currentTime = 0;
sounds[type].play();
} catch(e) {
console.log("Sound error:", e);
// Fallback to simple beep
const oscillator = new (window.AudioContext || window.webkitAudioContext)().createOscillator();
const gainNode = new (window.AudioContext || window.webkitAudioContext)().createGain();
oscillator.connect(gainNode);
gainNode.connect((window.AudioContext || window.webkitAudioContext)().destination);
if (type === 'move') {
oscillator.frequency.value = 200;
gainNode.gain.value = 0.1;
} else if (type === 'rotate') {
oscillator.frequency.value = 300;
gainNode.gain.value = 0.1;
} else if (type === 'drop') {
oscillator.frequency.value = 100;
gainNode.gain.value = 0.2;
} else if (type === 'clear') {
oscillator.frequency.value = 500;
gainNode.gain.value = 0.3;
} else if (type === 'gameover') {
oscillator.frequency.value = 150;
gainNode.gain.value = 0.3;
}
oscillator.start();
oscillator.stop((window.AudioContext || window.webkitAudioContext)().currentTime + 0.1);
}
}
// Game constants
const COLS = 10;
const ROWS = 20;
const BLOCK_SIZE = 30;
const COLORS = [
'rgba(255, 42, 109, 0)',
'rgba(255, 42, 109, 1)', // I
'rgba(5, 217, 232, 1)', // J
'rgba(211, 0, 197, 1)', // L
'rgba(0, 255, 157, 1)', // O
'rgba(255, 204, 0, 1)', // S
'rgba(255, 128, 0, 1)', // T
'rgba(180, 0, 255, 1)' // Z
];
const SHAPES = [
[],
[[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // I
[[2, 0, 0], [2, 2, 2], [0, 0, 0]], // J
[[0, 0, 3], [3, 3, 3], [0, 0, 0]], // L
[[0, 4, 4], [0, 4, 4], [0, 0, 0]], // O
[[0, 5, 5], [5, 5, 0], [0, 0, 0]], // S
[[0, 6, 0], [6, 6, 6], [0, 0, 0]], // T
[[7, 7, 0], [0, 7, 7], [0, 0, 0]] // Z
];
// Game state
let board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
let piece = null;
let nextPiece = null;
let score = 0;
let lines = 0;
let level = 1;
let gameOver = false;
let isPaused = false;
let dropCounter = 0;
let dropInterval = 1000;
let lastTime = 0;
// UI elements
const scoreElement = document.getElementById('score');
const levelElement = document.getElementById('level');
const linesElement = document.getElementById('lines');
const startButton = document.getElementById('start-btn');
const pauseButton = document.getElementById('pause-btn');
const restartButton = document.getElementById('restart-btn');
const gameOverScreen = document.getElementById('game-over');
// Initialize the game
function init() {
board = Array(ROWS).fill().map(() => Array(COLS).fill(0));
score = 0;
lines = 0;
level = 1;
gameOver = false;
isPaused = false;
dropInterval = 1000;
updateScore();
createNextPiece();
spawnPiece();
draw();
}
// Create a new random piece
function createNextPiece() {
const shapeId = Math.floor(Math.random() * 7) + 1;
const shape = SHAPES[shapeId];
nextPiece = {
shapeId,
shape,
pos: {x: 0, y: 0},
width: shape[0].length,
height: shape.length
};
drawNextPiece();
}
// Draw the next piece preview
function drawNextPiece() {
nextCtx.clearRect(0, 0, nextCanvas.width, nextCanvas.height);
if (!nextPiece) return;
nextCtx.fillStyle = COLORS[nextPiece.shapeId];
nextCtx.strokeStyle = '#fff';
nextCtx.lineWidth = 1;
// Center the piece in the preview
const offsetX = (nextCanvas.width - nextPiece.width * (BLOCK_SIZE / 2)) / 2;
const offsetY = (nextCanvas.height - nextPiece.height * (BLOCK_SIZE / 2)) / 2;
for (let y = 0; y < nextPiece.height; y++) {
for (let x = 0; x < nextPiece.width; x++) {
if (nextPiece.shape[y][x]) {
drawBlock(
nextCtx,
x * (BLOCK_SIZE / 2) + offsetX,
y * (BLOCK_SIZE / 2) + offsetY,
BLOCK_SIZE / 2,
COLORS[nextPiece.shapeId],
true
);
}
}
}
}
// Spawn the next piece
function spawnPiece() {
piece = {
shapeId: nextPiece.shapeId,
shape: nextPiece.shape,
pos: {
x: Math.floor(COLS / 2) - Math.floor(nextPiece.width / 2),
y: 0
},
width: nextPiece.width,
height: nextPiece.height
};
createNextPiece();
// Check if game over
if (collision()) {
gameOver = true;
playSound('gameover');
gameOverScreen.style.display = 'flex';
}
}
// Rotate the current piece
function rotatePiece() {
if (isPaused || gameOver) return;
const rotated = Array(piece.width).fill().map(() => Array(piece.height).fill(0));
for (let y = 0; y < piece.height; y++) {
for (let x = 0; x < piece.width; x++) {
rotated[x][piece.height - 1 - y] = piece.shape[y][x];
}
}
const originalShape = piece.shape;
const originalWidth = piece.width;
const originalHeight = piece.height;
piece.shape = rotated;
piece.width = rotated[0].length;
piece.height = rotated.length;
// Wall kick - adjust position if rotation causes collision
if (collision()) {
// Try moving left
piece.pos.x -= 1;
if (collision()) {
// Try moving right
piece.pos.x += 2;
if (collision()) {
// Revert rotation
piece.shape = originalShape;
piece.width = originalWidth;
piece.height = originalHeight;
return;
}
}
}
playSound('rotate');
}
// Move the current piece
function movePiece(dir) {
if (isPaused || gameOver) return;
piece.pos.x += dir;
if (collision()) {
piece.pos.x -= dir;
return false;
}
playSound('move');
return true;
}
// Hard drop the current piece
function hardDrop() {
if (isPaused || gameOver) return;
while (!collision()) {
piece.pos.y++;
}
piece.pos.y--;
dropCounter = dropInterval;
merge();
spawnPiece();
playSound('drop');
}
// Merge the current piece with the board
function merge() {
for (let y = 0; y < piece.height; y++) {
for (let x = 0; x < piece.width; x++) {
if (piece.shape[y][x]) {
board[y + piece.pos.y][x + piece.pos.x] = piece.shapeId;
}
}
}
// Check for cleared lines
clearLines();
updateScore();
}
// Clear completed lines
function clearLines() {
let linesCleared = 0;
for (let y = ROWS - 1; y >= 0; y--) {
if (board[y].every(cell => cell > 0)) {
// Remove the line
board.splice(y, 1);
// Add a new empty line at the top
board.unshift(Array(COLS).fill(0));
linesCleared++;
y++; // Check this row again since rows shift down
}
}
if (linesCleared > 0) {
lines += linesCleared;
playSound('clear');
// Level up every 10 lines
level = Math.floor(lines / 10) + 1;
dropInterval = Math.max(100, 1000 - (level - 1) * 100);
}
}
// Check if the current piece collides with the board boundaries or other pieces
function collision() {
for (let y = 0; y < piece.height; y++) {
for (let x = 0; x < piece.width; x++) {
if (piece.shape[y][x]) {
const boardX = piece.pos.x + x;
const boardY = piece.pos.y + y;
if (
boardX < 0 ||
boardX >= COLS ||
boardY >= ROWS ||
(boardY >= 0 && board[boardY][boardX])
) {
return true;
}
}
}
}
return false;
}
// Update game score and level display
function updateScore() {
// Simple scoring system
const newScore = lines * 100 * level;
scoreElement.textContent = String(newScore).padStart(5, '0');
levelElement.textContent = level;
linesElement.textContent = lines;
}
// Draw a single block with neon glow effect
function drawBlock(ctx, x, y, size, color, isGhost = false) {
const realX = x * size;
const realY = y * size;
// Inner block
if (isGhost) {
ctx.fillStyle = color.replace('1)', '0.3)');
} else {
ctx.fillStyle = color;
}
ctx.fillRect(realX, realY, size, size);
// Neon border effect
ctx.strokeStyle = isGhost ? color.replace('1)', '0.5)') : color;
ctx.lineWidth = isGhost ? 1 : 2;
ctx.strokeRect(realX, realY, size, size);
// Glow effect
if (!isGhost) {
ctx.shadowColor = color;
ctx.shadowBlur = 15;
ctx.fillRect(realX, realY, size, size);
ctx.shadowBlur = 0;
// Inner highlight
ctx.fillStyle = color.replace('1)', '0.7)');
ctx.fillRect(realX + size * 0.2, realY + size * 0.2, size * 0.6, size * 0.6);
}
}
// Draw the game board and current piece
function draw() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the board
for (let y = 0; y < ROWS; y++) {
for (let x = 0; x < COLS; x++) {
if (board[y][x]) {
drawBlock(ctx, x, y, BLOCK_SIZE, COLORS[board[y][x]]);
}
}
}
// Draw the current piece
if (piece) {
// Draw ghost piece
const ghostY = piece.pos.y;
while (!collision()) {
piece.pos.y++;
}
piece.pos.y--;
const currentY = piece.pos.y;
piece.pos.y = ghostY;
for (let y = 0; y < piece.height; y++) {
for (let x = 0; x < piece.width; x++) {
if (piece.shape[y][x]) {
drawBlock(ctx,
x + piece.pos.x,
y + currentY,
BLOCK_SIZE,
COLORS[piece.shapeId],
true
);
}
}
}
// Draw actual piece
for (let y = 0; y < piece.height; y++) {
for (let x = 0; x < piece.width; x++) {
if (piece.shape[y][x]) {
drawBlock(ctx,
x + piece.pos.x,
y + piece.pos.y,
BLOCK_SIZE,
COLORS[piece.shapeId]
);
}
}
}
}
}
// Game loop
function update(time = 0) {
if (gameOver) return;
if (isPaused) {
window.requestAnimationFrame(update);
return;
}
const deltaTime = time - lastTime;
lastTime = time;
dropCounter += deltaTime;
if (dropCounter > dropInterval) {
piece.pos.y++;
if (collision()) {
piece.pos.y--;
merge();
spawnPiece();
}
dropCounter = 0;
}
draw();
window.requestAnimationFrame(update);
}
// Event listeners
document.addEventListener('keydown', event => {
if (gameOver) return;
switch (event.key) {
case 'ArrowLeft':
movePiece(-1);
break;
case 'ArrowRight':
movePiece(1);
break;
case 'ArrowDown':
piece.pos.y++;
if (collision()) {
piece.pos.y--;
merge();
spawnPiece();
}
playSound('move');
dropCounter = 0;
break;
case 'ArrowUp':
rotatePiece();
break;
case ' ':
hardDrop();
break;
case 'p':
togglePause();
break;
}
});
function togglePause() {
if (gameOver) return;
isPaused = !isPaused;
pauseButton.textContent = isPaused ? 'RESUME' : 'PAUSE';
if (!isPaused) {
lastTime = performance.now();
update();
}
}
// Button event listeners
startButton.addEventListener('click', () => {
init();
lastTime = performance.now();
update();
});
pauseButton.addEventListener('click', togglePause);
restartButton.addEventListener('click', () => {
init();
gameOverScreen.style.display = 'none';
lastTime = performance.now();
update();
});
// Start the game
init();
draw();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
</html>