FAZA 17: 2.5D Minecraft-Style Terrain + Y-Layer Stacking + Custom Sprites

COMPLETED FEATURES:

 Custom Sprite Integration:
- Player, Zombie, Merchant sprites (0.2 scale)
- 11 custom sprites + 5 asset packs loaded
- Auto-transparency processing (white/brown removal)
- Gravestone system with atlas extraction

 2.5D Minecraft-Style Terrain:
- Volumetric blocks with 25px thickness
- Strong left/right side shading (30%/50% darker)
- Minecraft-style texture patterns (grass, dirt, stone)
- Crisp black outlines for definition

 Y-Layer Stacking System:
- GRASS_FULL: All green (elevation > 0.7)
- GRASS_TOP: Green top + brown sides (elevation 0.4-0.7)
- DIRT: All brown (elevation < 0.4)
- Dynamic terrain depth based on height

 Floating Island World Edge:
- Stone cliff walls at map borders
- 2-tile transition zone
- Elevation flattening for cliff drop-off effect
- 100x100 world with defined boundaries

 Performance & Polish:
- Canvas renderer for pixel-perfect sharpness
- CSS image-rendering: crisp-edges
- willReadFrequently optimization
- No Canvas2D warnings

 Technical:
- 3D volumetric trees and rocks
- Hybrid rendering (2.5D terrain + 2D characters)
- Procedural texture generation
- Y-layer aware terrain type selection
This commit is contained in:
2025-12-07 01:44:16 +01:00
parent 34a2d07538
commit 9eb57ed117
60 changed files with 5082 additions and 195 deletions

148
src/systems/SoundManager.js Normal file
View File

@@ -0,0 +1,148 @@
class SoundManager {
constructor(scene) {
this.scene = scene;
this.musicVolume = 0.3;
this.sfxVolume = 0.5;
this.isMuted = false;
this.currentMusic = null;
this.currentAmbient = null;
console.log('🎵 SoundManager: Initialized');
}
playSFX(key) {
if (this.isMuted) return;
if (this.scene.sound.get(key)) {
this.scene.sound.play(key, { volume: this.sfxVolume });
} else {
// Enhanced placeholder beeps
if (key === 'chop') {
this.beepChop();
} else if (key === 'pickup') {
this.beepPickup();
} else if (key === 'plant') {
this.beepPlant();
} else if (key === 'harvest') {
this.beepHarvest();
} else if (key === 'build') {
this.beepBuild();
}
}
}
beepChop() {
if (!this.scene.sound.context) return;
const ctx = this.scene.sound.context;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.frequency.value = 150;
osc.type = 'sawtooth';
gain.gain.setValueAtTime(0.15, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.15);
osc.start();
osc.stop(ctx.currentTime + 0.15);
}
beepPickup() {
if (!this.scene.sound.context) return;
const ctx = this.scene.sound.context;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.frequency.setValueAtTime(600, ctx.currentTime);
osc.frequency.linearRampToValueAtTime(1200, ctx.currentTime + 0.1);
osc.type = 'sine';
gain.gain.setValueAtTime(0.12, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.1);
osc.start();
osc.stop(ctx.currentTime + 0.1);
}
beepPlant() {
if (!this.scene.sound.context) return;
const ctx = this.scene.sound.context;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.frequency.value = 300;
osc.type = 'triangle';
gain.gain.setValueAtTime(0.1, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.12);
osc.start();
osc.stop(ctx.currentTime + 0.12);
}
beepHarvest() {
if (!this.scene.sound.context) return;
const ctx = this.scene.sound.context;
const osc1 = ctx.createOscillator();
const gain1 = ctx.createGain();
osc1.connect(gain1);
gain1.connect(ctx.destination);
osc1.frequency.value = 523;
osc1.type = 'sine';
gain1.gain.setValueAtTime(0.1, ctx.currentTime);
gain1.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.08);
osc1.start();
osc1.stop(ctx.currentTime + 0.08);
const osc2 = ctx.createOscillator();
const gain2 = ctx.createGain();
osc2.connect(gain2);
gain2.connect(ctx.destination);
osc2.frequency.value = 659;
osc2.type = 'sine';
gain2.gain.setValueAtTime(0.1, ctx.currentTime + 0.08);
gain2.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.16);
osc2.start(ctx.currentTime + 0.08);
osc2.stop(ctx.currentTime + 0.16);
}
beepBuild() {
if (!this.scene.sound.context) return;
const ctx = this.scene.sound.context;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.connect(gain);
gain.connect(ctx.destination);
osc.frequency.value = 80;
osc.type = 'square';
gain.gain.setValueAtTime(0.2, ctx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.2);
osc.start();
osc.stop(ctx.currentTime + 0.2);
}
playAmbient(key, loop = true) {
if (this.isMuted) return;
if (this.currentAmbient) this.currentAmbient.stop();
if (!this.scene.sound.get(key)) return;
this.currentAmbient = this.scene.sound.add(key, { volume: this.sfxVolume * 0.5, loop: loop });
this.currentAmbient.play();
}
stopAmbient() {
if (this.currentAmbient) {
this.currentAmbient.stop();
this.currentAmbient = null;
}
}
toggleMute() {
this.isMuted = !this.isMuted;
this.scene.sound.mute = this.isMuted;
console.log(this.isMuted ? '🔇 Muted' : '🔊 Unmuted');
}
playChop() { this.playSFX('chop'); }
playPlant() { this.playSFX('plant'); }
playHarvest() { this.playSFX('harvest'); }
playBuild() { this.playSFX('build'); }
playPickup() { this.playSFX('pickup'); }
playRainSound() { this.playAmbient('rain_loop'); }
stopRainSound() { this.stopAmbient(); }
}