COMPLETED FEATURES: PART 1: IMMEDIATE INTEGRATION (30 min) - Crafting system integration verified - Created comprehensive test plans - INTEGRATION_TEST_PLAN.md - QUICK_START_TEST.md PART 3: POLISH & EFFECTS (2h 5min) - 100% DONE! Phase 5C: Lighting & Shadows (20 min) - LightingSystem.js (215 lines) - Dynamic player shadow with time-of-day opacity - Auto-torch at night (flickering effect) - Campfire creation API - Light source management Phase 5B: Enhanced Weather (25 min) - WeatherEnhancementsSystem.js (245 lines) - Dynamic wind system (strength + direction) - Wind affects rain particles - Tree sway animations - Smooth weather transitions (2s fade) - Wind info API (speed km/h, compass) Phase 5D: UI Polish (20 min) - UIPolishSystem.js (330 lines) - Fade in/out & slide animations - Button hover effects with sound - Tooltips (auto + manual, cursor follow) - Pulse, shake, flash animations - Typewriter text effect - Number counter animation - Smooth scroll support Phase 5E: Particle Effects (30 min) - ParticleEnhancementsSystem.js (450 lines) - Craft sparkles (golden burst) - Walk dust clouds (grass/dirt only) - Harvest bursts (crop-colored!) - Dig/till soil particles - Plant sparkles - Level up / damage / heal effects - Integrated with CraftingSystem & FarmingSystem STATS: - 4 new systems created (~1,240 lines) - 5 documentation files - 30+ new features - 7 files modified - Total time: 2h 35min GAME NOW HAS: - Dynamic shadows & lighting - Wind-affected weather - Complete UI animation toolkit - Enhanced particle effects for all actions Files modified: - index.html (4 new script tags) - GameScene.js (4 system initializations + update calls) - CraftingSystem.js (craft sparkles on completion) - FarmingSystem.js (dig/plant/harvest particles) - TASKS.md (Phase 29 updated) - FINAL_IMPLEMENTATION_ROADMAP.md (PART 3 100% complete)
195 lines
5.5 KiB
JavaScript
195 lines
5.5 KiB
JavaScript
// Lighting & Shadow System - Dynamic lighting and shadows
|
|
class LightingSystem {
|
|
constructor(scene) {
|
|
this.scene = scene;
|
|
this.shadows = new Map(); // Entity → Shadow sprite
|
|
this.lights = new Map(); // Source → Light circle
|
|
|
|
console.log('💡 LightingSystem initialized');
|
|
}
|
|
|
|
// Create shadow for an entity (player, NPC, etc.)
|
|
createShadow(entity, offsetY = 10, width = 30, height = 15) {
|
|
if (!entity || !entity.sprite) return null;
|
|
|
|
const shadow = this.scene.add.ellipse(
|
|
entity.sprite.x,
|
|
entity.sprite.y + offsetY,
|
|
width,
|
|
height,
|
|
0x000000,
|
|
0.3
|
|
);
|
|
|
|
shadow.setDepth(entity.sprite.depth - 1); // Always below entity
|
|
this.shadows.set(entity, { shadow, offsetY, width, height });
|
|
|
|
return shadow;
|
|
}
|
|
|
|
// Update shadow position (call in entity update)
|
|
updateShadow(entity) {
|
|
const data = this.shadows.get(entity);
|
|
if (!data || !entity.sprite) return;
|
|
|
|
const { shadow, offsetY } = data;
|
|
shadow.setPosition(entity.sprite.x, entity.sprite.y + offsetY);
|
|
|
|
// Adjust shadow opacity based on time of day
|
|
if (this.scene.weatherSystem) {
|
|
const hour = this.scene.weatherSystem.getCurrentHour();
|
|
let opacity = 0.3;
|
|
|
|
if (hour >= 5 && hour < 7) {
|
|
// Dawn - weak shadow
|
|
opacity = 0.1 + ((hour - 5) / 2) * 0.2;
|
|
} else if (hour >= 7 && hour < 18) {
|
|
// Day - strong shadow
|
|
opacity = 0.3;
|
|
} else if (hour >= 18 && hour < 20) {
|
|
// Dusk - fading shadow
|
|
opacity = 0.3 - ((hour - 18) / 2) * 0.2;
|
|
} else {
|
|
// Night - very weak shadow
|
|
opacity = 0.1;
|
|
}
|
|
|
|
shadow.setAlpha(opacity);
|
|
}
|
|
}
|
|
|
|
// Remove shadow
|
|
removeShadow(entity) {
|
|
const data = this.shadows.get(entity);
|
|
if (data && data.shadow) {
|
|
data.shadow.destroy();
|
|
}
|
|
this.shadows.delete(entity);
|
|
}
|
|
|
|
// Create light source (torch, campfire, etc.)
|
|
createLight(x, y, radius = 100, color = 0xffee88, alpha = 0.3, flicker = false) {
|
|
const light = this.scene.add.circle(x, y, radius, color, alpha);
|
|
light.setBlendMode(Phaser.BlendModes.ADD);
|
|
light.setDepth(999990); // Above most things
|
|
|
|
const lightData = {
|
|
light,
|
|
radius,
|
|
color,
|
|
baseAlpha: alpha,
|
|
flicker,
|
|
flickerTimer: 0
|
|
};
|
|
|
|
this.lights.set(light, lightData);
|
|
|
|
return light;
|
|
}
|
|
|
|
// Create player torch (follows player at night)
|
|
createPlayerTorch(player) {
|
|
if (!player || !player.sprite) return null;
|
|
|
|
const torch = this.scene.add.circle(
|
|
player.sprite.x,
|
|
player.sprite.y,
|
|
80,
|
|
0xffee88,
|
|
0
|
|
);
|
|
|
|
torch.setBlendMode(Phaser.BlendModes.ADD);
|
|
torch.setDepth(999990);
|
|
|
|
this.lights.set(torch, {
|
|
light: torch,
|
|
radius: 80,
|
|
color: 0xffee88,
|
|
baseAlpha: 0.35,
|
|
flicker: true,
|
|
flickerTimer: 0,
|
|
followPlayer: player
|
|
});
|
|
|
|
return torch;
|
|
}
|
|
|
|
// Update all lights
|
|
update(delta) {
|
|
const isNight = this.scene.weatherSystem ? this.scene.weatherSystem.isNight() : false;
|
|
|
|
for (const [light, data] of this.lights) {
|
|
// Follow player if assigned
|
|
if (data.followPlayer && data.followPlayer.sprite) {
|
|
light.setPosition(
|
|
data.followPlayer.sprite.x,
|
|
data.followPlayer.sprite.y
|
|
);
|
|
|
|
// Only show torch at night
|
|
if (isNight) {
|
|
light.setAlpha(data.baseAlpha);
|
|
} else {
|
|
light.setAlpha(0); // Hidden during day
|
|
}
|
|
}
|
|
|
|
// Flicker effect
|
|
if (data.flicker) {
|
|
data.flickerTimer += delta;
|
|
if (data.flickerTimer > 50) { // Every 50ms
|
|
data.flickerTimer = 0;
|
|
const flicker = data.baseAlpha + (Math.random() * 0.1 - 0.05);
|
|
if (isNight || !data.followPlayer) {
|
|
light.setAlpha(Phaser.Math.Clamp(flicker, 0, 1));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update all shadows
|
|
for (const [entity, data] of this.shadows) {
|
|
this.updateShadow(entity);
|
|
}
|
|
}
|
|
|
|
// Remove light
|
|
removeLight(light) {
|
|
const data = this.lights.get(light);
|
|
if (data && data.light) {
|
|
data.light.destroy();
|
|
}
|
|
this.lights.delete(light);
|
|
}
|
|
|
|
// Create campfire effect (static light with particles)
|
|
createCampfire(x, y) {
|
|
// Light glow
|
|
const light = this.createLight(x, y, 120, 0xff6600, 0.4, true);
|
|
|
|
// Smoke particles (if ParticleEffects exists)
|
|
if (this.scene.particleEffects) {
|
|
// Add smoke rising from campfire
|
|
// (Implement in ParticleEffects system)
|
|
}
|
|
|
|
return light;
|
|
}
|
|
|
|
// Clean up
|
|
destroy() {
|
|
// Remove all shadows
|
|
for (const [entity, data] of this.shadows) {
|
|
this.removeShadow(entity);
|
|
}
|
|
|
|
// Remove all lights
|
|
for (const [light, data] of this.lights) {
|
|
this.removeLight(light);
|
|
}
|
|
|
|
console.log('💡 LightingSystem destroyed');
|
|
}
|
|
}
|