deepdive / index.html
offerpk3's picture
Update index.html
c0efcd9 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deep Sea Jumper</title>
<style>
body {
margin: 0;
padding: 0;
background: linear-gradient(180deg, #87CEEB 0%, #4682B4 50%, #191970 100%);
font-family: 'Arial', sans-serif;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
#gameContainer {
position: relative;
width: 400px;
height: 600px;
background: linear-gradient(180deg, #87CEEB 0%, #4682B4 30%, #191970 70%, #000080 100%);
border: 3px solid #2F4F4F;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 0 30px rgba(0, 100, 200, 0.5);
}
#gameCanvas {
width: 100%;
height: 100%;
display: block;
}
#ui {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 18px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
z-index: 10;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 30px;
border-radius: 15px;
text-align: center;
display: none;
z-index: 20;
}
#startButton, #restartButton {
background: linear-gradient(45deg, #FF6B6B, #FF8E8E);
color: white;
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 25px;
cursor: pointer;
margin-top: 15px;
transition: all 0.3s ease;
}
#startButton:hover, #restartButton:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(255, 107, 107, 0.4);
}
.bubble {
position: absolute;
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.3));
border-radius: 50%;
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(180deg); }
}
#instructions {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.8);
font-size: 12px;
text-align: center;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas" width="400" height="600"></canvas>
<div id="ui">
<div>Depth: <span id="depth">0</span>m</div>
<div>Score: <span id="score">0</span></div>
</div>
<div id="gameOver">
<h2>Game Over!</h2>
<p>Final Depth: <span id="finalDepth">0</span>m</p>
<p>Final Score: <span id="finalScore">0</span></p>
<button id="restartButton">Dive Again</button>
</div>
<div id="instructions">
Use ARROW KEYS or A/D to move<br>
Space to jump higher
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gameOverDiv = document.getElementById('gameOver');
// Audio context for sound effects
let audioContext;
let sounds = {};
function initAudio() {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
// Create jump sound
sounds.jump = createSound(300, 0.1, 'sine');
sounds.land = createSound(150, 0.15, 'square');
sounds.ambient = createAmbientSound();
}
function createSound(frequency, duration, type = 'sine') {
return function() {
if (!audioContext) return;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(frequency, audioContext.currentTime);
oscillator.type = type;
gainNode.gain.setValueAtTime(0.1, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + duration);
};
}
function createAmbientSound() {
return function() {
if (!audioContext) return;
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.setValueAtTime(60, audioContext.currentTime);
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.02, audioContext.currentTime);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.5);
};
}
class Game {
constructor() {
this.player = {
x: 200,
y: 500,
width: 20,
height: 20,
vx: 0,
vy: 0,
onGround: false,
color: '#FF6B6B',
lastJumpTime: 0
};
this.platforms = [];
this.camera = { y: 0 };
this.depth = 0;
this.score = 0;
this.gameRunning = false;
this.keys = {};
this.particles = [];
this.bubbles = [];
this.seaCreatures = [];
this.snake = null;
this.lastCreatureSpawn = 0;
this.gravity = 0.5;
this.jumpPower = -12;
this.moveSpeed = 5;
this.generateInitialPlatforms();
this.setupEventListeners();
}
generateInitialPlatforms() {
// Starting platform
this.platforms.push({
x: 150,
y: 550,
width: 100,
height: 15,
color: '#8FBC8F'
});
// Generate platforms going down
for (let i = 1; i < 50; i++) {
this.platforms.push({
x: Math.random() * (canvas.width - 80),
y: 550 + i * 80,
width: 80 + Math.random() * 40,
height: 12,
color: this.getPlatformColor(i * 80)
});
}
}
getPlatformColor(depth) {
if (depth < 200) return '#8FBC8F'; // Light green
if (depth < 500) return '#20B2AA'; // Light sea green
if (depth < 1000) return '#4682B4'; // Steel blue
if (depth < 1500) return '#483D8B'; // Dark slate blue
return '#2F4F4F'; // Dark slate gray
}
getSeaCreatureType(depth) {
if (depth < 200) return { type: 'fish', color: '#FFD700', size: 15 };
if (depth < 400) return { type: 'jellyfish', color: '#FF69B4', size: 20 };
if (depth < 600) return { type: 'octopus', color: '#8A2BE2', size: 25 };
if (depth < 800) return { type: 'shark', color: '#708090', size: 30 };
if (depth < 1000) return { type: 'whale', color: '#4169E1', size: 40 };
return { type: 'anglerfish', color: '#32CD32', size: 35 };
}
spawnSeaCreature() {
const creatureInfo = this.getSeaCreatureType(this.depth);
const creature = {
x: Math.random() * canvas.width,
y: this.camera.y + Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 3,
vy: (Math.random() - 0.5) * 2,
type: creatureInfo.type,
color: creatureInfo.color,
size: creatureInfo.size,
angle: Math.random() * Math.PI * 2,
life: 300 + Math.random() * 200
};
this.seaCreatures.push(creature);
}
spawnSnake() {
if (this.snake) return; // Only one snake at a time
this.snake = {
x: Math.random() * canvas.width,
y: this.player.y + 200, // Start below player
vx: 0,
vy: 0,
speed: 2.5,
size: 25,
color: '#8B0000',
segments: [],
targetX: this.player.x,
targetY: this.player.y
};
// Create snake segments
for (let i = 0; i < 8; i++) {
this.snake.segments.push({
x: this.snake.x - i * 15,
y: this.snake.y,
angle: 0
});
}
}
setupEventListeners() {
document.addEventListener('keydown', (e) => {
this.keys[e.code] = true;
if (e.code === 'Space') {
e.preventDefault();
if (!this.gameRunning) {
this.start();
}
}
});
document.addEventListener('keyup', (e) => {
this.keys[e.code] = false;
});
document.getElementById('restartButton').addEventListener('click', () => {
this.restart();
});
// Start game on first interaction
canvas.addEventListener('click', () => {
if (!audioContext) {
initAudio();
}
if (!this.gameRunning) {
this.start();
}
});
}
start() {
if (!audioContext) {
initAudio();
}
this.gameRunning = true;
gameOverDiv.style.display = 'none';
this.gameLoop();
}
restart() {
this.player.x = 200;
this.player.y = 500;
this.player.vx = 0;
this.player.vy = 0;
this.player.onGround = false;
this.player.lastJumpTime = 0;
this.camera.y = 0;
this.depth = 0;
this.score = 0;
this.particles = [];
this.bubbles = [];
this.seaCreatures = [];
this.snake = null;
this.lastCreatureSpawn = 0;
this.platforms = [];
this.generateInitialPlatforms();
this.start();
}
update() {
if (!this.gameRunning) return;
// Player movement
if (this.keys['ArrowLeft'] || this.keys['KeyA']) {
this.player.vx = -this.moveSpeed;
}
if (this.keys['ArrowRight'] || this.keys['KeyD']) {
this.player.vx = this.moveSpeed;
}
// Friction
this.player.vx *= 0.8;
// Gravity
this.player.vy += this.gravity;
// Update position
this.player.x += this.player.vx;
this.player.y += this.player.vy;
// Screen wrapping
if (this.player.x < 0) this.player.x = canvas.width;
if (this.player.x > canvas.width) this.player.x = 0;
// Platform collision
this.player.onGround = false;
for (let platform of this.platforms) {
if (this.player.x < platform.x + platform.width &&
this.player.x + this.player.width > platform.x &&
this.player.y < platform.y + platform.height &&
this.player.y + this.player.height > platform.y) {
if (this.player.vy > 0 && this.player.y < platform.y) {
this.player.y = platform.y - this.player.height;
this.player.vy = 0;
this.player.onGround = true;
// Create landing particles
this.createParticles(this.player.x + this.player.width/2, this.player.y + this.player.height);
if (sounds.land) sounds.land();
}
}
}
// Jumping
if ((this.keys['Space'] || this.keys['ArrowUp'] || this.keys['KeyW']) && this.player.onGround) {
this.player.vy = this.jumpPower;
this.player.onGround = false;
this.player.lastJumpTime = Date.now();
if (sounds.jump) sounds.jump();
// Create jump particles
this.createParticles(this.player.x + this.player.width/2, this.player.y + this.player.height);
}
// Spawn sea creatures every few seconds
if (Date.now() - this.lastCreatureSpawn > 2000 + Math.random() * 3000) {
this.spawnSeaCreature();
this.lastCreatureSpawn = Date.now();
}
// Spawn snake every 100 score points
if (this.score > 0 && this.score % 100 === 0 && !this.snake && this.score !== this.lastSnakeSpawn) {
this.spawnSnake();
this.lastSnakeSpawn = this.score;
}
// Update sea creatures
this.seaCreatures = this.seaCreatures.filter(creature => {
creature.x += creature.vx;
creature.y += creature.vy;
creature.angle += 0.05;
// Random direction changes
if (Math.random() < 0.02) {
creature.vx += (Math.random() - 0.5) * 0.5;
creature.vy += (Math.random() - 0.5) * 0.5;
}
// Keep creatures in bounds
if (creature.x < 0 || creature.x > canvas.width) creature.vx *= -1;
creature.life--;
return creature.life > 0 && creature.y > this.camera.y - 100 && creature.y < this.camera.y + canvas.height + 100;
});
// Update snake
if (this.snake) {
// Calculate distance to player
const dx = this.player.x - this.snake.x;
const dy = this.player.y - this.snake.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// Move towards player (slower than player)
this.snake.targetX = this.player.x;
this.snake.targetY = this.player.y;
const targetDx = this.snake.targetX - this.snake.x;
const targetDy = this.snake.targetY - this.snake.y;
const targetDistance = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDistance > 5) {
this.snake.vx = (targetDx / targetDistance) * this.snake.speed;
this.snake.vy = (targetDy / targetDistance) * this.snake.speed;
}
this.snake.x += this.snake.vx;
this.snake.y += this.snake.vy;
// Update snake segments
for (let i = this.snake.segments.length - 1; i > 0; i--) {
const segment = this.snake.segments[i];
const prevSegment = this.snake.segments[i - 1];
const segDx = prevSegment.x - segment.x;
const segDy = prevSegment.y - segment.y;
const segDistance = Math.sqrt(segDx * segDx + segDy * segDy);
if (segDistance > 15) {
segment.x = prevSegment.x - (segDx / segDistance) * 15;
segment.y = prevSegment.y - (segDy / segDistance) * 15;
}
segment.angle = Math.atan2(segDy, segDx);
}
// Update head segment
this.snake.segments[0].x = this.snake.x;
this.snake.segments[0].y = this.snake.y;
this.snake.segments[0].angle = Math.atan2(this.snake.vy, this.snake.vx);
// Check if player is idle too long
const timeSinceLastJump = Date.now() - this.player.lastJumpTime;
if (this.player.onGround && timeSinceLastJump > 4000) { // 4 seconds idle
// Check if snake is close enough to bite
if (distance < 40) {
this.gameOver("The sea snake caught you! Keep moving!");
return;
}
}
// Remove snake if too far from camera
if (this.snake.y < this.camera.y - 300) {
this.snake = null;
}
}
// Camera follows player
let targetCameraY = this.player.y - canvas.height/2;
this.camera.y += (targetCameraY - this.camera.y) * 0.1;
// Update depth and score
this.depth = Math.max(0, Math.floor((this.player.y - 500) / 10));
this.score = this.depth;
// Generate more platforms
if (this.player.y > this.platforms[this.platforms.length - 1].y - 500) {
for (let i = 0; i < 10; i++) {
let lastY = this.platforms[this.platforms.length - 1].y;
this.platforms.push({
x: Math.random() * (canvas.width - 80),
y: lastY + 80 + Math.random() * 40,
width: 80 + Math.random() * 40,
height: 12,
color: this.getPlatformColor(lastY + 80)
});
}
}
// Update particles
this.particles = this.particles.filter(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
particle.vy += 0.2;
particle.life--;
return particle.life > 0;
});
// Update bubbles
this.bubbles = this.bubbles.filter(bubble => {
bubble.y -= bubble.speed;
bubble.x += Math.sin(bubble.y * 0.01) * 0.5;
return bubble.y > this.camera.y - 100;
});
// Add new bubbles
if (Math.random() < 0.1) {
this.bubbles.push({
x: Math.random() * canvas.width,
y: this.camera.y + canvas.height + 20,
size: 3 + Math.random() * 8,
speed: 1 + Math.random() * 2
});
}
// Check game over
if (this.player.y > this.camera.y + canvas.height + 100) {
this.gameOver("You fell into the abyss!");
}
// Update UI
document.getElementById('depth').textContent = this.depth;
document.getElementById('score').textContent = this.score;
}
createParticles(x, y) {
for (let i = 0; i < 5; i++) {
this.particles.push({
x: x + (Math.random() - 0.5) * 20,
y: y + (Math.random() - 0.5) * 10,
vx: (Math.random() - 0.5) * 6,
vy: (Math.random() - 0.5) * 6,
life: 20 + Math.random() * 20,
color: `hsl(${180 + Math.random() * 60}, 70%, 60%)`
});
}
}
render() {
// Clear canvas with gradient background
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
let depthRatio = Math.min(this.depth / 1000, 1);
gradient.addColorStop(0, `hsl(200, 60%, ${80 - depthRatio * 30}%)`);
gradient.addColorStop(1, `hsl(220, 80%, ${20 - depthRatio * 15}%)`);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(0, -this.camera.y);
// Draw bubbles
ctx.globalAlpha = 0.6;
for (let bubble of this.bubbles) {
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.beginPath();
ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1;
// Draw platforms
for (let platform of this.platforms) {
if (platform.y > this.camera.y - 50 && platform.y < this.camera.y + canvas.height + 50) {
ctx.fillStyle = platform.color;
ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
// Platform highlight
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(platform.x, platform.y, platform.width, 3);
}
}
// Draw particles
for (let particle of this.particles) {
ctx.fillStyle = particle.color;
ctx.globalAlpha = particle.life / 40;
ctx.fillRect(particle.x, particle.y, 3, 3);
}
ctx.globalAlpha = 1;
// Draw player
ctx.fillStyle = this.player.color;
ctx.fillRect(this.player.x, this.player.y, this.player.width, this.player.height);
// Player highlight
ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
ctx.fillRect(this.player.x, this.player.y, this.player.width, 5);
ctx.restore();
}
gameOver() {
this.gameRunning = false;
document.getElementById('finalDepth').textContent = this.depth;
document.getElementById('finalScore').textContent = this.score;
gameOverDiv.style.display = 'block';
}
gameLoop() {
if (this.gameRunning) {
this.update();
this.render();
requestAnimationFrame(() => this.gameLoop());
}
}
}
// Initialize game
const game = new Game();
// Start message
ctx.fillStyle = 'white';
ctx.font = '24px Arial';
ctx.textAlign = 'center';
ctx.fillText('Deep Sea Jumper', canvas.width/2, canvas.height/2 - 50);
ctx.font = '16px Arial';
ctx.fillText('Click or Press SPACE to Start', canvas.width/2, canvas.height/2);
ctx.fillText('Dive as deep as you can!', canvas.width/2, canvas.height/2 + 30);
</script>
</body>
</html>