import Phaser from 'phaser'; import { fontSize } from '../utils/responsive.js'; import { createFullscreenButton } from '../utils/fullscreen.js'; export default class DeepSeaHuntingScene extends Phaser.Scene { constructor() { super({ key: 'DeepSeaHuntingScene' }); } init(data) { this.inventory = data.inventory || { whaleOil: 0, fuel: 100, penguins: 0 }; this.whalesHunted = 0; this.currentWhale = null; } create() { // Dark deep ocean background this.add.rectangle(400, 300, 800, 600, 0x0a1a2a); // Create atmospheric effects this.createDeepOceanAtmosphere(); // UI elements this.createHUD(); this.createInstructions(); // Fullscreen button createFullscreenButton(this); // Spawn first whale this.spawnWhale(); // Show initial message this.showMessage('The leviathan approaches... Watch the depths!'); } createDeepOceanAtmosphere() { // Gradient darkness for (let i = 0; i < 6; i++) { const alpha = 0.05 + i * 0.03; this.add.rectangle(400, 500 - i * 80, 800, 100, 0x000000, alpha); } // Subtle waves const graphics = this.add.graphics(); graphics.lineStyle(2, 0x1a3a5a, 0.3); for (let i = 0; i < 8; i++) { const y = 80 + i * 70; graphics.beginPath(); for (let x = 0; x < 800; x += 30) { const waveY = y + Math.sin((x + i * 50) * 0.012) * 8; if (x === 0) { graphics.moveTo(x, waveY); } else { graphics.lineTo(x, waveY); } } graphics.strokePath(); } // Bioluminescent particles for (let i = 0; i < 20; i++) { const x = Math.random() * 800; const y = 100 + Math.random() * 400; const particle = this.add.circle(x, y, 2 + Math.random() * 2, 0x4a9fff, 0.5); this.tweens.add({ targets: particle, alpha: { from: 0.5, to: 0.1 }, y: y - 30, duration: 3000 + Math.random() * 2000, yoyo: true, repeat: -1, ease: 'Sine.inOut' }); } } spawnWhale() { // Get random entry and exit points on different edges const edges = ['top', 'bottom', 'left', 'right']; const entryEdge = edges[Math.floor(Math.random() * edges.length)]; // Pick a different edge for exit const exitEdges = edges.filter(e => e !== entryEdge); const exitEdge = exitEdges[Math.floor(Math.random() * exitEdges.length)]; const entry = this.getEdgePoint(entryEdge); const exit = this.getEdgePoint(exitEdge); // Calculate angle for whale rotation const angle = Phaser.Math.Angle.Between(entry.x, entry.y, exit.x, exit.y); // Create whale container const whale = this.add.container(entry.x, entry.y); // Whale body (large deep sea whale) const body = this.add.ellipse(0, 0, 180, 70, 0x1a2a3a); body.setStrokeStyle(2, 0x2a4a6a); body.disableInteractive(); // Whale underbelly const belly = this.add.ellipse(0, 10, 140, 40, 0x2a3a4a); // Tail const tail = this.add.triangle(-100, 0, 0, -35, 0, 35, -50, 0, 0x1a2a3a); // Dorsal fin const dorsalFin = this.add.triangle(20, -25, 0, 0, 30, 0, 15, -30, 0x1a2a3a); // Eye (bioluminescent glow) const eyeGlow = this.add.circle(60, -10, 12, 0x4a9fff, 0.3); const eye = this.add.circle(60, -10, 6, 0x6abfff); // Add glow effect to eye this.tweens.add({ targets: eyeGlow, alpha: { from: 0.3, to: 0.6 }, scale: { from: 1, to: 1.3 }, duration: 1000, yoyo: true, repeat: -1, ease: 'Sine.inOut' }); whale.add([tail, body, belly, dorsalFin, eyeGlow, eye]); whale.setRotation(angle); whale.setDepth(10); this.currentWhale = whale; // Subtle bobbing animation during movement this.tweens.add({ targets: whale, scaleY: { from: 1, to: 1.05 }, duration: 800, yoyo: true, repeat: -1, ease: 'Sine.inOut' }); // Create curved path from entry to exit const path = new Phaser.Curves.Path(entry.x, entry.y); // Calculate midpoint and perpendicular offset for curve const midX = (entry.x + exit.x) / 2; const midY = (entry.y + exit.y) / 2; // Create perpendicular offset for natural swimming curve const dx = exit.x - entry.x; const dy = exit.y - entry.y; const dist = Math.sqrt(dx * dx + dy * dy); // Perpendicular direction (normalized) const perpX = -dy / dist; const perpY = dx / dist; // Random curve amplitude (positive or negative for variety) const curveAmount = (Math.random() > 0.5 ? 1 : -1) * (80 + Math.random() * 120); // Control points for smooth S-curve const ctrl1X = entry.x + dx * 0.25 + perpX * curveAmount; const ctrl1Y = entry.y + dy * 0.25 + perpY * curveAmount; const ctrl2X = entry.x + dx * 0.75 - perpX * curveAmount * 0.7; const ctrl2Y = entry.y + dy * 0.75 - perpY * curveAmount * 0.7; // Add cubic bezier curve path.cubicBezierTo(exit.x, exit.y, ctrl1X, ctrl1Y, ctrl2X, ctrl2Y); // Store path progress const pathData = { t: 0 }; // Animate along curved path this.tweens.add({ targets: pathData, t: 1, duration: 15000, ease: 'Sine.inOut', onUpdate: () => { const point = path.getPoint(pathData.t); const tangent = path.getTangent(pathData.t); whale.setPosition(point.x, point.y); whale.setRotation(Math.atan2(tangent.y, tangent.x)); }, onComplete: () => { this.onWhaleExit(whale); } }); } getEdgePoint(edge) { const margin = 100; // How far off-screen to spawn const padding = 100; // Padding from corners switch (edge) { case 'top': return { x: padding + Math.random() * (800 - padding * 2), y: -margin }; case 'bottom': return { x: padding + Math.random() * (800 - padding * 2), y: 600 + margin }; case 'left': return { x: -margin, y: padding + Math.random() * (600 - padding * 2) }; case 'right': return { x: 800 + margin, y: padding + Math.random() * (600 - padding * 2) }; } } onWhaleExit(whale) { // Destroy the whale whale.destroy(); this.currentWhale = null; // Respawn after 2 seconds this.time.delayedCall(2000, () => { this.spawnWhale(); }); } createHUD() { // HUD background const hudBg = this.add.rectangle(400, 30, 780, 50, 0x000000, 0.7); hudBg.setStrokeStyle(2, 0x4a9fff); // Stats this.statsText = this.add.text(20, 15, '', { fontSize: fontSize(16), fill: '#4a9fff' }); // Return button const returnBtn = this.add.rectangle(750, 30, 80, 35, 0x1a3a5a); returnBtn.setInteractive({ useHandCursor: true }); returnBtn.setStrokeStyle(2, 0x4a9fff); const returnText = this.add.text(750, 30, 'RETURN', { fontSize: fontSize(14), fill: '#4a9fff' }).setOrigin(0.5); returnBtn.on('pointerdown', () => { this.returnToMap(); }); this.updateStats(); } updateStats() { this.statsText.setText([ `Fuel: ${this.inventory.fuel}/100`, `Oil: ${this.inventory.whaleOil}/50`, `Leviathans: ${this.whalesHunted}` ]); } createInstructions() { this.messageText = this.add.text(400, 570, '', { fontSize: fontSize(16), fill: '#4a9fff', backgroundColor: '#000000', padding: { x: 10, y: 5 } }).setOrigin(0.5); } showMessage(text) { this.messageText.setText(text); } returnToMap() { this.scene.start('MapScene', { inventory: this.inventory }); } }