/** * WaterPhysicsSystem.js * * Handles player movement in water with realistic physics: * - Movement drag (30% slower) * - Buoyancy (upward drift) * - Jump reduction * - Hair floating effect (integrates with WindFoliageSystem) * - Swimming animation trigger * * Style 32 Dark-Chibi Noir compatible */ class WaterPhysicsSystem { constructor(scene) { this.scene = scene; // Physics constants this.waterDragFactor = 0.7; // 30% slower movement this.waterJumpReduction = 0.5; // 50% jump power this.buoyancyForce = -15; // Upward drift (pixels/s) this.hairFloatStrength = 1.5; // Hair rises more in water // Water zones (areas with water) this.waterZones = []; // Player state this.playerInWater = false; this.waterDepth = 0; console.log('🌊 WaterPhysicsSystem: Initialized'); } /** * Add water zone * @param {number} x - X position * @param {number} y - Y position * @param {number} width - Zone width * @param {number} height - Zone height * @param {number} depth - Water depth (0-100) */ addWaterZone(x, y, width, height, depth = 50) { const zone = this.scene.add.rectangle(x, y, width, height, 0x4682B4, 0.3); zone.setOrigin(0, 0); zone.setDepth(-10); this.waterZones.push({ zone, x, y, width, height, depth }); console.log(`🌊 Water zone added at (${x}, ${y}) - ${width}x${height}, depth: ${depth}`); } /** * Check if player is in water * @param {Object} player - Player object with x, y position * @returns {number} Water depth (0 = no water, 100 = deep) */ checkWaterDepth(player) { if (!player) return 0; for (const waterData of this.waterZones) { const { x, y, width, height, depth } = waterData; // Check if player is inside water zone if (player.x >= x && player.x <= x + width && player.y >= y && player.y <= y + height) { return depth; } } return 0; } /** * Apply water physics to player * @param {Object} player - Player object * @param {number} delta - Time delta */ applyWaterPhysics(player, delta) { if (!player || !player.body) return; // Check water depth const depth = this.checkWaterDepth(player); const wasInWater = this.playerInWater; this.playerInWater = depth > 0; this.waterDepth = depth; if (this.playerInWater) { // Apply movement drag player.body.velocity.x *= this.waterDragFactor; player.body.velocity.y *= this.waterDragFactor; // Apply buoyancy (slow upward drift) player.body.velocity.y += this.buoyancyForce * (delta / 1000); // Reduce jump power if jumping in water if (player.isJumping && player.body.velocity.y < 0) { player.body.velocity.y *= this.waterJumpReduction; } // Trigger swimming animation if (player.sprite && player.sprite.anims) { const currentAnim = player.sprite.anims.currentAnim; if (!currentAnim || currentAnim.key !== 'swim') { // player.sprite.play('swim'); // Uncomment when swim animation exists } } // Float hair upward (if WindFoliageSystem exists) if (this.scene.weather && this.scene.weather.windSystem) { this.floatHair(player, true); } // Entry splash effect (once) if (!wasInWater) { this.createSplash(player.x, player.y); } } else { // Player left water if (wasInWater) { // Reset hair physics if (this.scene.weather && this.scene.weather.windSystem) { this.floatHair(player, false); } } } } /** * Make hair float upward in water * @param {Object} player - Player object * @param {boolean} float - Enable/disable floating */ floatHair(player, float) { if (!player.hairLayer) return; const windSystem = this.scene.weather.windSystem; windSystem.windAffectedLayers.forEach(layer => { if (layer.sprite === player.hairLayer) { layer.buoyantMode = float; layer.floatStrength = float ? this.hairFloatStrength : 0; } }); } /** * Create splash effect when entering water * @param {number} x - X position * @param {number} y - Y position */ createSplash(x, y) { // Create ripples if WaterRipplesSystem exists if (this.scene.waterRipples) { this.scene.waterRipples.createSplashRipples(x, y); } // Splash particles (simple white particles) const splash = this.scene.add.particles(x, y, 'particle_white', { lifespan: 500, speed: { min: 50, max: 150 }, angle: { min: 240, max: 300 }, scale: { start: 0.5, end: 0.1 }, alpha: { start: 0.8, end: 0 }, quantity: 10, frequency: -1 }); splash.explode(); // Auto destroy this.scene.time.delayedCall(500, () => { splash.destroy(); }); } /** * Get player state info * @returns {Object} State info */ getPlayerState() { return { inWater: this.playerInWater, depth: this.waterDepth, dragFactor: this.playerInWater ? this.waterDragFactor : 1.0, buoyancy: this.playerInWater ? this.buoyancyForce : 0 }; } /** * Cleanup */ destroy() { this.waterZones.forEach(data => { if (data.zone) data.zone.destroy(); }); this.waterZones = []; } }