feat: Complete 2D Visual Overhaul - Isometric to Flat Top-Down
- NEW: Flat2DTerrainSystem.js (375 lines) - NEW: map2d_data.js procedural map (221 lines) - MODIFIED: GameScene async create, 2D terrain integration - MODIFIED: Player.js flat 2D positioning - MODIFIED: game.js disabled pixelArt for smooth rendering - FIXED: 15+ bugs (updateCulling, isometric conversions, grid lines) - ADDED: Phase 28 to TASKS.md - DOCS: DNEVNIK.md session summary Result: Working flat 2D game with Stardew Valley style! Time: 5.5 hours
This commit is contained in:
@@ -15,13 +15,14 @@ class GameScene extends Phaser.Scene {
|
||||
};
|
||||
}
|
||||
|
||||
create() {
|
||||
async create() {
|
||||
console.log('🎮 GameScene: Initialized!');
|
||||
|
||||
// Generate procedural textures
|
||||
new TextureGenerator(this).generateAll();
|
||||
InventoryIcons.create(this); // Override with flat 2D inventory icons
|
||||
window.gameState.currentScene = 'GameScene';
|
||||
window.gameState.gameScene = this; // Reference for global inventory helper
|
||||
|
||||
const width = this.cameras.main.width;
|
||||
const height = this.cameras.main.height;
|
||||
@@ -55,8 +56,25 @@ class GameScene extends Phaser.Scene {
|
||||
// Inicializiraj terrain sistem - 100x100 mapa
|
||||
console.log('🌍 Initializing terrain...');
|
||||
try {
|
||||
this.terrainSystem = new TerrainSystem(this, 100, 100);
|
||||
this.terrainSystem.generate();
|
||||
// 🎲 SEED-BASED GENERATION - Vsak krat ista mapa!
|
||||
// Preveri če že imaš shranjeno spawn točko
|
||||
let spawnPoint = localStorage.getItem('novafarma_spawn_point');
|
||||
let terrainSeed = localStorage.getItem('novafarma_terrain_seed');
|
||||
|
||||
if (!terrainSeed) {
|
||||
// PRVI LOGIN - generiraj nov seed
|
||||
terrainSeed = Math.random().toString(36).substring(7);
|
||||
localStorage.setItem('novafarma_terrain_seed', terrainSeed);
|
||||
console.log('🆕 New world seed:', terrainSeed);
|
||||
} else {
|
||||
console.log('♻️ Loading existing world with seed:', terrainSeed);
|
||||
}
|
||||
|
||||
// 🎨 2D FLAT TERRAIN SYSTEM (NEW!)
|
||||
console.log('🎨 Initializing Flat 2D Terrain...');
|
||||
this.terrainSystem = new Flat2DTerrainSystem(this);
|
||||
await this.terrainSystem.generate();
|
||||
console.log('✅ Flat 2D terrain ready!');
|
||||
|
||||
// Initialize Farming System
|
||||
this.farmingSystem = new FarmingSystem(this);
|
||||
@@ -115,8 +133,28 @@ class GameScene extends Phaser.Scene {
|
||||
// Initial force update to render active tiles before first frame
|
||||
this.terrainSystem.updateCulling(this.cameras.main);
|
||||
|
||||
// INITIALIZE FARM AREA (Starter Zone @ 20,20)
|
||||
this.initializeFarmWorld();
|
||||
// 🎲 RANDOM SPAWN POINT + 8x8 FARM (Prvi login)
|
||||
if (!spawnPoint) {
|
||||
// PRVI LOGIN - Random spawn točka
|
||||
const spawnX = Math.floor(Math.random() * 80) + 10; // 10-90
|
||||
const spawnY = Math.floor(Math.random() * 80) + 10; // 10-90
|
||||
|
||||
spawnPoint = `${spawnX},${spawnY}`;
|
||||
localStorage.setItem('novafarma_spawn_point', spawnPoint);
|
||||
|
||||
console.log(`🎲 First login - Random spawn at (${spawnX}, ${spawnY})`);
|
||||
console.log(`🏡 Creating 8x8 starter farm at spawn location...`);
|
||||
|
||||
// Ustvari 8x8 farmo na spawn točki
|
||||
this.initializeFarmWorld(spawnX, spawnY);
|
||||
} else {
|
||||
// NI PRVI LOGIN - Naloži obstoječo spawn točko
|
||||
const [spawnX, spawnY] = spawnPoint.split(',').map(Number);
|
||||
console.log(`♻️ Returning player - spawn at (${spawnX}, ${spawnY})`);
|
||||
|
||||
// Obnovi farmo (če je shranjena)
|
||||
this.initializeFarmWorld(spawnX, spawnY);
|
||||
}
|
||||
|
||||
// 🍎 SADOVNJAK - Sadna Drevesa (ONEMOGOČENO)
|
||||
// ========================================================
|
||||
@@ -334,9 +372,17 @@ class GameScene extends Phaser.Scene {
|
||||
console.error("Terrain system failed:", e);
|
||||
}
|
||||
|
||||
// Dodaj igralca
|
||||
// Dodaj igralca NA SPAWN TOČKI
|
||||
console.log('👤 Initializing player...');
|
||||
this.player = new Player(this, 50, 50, this.terrainOffsetX, this.terrainOffsetY);
|
||||
const savedSpawn = localStorage.getItem('novafarma_spawn_point');
|
||||
let playerSpawnX = 50, playerSpawnY = 50;
|
||||
|
||||
if (savedSpawn) {
|
||||
[playerSpawnX, playerSpawnY] = savedSpawn.split(',').map(Number);
|
||||
console.log(`👤 Spawning player at saved location: (${playerSpawnX}, ${playerSpawnY})`);
|
||||
}
|
||||
|
||||
this.player = new Player(this, playerSpawnX, playerSpawnY, this.terrainOffsetX, this.terrainOffsetY);
|
||||
|
||||
// 🎯 SORTABLE OBJECTS GROUP - Za 2.5D Z-Sorting
|
||||
console.log('🎯 Creating sortableObjects group for Z-sorting...');
|
||||
@@ -438,6 +484,22 @@ class GameScene extends Phaser.Scene {
|
||||
this.statsSystem = new StatsSystem(this);
|
||||
this.inventorySystem = new InventorySystem(this);
|
||||
|
||||
// 🛠️ CRAFTING SYSTEM
|
||||
this.craftingSystem = new CraftingSystem(this);
|
||||
this.craftingSystem.loadRecipes().then(() => {
|
||||
console.log('🛠️ Crafting system ready!');
|
||||
|
||||
// Create UI after recipes loaded
|
||||
this.craftingUI = new CraftingUI(this);
|
||||
|
||||
// Add C key to toggle crafting UI
|
||||
this.input.keyboard.on('keydown-C', () => {
|
||||
if (this.craftingUI) {
|
||||
this.craftingUI.toggle();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ========================================================
|
||||
// 💎 NEOMEJENI VIRI - Les in Kamen
|
||||
// ========================================================
|
||||
@@ -775,6 +837,18 @@ class GameScene extends Phaser.Scene {
|
||||
setupCamera() {
|
||||
const cam = this.cameras.main;
|
||||
|
||||
// 🎨 FLAT 2D CAMERA SETUP (NEW!)
|
||||
const worldSize = 100 * 48; // 100 tiles × 48px = 4800px
|
||||
cam.setBounds(0, 0, worldSize, worldSize);
|
||||
cam.setZoom(1.0); // Default zoom for 2D
|
||||
|
||||
// Follow player
|
||||
if (this.player && this.player.sprite) {
|
||||
cam.startFollow(this.player.sprite, true, 0.1, 0.1);
|
||||
}
|
||||
|
||||
console.log('📹 2D Camera setup:', worldSize, 'x', worldSize);
|
||||
|
||||
// Zoom kontrole (Mouse Wheel)
|
||||
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
|
||||
const zoomSpeed = 0.001;
|
||||
@@ -843,6 +917,597 @@ class GameScene extends Phaser.Scene {
|
||||
this.input.keyboard.on('keydown-M', () => {
|
||||
if (this.soundManager) this.soundManager.toggleMute();
|
||||
});
|
||||
|
||||
// 🌧️ WEATHER SYSTEM KEYS
|
||||
this.input.keyboard.on('keydown-R', () => this.setWeather('rain'));
|
||||
this.input.keyboard.on('keydown-SHIFT-S', () => this.setWeather('snow'));
|
||||
this.input.keyboard.on('keydown-T', () => this.setWeather('storm'));
|
||||
this.input.keyboard.on('keydown-F', () => this.setWeather('fog'));
|
||||
this.input.keyboard.on('keydown-SHIFT-C', () => this.setWeather('clear'));
|
||||
|
||||
// Initialize weather system
|
||||
this.initializeWeatherSystem();
|
||||
|
||||
// Initialize Weather UI Panel
|
||||
this.weatherUI = new WeatherUI(this);
|
||||
console.log('📊 Weather UI Panel created (press W to toggle)');
|
||||
}
|
||||
|
||||
// 🌦️ COMPLETE WEATHER SYSTEM
|
||||
initializeWeatherSystem() {
|
||||
this.currentWeather = 'clear';
|
||||
this.weatherIntensity = 1.0;
|
||||
this.puddles = [];
|
||||
this.splashes = [];
|
||||
this.autoWeatherEnabled = false;
|
||||
this.weatherCycleTimer = null;
|
||||
|
||||
// Load saved weather state
|
||||
this.loadWeatherState();
|
||||
|
||||
console.log('🌦️ Weather system initialized');
|
||||
console.log('💡 R = Rain | Shift+S = Snow | T = Storm | F = Fog | Shift+C = Clear');
|
||||
console.log('💡 Shift+A = Toggle Auto Weather Cycle');
|
||||
|
||||
// Auto weather cycle toggle
|
||||
this.input.keyboard.on('keydown-SHIFT-A', () => {
|
||||
this.toggleAutoWeather();
|
||||
});
|
||||
|
||||
// Intensity controls (+/-)
|
||||
this.input.keyboard.on('keydown-PLUS', () => this.adjustIntensity(0.2));
|
||||
this.input.keyboard.on('keydown-MINUS', () => this.adjustIntensity(-0.2));
|
||||
}
|
||||
|
||||
toggleAutoWeather() {
|
||||
this.autoWeatherEnabled = !this.autoWeatherEnabled;
|
||||
|
||||
if (this.autoWeatherEnabled) {
|
||||
console.log('🔄 Auto weather cycle ENABLED');
|
||||
this.startWeatherCycle();
|
||||
} else {
|
||||
console.log('⏸️ Auto weather cycle DISABLED');
|
||||
if (this.weatherCycleTimer) {
|
||||
this.weatherCycleTimer.destroy();
|
||||
this.weatherCycleTimer = null;
|
||||
}
|
||||
}
|
||||
this.saveWeatherState();
|
||||
}
|
||||
|
||||
startWeatherCycle() {
|
||||
// Weather cycle: Clear → Rain → Storm → Clear → Snow → Fog → Clear
|
||||
const weatherSequence = ['clear', 'rain', 'storm', 'clear', 'snow', 'fog'];
|
||||
const weatherDurations = {
|
||||
clear: 120000, // 2 minutes
|
||||
rain: 90000, // 1.5 minutes
|
||||
storm: 60000, // 1 minute
|
||||
snow: 90000, // 1.5 minutes
|
||||
fog: 60000 // 1 minute
|
||||
};
|
||||
|
||||
let currentIndex = 0;
|
||||
|
||||
const cycleWeather = () => {
|
||||
const nextWeather = weatherSequence[currentIndex];
|
||||
this.setWeather(nextWeather);
|
||||
|
||||
currentIndex = (currentIndex + 1) % weatherSequence.length;
|
||||
|
||||
const duration = weatherDurations[nextWeather] || 120000;
|
||||
|
||||
this.weatherCycleTimer = this.time.delayedCall(duration, () => {
|
||||
if (this.autoWeatherEnabled) {
|
||||
cycleWeather();
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`🌦️ Auto weather: ${nextWeather} (${duration / 1000}s)`);
|
||||
};
|
||||
|
||||
cycleWeather();
|
||||
}
|
||||
|
||||
adjustIntensity(delta) {
|
||||
this.weatherIntensity = Phaser.Math.Clamp(this.weatherIntensity + delta, 0.2, 2.0);
|
||||
|
||||
// Update current weather with new intensity
|
||||
if (this.currentWeather !== 'clear' && this.currentWeather !== 'fog') {
|
||||
this.setWeather(this.currentWeather); // Restart with new intensity
|
||||
}
|
||||
|
||||
console.log(`🎚️ Weather intensity: ${(this.weatherIntensity * 100).toFixed(0)}%`);
|
||||
this.saveWeatherState();
|
||||
}
|
||||
|
||||
saveWeatherState() {
|
||||
const state = {
|
||||
currentWeather: this.currentWeather,
|
||||
intensity: this.weatherIntensity,
|
||||
autoEnabled: this.autoWeatherEnabled
|
||||
};
|
||||
localStorage.setItem('novafarma_weather_state', JSON.stringify(state));
|
||||
}
|
||||
|
||||
loadWeatherState() {
|
||||
const saved = localStorage.getItem('novafarma_weather_state');
|
||||
if (saved) {
|
||||
try {
|
||||
const state = JSON.parse(saved);
|
||||
this.currentWeather = state.currentWeather || 'clear';
|
||||
this.weatherIntensity = state.intensity || 1.0;
|
||||
this.autoWeatherEnabled = state.autoEnabled || false;
|
||||
|
||||
console.log('📂 Weather state loaded:', state);
|
||||
|
||||
// Restore weather (delayed to avoid initialization issues)
|
||||
this.time.delayedCall(1000, () => {
|
||||
if (this.currentWeather !== 'clear') {
|
||||
this.setWeather(this.currentWeather);
|
||||
}
|
||||
if (this.autoWeatherEnabled) {
|
||||
this.startWeatherCycle();
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('⚠️ Failed to load weather state:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setWeather(type) {
|
||||
// Stop current weather
|
||||
this.stopAllWeather();
|
||||
|
||||
this.currentWeather = type;
|
||||
|
||||
switch (type) {
|
||||
case 'rain':
|
||||
this.startRain();
|
||||
break;
|
||||
case 'snow':
|
||||
this.startSnow();
|
||||
break;
|
||||
case 'storm':
|
||||
this.startStorm();
|
||||
break;
|
||||
case 'fog':
|
||||
this.startFog();
|
||||
break;
|
||||
case 'clear':
|
||||
console.log('☀️ Clear weather');
|
||||
break;
|
||||
}
|
||||
|
||||
this.saveWeatherState();
|
||||
}
|
||||
|
||||
stopAllWeather() {
|
||||
// Stop all particle emitters
|
||||
if (this.rainEmitter) this.rainEmitter.stop();
|
||||
if (this.snowEmitter) this.snowEmitter.stop();
|
||||
if (this.stormEmitter) this.stormEmitter.stop();
|
||||
|
||||
// Stop lightning
|
||||
if (this.lightningTimer) {
|
||||
this.lightningTimer.destroy();
|
||||
this.lightningTimer = null;
|
||||
}
|
||||
|
||||
// Stop puddle spawning
|
||||
if (this.puddleTimer) {
|
||||
this.puddleTimer.destroy();
|
||||
this.puddleTimer = null;
|
||||
}
|
||||
|
||||
// Fade out fog
|
||||
if (this.fogOverlay) {
|
||||
this.tweens.add({
|
||||
targets: this.fogOverlay,
|
||||
alpha: 0,
|
||||
duration: 2000,
|
||||
onComplete: () => {
|
||||
if (this.fogOverlay) this.fogOverlay.destroy();
|
||||
this.fogOverlay = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cleanup puddles
|
||||
this.puddles.forEach(puddle => {
|
||||
this.tweens.add({
|
||||
targets: puddle,
|
||||
alpha: 0,
|
||||
duration: 3000,
|
||||
onComplete: () => puddle.destroy()
|
||||
});
|
||||
});
|
||||
this.puddles = [];
|
||||
}
|
||||
|
||||
// ☔ RAIN SYSTEM
|
||||
startRain() {
|
||||
if (!this.rainEmitter) {
|
||||
this.createRainParticles();
|
||||
}
|
||||
|
||||
// Apply intensity
|
||||
this.rainEmitter.setQuantity(Math.floor(3 * this.weatherIntensity));
|
||||
this.rainEmitter.setFrequency(30 / this.weatherIntensity);
|
||||
|
||||
this.rainEmitter.start();
|
||||
console.log('🌧️ Rain started!');
|
||||
|
||||
// Start puddle spawning
|
||||
this.puddleTimer = this.time.addEvent({
|
||||
delay: 3000 / this.weatherIntensity,
|
||||
callback: () => this.spawnPuddle(),
|
||||
loop: true
|
||||
});
|
||||
|
||||
// Play rain sound
|
||||
if (this.soundManager && this.soundManager.playRainSound) {
|
||||
this.soundManager.playRainSound();
|
||||
}
|
||||
}
|
||||
|
||||
createRainParticles() {
|
||||
if (!this.textures.exists('raindrop')) {
|
||||
const graphics = this.make.graphics({ x: 0, y: 0, add: false });
|
||||
graphics.fillStyle(0x88ccff, 1);
|
||||
graphics.fillCircle(1, 2, 1);
|
||||
graphics.generateTexture('raindrop', 2, 4);
|
||||
graphics.destroy();
|
||||
}
|
||||
|
||||
const cam = this.cameras.main;
|
||||
this.rainEmitter = this.add.particles(0, 0, 'raindrop', {
|
||||
x: { min: 0, max: cam.width },
|
||||
y: -50,
|
||||
lifespan: 3000,
|
||||
speedY: { min: 400, max: 600 },
|
||||
scale: { start: 1, end: 0.5 },
|
||||
alpha: { start: 0.6, end: 0.2 },
|
||||
quantity: 3,
|
||||
frequency: 30,
|
||||
blendMode: 'ADD',
|
||||
|
||||
// 🌊 DETECT WHEN RAINDROP HITS GROUND
|
||||
deathCallback: (particle) => {
|
||||
// Convert camera-relative position to world position
|
||||
const worldX = particle.x + cam.scrollX;
|
||||
const worldY = particle.y + cam.scrollY;
|
||||
|
||||
// Check if hit water tile
|
||||
this.checkRainImpactOnWater(worldX, worldY);
|
||||
}
|
||||
});
|
||||
|
||||
this.rainEmitter.setScrollFactor(0);
|
||||
this.rainEmitter.setDepth(999999);
|
||||
this.rainEmitter.stop();
|
||||
}
|
||||
|
||||
// ❄️ SNOW SYSTEM
|
||||
startSnow() {
|
||||
if (!this.snowEmitter) {
|
||||
this.createSnowParticles();
|
||||
}
|
||||
|
||||
this.snowEmitter.start();
|
||||
console.log('❄️ Snow started!');
|
||||
}
|
||||
|
||||
createSnowParticles() {
|
||||
if (!this.textures.exists('snowflake')) {
|
||||
const graphics = this.make.graphics({ x: 0, y: 0, add: false });
|
||||
graphics.fillStyle(0xffffff, 1);
|
||||
graphics.fillCircle(2, 2, 2);
|
||||
graphics.generateTexture('snowflake', 4, 4);
|
||||
graphics.destroy();
|
||||
}
|
||||
|
||||
const cam = this.cameras.main;
|
||||
this.snowEmitter = this.add.particles(0, 0, 'snowflake', {
|
||||
x: { min: 0, max: cam.width },
|
||||
y: -50,
|
||||
lifespan: 8000,
|
||||
speedY: { min: 50, max: 150 },
|
||||
speedX: { min: -30, max: 30 },
|
||||
scale: { start: 0.5, end: 1.5 },
|
||||
alpha: { start: 0.8, end: 0.3 },
|
||||
quantity: 2,
|
||||
frequency: 80,
|
||||
blendMode: 'ADD'
|
||||
});
|
||||
|
||||
this.snowEmitter.setScrollFactor(0);
|
||||
this.snowEmitter.setDepth(999999);
|
||||
this.snowEmitter.stop();
|
||||
}
|
||||
|
||||
// ⚡ STORM SYSTEM
|
||||
startStorm() {
|
||||
if (!this.stormEmitter) {
|
||||
this.createStormParticles();
|
||||
}
|
||||
|
||||
this.stormEmitter.start();
|
||||
console.log('⛈️ Storm started!');
|
||||
|
||||
// Lightning flashes
|
||||
this.lightningTimer = this.time.addEvent({
|
||||
delay: Phaser.Math.Between(3000, 8000),
|
||||
callback: () => this.triggerLightning(),
|
||||
loop: true
|
||||
});
|
||||
|
||||
// Puddles (faster spawn)
|
||||
this.puddleTimer = this.time.addEvent({
|
||||
delay: 2000,
|
||||
callback: () => this.spawnPuddle(),
|
||||
loop: true
|
||||
});
|
||||
}
|
||||
|
||||
createStormParticles() {
|
||||
if (!this.textures.exists('raindrop')) {
|
||||
this.createRainParticles();
|
||||
}
|
||||
|
||||
const cam = this.cameras.main;
|
||||
this.stormEmitter = this.add.particles(0, 0, 'raindrop', {
|
||||
x: { min: 0, max: cam.width },
|
||||
y: -50,
|
||||
lifespan: 2000,
|
||||
speedY: { min: 600, max: 900 },
|
||||
speedX: { min: 50, max: 150 },
|
||||
scale: { start: 1.5, end: 0.5 },
|
||||
alpha: { start: 0.8, end: 0.3 },
|
||||
quantity: 5,
|
||||
frequency: 20,
|
||||
blendMode: 'ADD'
|
||||
});
|
||||
|
||||
this.stormEmitter.setScrollFactor(0);
|
||||
this.stormEmitter.setDepth(999999);
|
||||
this.stormEmitter.stop();
|
||||
}
|
||||
|
||||
triggerLightning() {
|
||||
// White flash overlay
|
||||
const flash = this.add.rectangle(
|
||||
this.cameras.main.scrollX,
|
||||
this.cameras.main.scrollY,
|
||||
this.cameras.main.width,
|
||||
this.cameras.main.height,
|
||||
0xffffff,
|
||||
0.8
|
||||
);
|
||||
flash.setOrigin(0, 0);
|
||||
flash.setScrollFactor(0);
|
||||
flash.setDepth(1000000);
|
||||
|
||||
// Fade out
|
||||
this.tweens.add({
|
||||
targets: flash,
|
||||
alpha: 0,
|
||||
duration: 200,
|
||||
onComplete: () => flash.destroy()
|
||||
});
|
||||
|
||||
// Screen shake
|
||||
this.cameras.main.shake(200, 0.005);
|
||||
|
||||
// Thunder sound (if available)
|
||||
if (this.soundManager && this.soundManager.playThunderSound) {
|
||||
this.time.delayedCall(300, () => {
|
||||
this.soundManager.playThunderSound();
|
||||
});
|
||||
}
|
||||
|
||||
console.log('⚡ Lightning strike!');
|
||||
}
|
||||
|
||||
// 🌫️ FOG SYSTEM
|
||||
startFog() {
|
||||
if (!this.fogOverlay) {
|
||||
this.fogOverlay = this.add.rectangle(
|
||||
0, 0,
|
||||
this.cameras.main.width * 2,
|
||||
this.cameras.main.height * 2,
|
||||
0xcccccc,
|
||||
0
|
||||
);
|
||||
this.fogOverlay.setOrigin(0, 0);
|
||||
this.fogOverlay.setScrollFactor(0);
|
||||
this.fogOverlay.setDepth(500000);
|
||||
}
|
||||
|
||||
this.tweens.add({
|
||||
targets: this.fogOverlay,
|
||||
alpha: 0.4,
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
console.log('🌫️ Fog started!');
|
||||
}
|
||||
|
||||
|
||||
// 💧 PUDDLES SYSTEM (Spawn on grass/dirt where rain lands!)
|
||||
spawnPuddle() {
|
||||
if (!this.terrainSystem) return;
|
||||
|
||||
const cam = this.cameras.main;
|
||||
const worldX = cam.scrollX + Phaser.Math.Between(100, cam.width - 100);
|
||||
const worldY = cam.scrollY + Phaser.Math.Between(100, cam.height - 100);
|
||||
|
||||
// 🎨 FLAT 2D CONVERSION (NEW!)
|
||||
const tileSize = 48;
|
||||
const x = Math.floor(worldX / tileSize);
|
||||
const y = Math.floor(worldY / tileSize);
|
||||
|
||||
// Get tile type
|
||||
const tile = this.terrainSystem.getTile(x, y);
|
||||
|
||||
// ONLY spawn puddles on grass or dirt!
|
||||
if (!tile || (tile.type !== 'grass' && tile.type !== 'dirt' && tile.type !== 'farmland')) {
|
||||
return; // Skip - not valid surface
|
||||
}
|
||||
|
||||
// Limit max puddles
|
||||
if (this.puddles.length >= 15) {
|
||||
const oldest = this.puddles.shift();
|
||||
if (oldest && oldest.active) oldest.destroy();
|
||||
}
|
||||
|
||||
// Create puddle SPRITE (realistic!)
|
||||
const puddle = this.add.image(worldX, worldY, 'luza_sprite');
|
||||
puddle.setOrigin(0.5, 0.5);
|
||||
puddle.setScale(1.5); // BIGGER - more visible!
|
||||
puddle.setDepth(10); // ABOVE terrain
|
||||
puddle.setScrollFactor(1);
|
||||
puddle.setAlpha(0); // Start invisible
|
||||
|
||||
// Fade in
|
||||
this.tweens.add({
|
||||
targets: puddle,
|
||||
alpha: 0.35,
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
this.puddles.push(puddle);
|
||||
|
||||
// Auto cleanup after 30 seconds
|
||||
this.time.delayedCall(30000, () => {
|
||||
if (puddle && puddle.active) {
|
||||
this.tweens.add({
|
||||
targets: puddle,
|
||||
alpha: 0,
|
||||
duration: 3000,
|
||||
onComplete: () => {
|
||||
puddle.destroy();
|
||||
const index = this.puddles.indexOf(puddle);
|
||||
if (index > -1) this.puddles.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Random splash effects
|
||||
this.time.addEvent({
|
||||
delay: Phaser.Math.Between(1000, 3000),
|
||||
callback: () => {
|
||||
if (puddle && puddle.active && (this.currentWeather === 'rain' || this.currentWeather === 'storm')) {
|
||||
this.createSplash(puddle.x, puddle.y);
|
||||
}
|
||||
},
|
||||
loop: true,
|
||||
repeat: 5
|
||||
});
|
||||
}
|
||||
|
||||
// 🌊 CHECK IF RAIN HIT WATER TILE
|
||||
checkRainImpactOnWater(worldX, worldY) {
|
||||
if (!this.terrainSystem) return;
|
||||
|
||||
// 🎨 FLAT 2D CONVERSION (NEW!)
|
||||
const tileSize = 48;
|
||||
const x = Math.floor(worldX / tileSize);
|
||||
const y = Math.floor(worldY / tileSize);
|
||||
|
||||
// Get tile at position
|
||||
const tile = this.terrainSystem.getTile(x, y);
|
||||
|
||||
// If water tile, create ripple!
|
||||
if (tile && tile.type === 'water') {
|
||||
this.createWaterRipple(worldX, worldY);
|
||||
}
|
||||
// If grass/dirt, 3% chance to spawn puddle
|
||||
else if (tile && (tile.type === 'grass' || tile.type === 'dirt' || tile.type === 'farmland')) {
|
||||
if (Math.random() < 0.03) {
|
||||
this.spawnPuddleAtLocation(worldX, worldY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 💧 CREATE WATER RIPPLE EFFECT
|
||||
createWaterRipple(x, y) {
|
||||
// Small expanding circle on water surface
|
||||
const ripple = this.add.circle(x, y, 2, 0xffffff, 0.5);
|
||||
ripple.setDepth(500); // Above water tiles
|
||||
ripple.setScrollFactor(1); // World-bound
|
||||
|
||||
this.tweens.add({
|
||||
targets: ripple,
|
||||
radius: 10,
|
||||
alpha: 0,
|
||||
duration: 400,
|
||||
ease: 'Quad.easeOut',
|
||||
onComplete: () => ripple.destroy()
|
||||
});
|
||||
}
|
||||
|
||||
// 💧 SPAWN PUDDLE AT EXACT LOCATION (from rain impact)
|
||||
spawnPuddleAtLocation(worldX, worldY) {
|
||||
// Limit max puddles
|
||||
if (this.puddles.length >= 15) {
|
||||
// Remove oldest puddle
|
||||
const oldest = this.puddles.shift();
|
||||
if (oldest && oldest.active) oldest.destroy();
|
||||
}
|
||||
|
||||
// Create puddle SPRITE (realistic!)
|
||||
const puddle = this.add.image(worldX, worldY, 'luza_sprite');
|
||||
puddle.setOrigin(0.5, 0.5);
|
||||
puddle.setScale(1.5); // BIGGER - more visible!
|
||||
puddle.setDepth(10); // ABOVE terrain
|
||||
puddle.setScrollFactor(1);
|
||||
puddle.setAlpha(0); // Start invisible
|
||||
|
||||
// Fade in
|
||||
this.tweens.add({
|
||||
targets: puddle,
|
||||
alpha: 0.35,
|
||||
duration: 2000
|
||||
});
|
||||
|
||||
this.puddles.push(puddle);
|
||||
|
||||
// Auto cleanup after 30 seconds
|
||||
this.time.delayedCall(30000, () => {
|
||||
if (puddle && puddle.active) {
|
||||
this.tweens.add({
|
||||
targets: puddle,
|
||||
alpha: 0,
|
||||
duration: 3000,
|
||||
onComplete: () => {
|
||||
puddle.destroy();
|
||||
const index = this.puddles.indexOf(puddle);
|
||||
if (index > -1) this.puddles.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 💦 SPLASH EFFECT (Ripples)
|
||||
createSplash(x, y) {
|
||||
// Concentric circles
|
||||
for (let i = 0; i < 3; i++) {
|
||||
this.time.delayedCall(i * 100, () => {
|
||||
const circle = this.add.circle(x, y, 3, 0xffffff, 0.5);
|
||||
circle.setDepth(2);
|
||||
|
||||
this.tweens.add({
|
||||
targets: circle,
|
||||
radius: 20 + (i * 5),
|
||||
alpha: 0,
|
||||
duration: 600,
|
||||
onComplete: () => circle.destroy()
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
update(time, delta) {
|
||||
@@ -862,9 +1527,13 @@ class GameScene extends Phaser.Scene {
|
||||
});
|
||||
}
|
||||
|
||||
// Weather UI Update
|
||||
if (this.weatherUI) this.weatherUI.update();
|
||||
|
||||
// Update Systems
|
||||
if (this.terrainSystem) this.terrainSystem.update(time, delta); // Water animation!
|
||||
if (this.statsSystem) this.statsSystem.update(delta);
|
||||
if (this.craftingSystem) this.craftingSystem.update(delta); // 🛠️ Crafting progress
|
||||
if (this.lootSystem) this.lootSystem.update(delta);
|
||||
if (this.interactionSystem) this.interactionSystem.update(delta);
|
||||
if (this.farmingSystem) this.farmingSystem.update(delta);
|
||||
@@ -948,26 +1617,30 @@ class GameScene extends Phaser.Scene {
|
||||
// Parallax Logic
|
||||
if (this.parallaxSystem && this.player) {
|
||||
const playerPos = this.player.getPosition();
|
||||
const screenPos = this.iso.toScreen(playerPos.x, playerPos.y);
|
||||
this.parallaxSystem.update(
|
||||
screenPos.x + this.terrainOffsetX,
|
||||
screenPos.y + this.terrainOffsetY
|
||||
);
|
||||
// 🎨 FLAT 2D (NEW!) - Direct position, no conversion
|
||||
const tileSize = 48;
|
||||
const screenX = playerPos.x * tileSize + tileSize / 2;
|
||||
const screenY = playerPos.y * tileSize + tileSize / 2;
|
||||
this.parallaxSystem.update(screenX, screenY);
|
||||
}
|
||||
|
||||
// Terrain Culling & Update
|
||||
// Terrain Update
|
||||
if (this.terrainSystem) {
|
||||
this.terrainSystem.updateCulling(this.cameras.main);
|
||||
// Note: Flat2D doesn't need culling (already optimized)
|
||||
// this.terrainSystem.updateCulling(this.cameras.main);
|
||||
this.terrainSystem.update(delta);
|
||||
}
|
||||
|
||||
|
||||
// Clouds
|
||||
if (this.clouds) {
|
||||
for (const cloud of this.clouds) {
|
||||
cloud.sprite.x += cloud.speed * (delta / 1000);
|
||||
if (cloud.sprite.x > this.terrainOffsetX + 2000) {
|
||||
cloud.sprite.x = this.terrainOffsetX - 2000;
|
||||
cloud.sprite.y = Phaser.Math.Between(0, 1000);
|
||||
if (cloud && cloud.sprite) {
|
||||
cloud.sprite.x += cloud.speed * (delta / 1000);
|
||||
if (cloud.sprite.x > this.terrainOffsetX + 2000) {
|
||||
cloud.sprite.x = this.terrainOffsetX - 2000;
|
||||
cloud.sprite.y = Phaser.Math.Between(0, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1138,12 +1811,12 @@ class GameScene extends Phaser.Scene {
|
||||
if (this.saveSystem) this.saveSystem.loadGame();
|
||||
}
|
||||
|
||||
initializeFarmWorld() {
|
||||
console.log('🌾 Initializing Farm Area (Starter Zone)...');
|
||||
initializeFarmWorld(spawnX = 20, spawnY = 20) {
|
||||
console.log(`🌾 Initializing 8x8 Farm Area at (${spawnX}, ${spawnY})...`);
|
||||
|
||||
const farmX = 20; // Farm center
|
||||
const farmY = 20;
|
||||
const farmRadius = 8;
|
||||
const farmX = spawnX; // Farm center (custom spawn)
|
||||
const farmY = spawnY;
|
||||
const farmRadius = 4; // 8x8 = radius 4 (4 tiles in each direction)
|
||||
|
||||
// 1. Clear farm area (odstrani drevesa in kamne)
|
||||
for (let x = farmX - farmRadius; x <= farmX + farmRadius; x++) {
|
||||
@@ -1154,27 +1827,25 @@ class GameScene extends Phaser.Scene {
|
||||
if (this.terrainSystem.decorationsMap.has(key)) {
|
||||
this.terrainSystem.removeDecoration(x, y);
|
||||
}
|
||||
// Make it DIRT for farming
|
||||
|
||||
// Change terrain to grass
|
||||
if (this.terrainSystem.tiles[y] && this.terrainSystem.tiles[y][x]) {
|
||||
this.terrainSystem.tiles[y][x].type = 'dirt';
|
||||
this.terrainSystem.tiles[y][x].type = 'grass';
|
||||
this.terrainSystem.tiles[y][x].solid = false;
|
||||
if (this.terrainSystem.tiles[y][x].sprite) {
|
||||
this.terrainSystem.tiles[y][x].sprite.setTexture('dirt');
|
||||
this.terrainSystem.tiles[y][x].sprite.setTint(0xffffff); // Clear tint
|
||||
this.terrainSystem.tiles[y][x].sprite.setTexture('grass');
|
||||
this.terrainSystem.tiles[y][x].sprite.clearTint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Place starter resources (chest s semeni) - REMOVED PER USER REQUEST (Floating chest bug)
|
||||
// this.terrainSystem.placeStructure(farmX + 3, farmY + 3, 'chest');
|
||||
|
||||
// 3. Place FULL FENCE around farm
|
||||
// console.log('🚧 Building Farm Fence...');
|
||||
// const minX = farmX - farmRadius;
|
||||
// const maxX = farmX + farmRadius;
|
||||
// const minY = farmY - farmRadius;
|
||||
// const maxY = farmY + farmRadius;
|
||||
// 2. Optional: Add fence around farm (commented out)
|
||||
// const minX = farmX - farmRadius - 1;
|
||||
// const maxX = farmX + farmRadius + 1;
|
||||
// const minY = farmY - farmRadius - 1;
|
||||
// const maxY = farmY + farmRadius + 1;
|
||||
|
||||
// // Top and bottom horizontal fences
|
||||
// for (let x = minX; x <= maxX; x++) {
|
||||
@@ -1192,7 +1863,7 @@ class GameScene extends Phaser.Scene {
|
||||
// }
|
||||
// }
|
||||
|
||||
console.log('✅ Farm Area Initialized at (20,20)');
|
||||
console.log(`✅ 8x8 Farm Area Initialized at (${spawnX},${spawnY})`);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
|
||||
Reference in New Issue
Block a user