// Preload Scene - Nalaganje assetov class PreloadScene extends Phaser.Scene { constructor() { super({ key: 'PreloadScene' }); } preload() { console.log('โณ PreloadScene: Loading MINIMAL assets for DEMO...'); this.createLoadingBar(); // ๐Ÿ› ๏ธ Asset Fix: Create placeholder for missing particles // This prevents crash in WaterPhysicsSystem if 'particle_white' is used before load if (!this.textures.exists('particle_white')) { const g = this.make.graphics({ x: 0, y: 0, add: false }); g.fillStyle(0xffffff, 1); g.fillCircle(4, 4, 4); g.generateTexture('particle_white', 8, 8); } // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐ŸŽต AUDIO PRELOAD - Cinematic Voices // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• this.preloadAudio(); // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐ŸŒ LOCALIZATION // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• this.load.json('localization', 'assets/localization.json'); // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐ŸŽจ CHARACTER SPRITES - Demo Characters // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• this.preloadCharacterSprites(); // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // ๐ŸŽฎ DEMO MODE - ALL OLD ASSETS DISABLED! // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• // DemoSceneEnhanced has its own preload() - ne rabimo niฤesar! // This PreloadScene samo pokazuje loading bar in gre v DemoSceneEnhanced console.log('โœ… Minimal preload complete - DemoSceneEnhanced will load its own assets!'); } preloadCharacterSprites() { console.log('๐ŸŽจ Preloading ALL ASSETS from AssetManifest...'); // Dynamic import logic (requires module system, but we'll use direct reference if global or shim) // Since we are in Setup phase, we assume AssetManifest is loaded via script tag or bundler // Check if AssetManifest exists globally or we need to rely on the file we created // For this environment, we'll manually iterate the data structure if it's not imported. // But since we just created src/AssetManifest.js, let's try to assume it's available. // NOTE: In standard Phaser/Webpack, we would import { AssetManifest } from '../AssetManifest'. // Here we will inject logic to load it. For now, let's assume we can fetch it or it's hardcoded. // Load Manifest Images // Since we are using script tags in index.html, window.AssetManifest is already available! // REMOVED MANIFEST CHECK - FORCE LOAD ASSETS console.log('๐Ÿ“ฆ FORCE LOADING ASSETS (No Manifest Check)...'); // ๐ŸŽจ TILESET IMAGES console.log('๐Ÿ” LOADING REFERENCE GRASS: assets/slike/teren/grass_tile.png'); this.load.image('trava_osnova', 'assets/slike/teren/grass_tile.png'); this.load.image('tileset_grass', 'assets/slike/teren/grass_tile.png'); // Duplicate for safety this.load.image('tileset_dirt', 'assets/slike/teren/dirt.png'); this.load.image('tileset_water', 'assets/slike/teren/water.png'); this.load.image('tileset_stone', 'assets/slike/teren/stone.png'); // Legacy Keys this.load.image('tileset_Terrain_Grass', 'assets/slike/teren/grass_tile.png'); this.load.image('tileset_Terrain_Dirt', 'assets/slike/teren/dirt.png'); this.load.image('tileset_Terrain_Water', 'assets/slike/teren/water.png'); console.log('โœ… All Tilesets Preloaded!'); // MAP (Tiled JSON) // Found at: assets/maps/NovaFarma.json this.load.tilemapTiledJSON('NovaFarma', 'assets/maps/NovaFarma.json'); console.log('๐Ÿ—บ๏ธ Preloading NovaFarma.json map...'); } loadImageSafe(key, path) { try { this.load.image(key, path); } catch (error) { console.warn(`โš ๏ธ Image skipped: ${key}`); } } preloadAudio() { console.log('๐ŸŽต Preloading audio assets... (Minimal)'); // Audio paths are currently in flux. Most are disabled to prevent 404s. // Once audio reorganization is done, enable them here. // this.load.audio('background_music', 'assets/audio/bg_noir.mp3'); // ๐Ÿง›โ€โ™‚๏ธ GRONK (Verified Path: assets/slike/liki/gronk_pro_sheet.png) this.load.spritesheet('gronk_pro_sheet', 'assets/slike/liki/gronk_pro_sheet.png', { frameWidth: 128, frameHeight: 128 }); console.log('๐ŸŽต Audio preload queued (Minimal)'); } loadAudioSafe(key, path) { try { this.load.audio(key, path); } catch (error) { console.warn(`โš ๏ธ Audio skipped: ${key}`); } } createAnimations() { if (this.anims.exists('protagonist_walk')) return; // Zombie animations (only if texture exists) if (this.textures.exists('zombie_walk')) { this.anims.create({ key: 'zombie_walk_anim', frames: this.anims.generateFrameNumbers('zombie_walk', { start: 0, end: 5 }), frameRate: 8, repeat: -1 }); } else { console.warn('โš ๏ธ Texture "zombie_walk" not found - skipping animation'); } this.anims.create({ key: 'zombie_worker_walk', frames: this.anims.generateFrameNumbers('zombie_worker', { start: 0, end: 5 }), frameRate: 8, repeat: -1 }); // KRVAVA ลฝETEV: Protagonist 4-directional walking animations // Row 1: DOWN (frames 0-3) this.anims.create({ key: 'protagonist_walk_down', frames: this.anims.generateFrameNumbers('player_protagonist', { start: 0, end: 3 }), frameRate: 8, repeat: -1 }); // Row 2: LEFT (frames 4-7) this.anims.create({ key: 'protagonist_walk_left', frames: this.anims.generateFrameNumbers('player_protagonist', { start: 4, end: 7 }), frameRate: 8, repeat: -1 }); // Row 3: RIGHT (frames 8-11) this.anims.create({ key: 'protagonist_walk_right', frames: this.anims.generateFrameNumbers('player_protagonist', { start: 8, end: 11 }), frameRate: 8, repeat: -1 }); // Row 4: UP (frames 12-15) this.anims.create({ key: 'protagonist_walk_up', frames: this.anims.generateFrameNumbers('player_protagonist', { start: 12, end: 15 }), frameRate: 8, repeat: -1 }); // IDLE: Use first frame of each direction this.anims.create({ key: 'protagonist_idle_down', frames: [{ key: 'player_protagonist', frame: 0 }], frameRate: 1 }); this.anims.create({ key: 'protagonist_idle_left', frames: [{ key: 'player_protagonist', frame: 4 }], frameRate: 1 }); this.anims.create({ key: 'protagonist_idle_right', frames: [{ key: 'player_protagonist', frame: 8 }], frameRate: 1 }); this.anims.create({ key: 'protagonist_idle_up', frames: [{ key: 'player_protagonist', frame: 12 }], frameRate: 1 }); // ๐Ÿง›โ€โ™‚๏ธ GRONK ANIMATIONS // Down this.anims.create({ key: 'gronk_walk_down', frames: this.anims.generateFrameNumbers('gronk_pro_sheet', { start: 0, end: 3 }), frameRate: 8, repeat: -1 }); // Right this.anims.create({ key: 'gronk_walk_right', frames: this.anims.generateFrameNumbers('gronk_pro_sheet', { start: 4, end: 7 }), frameRate: 8, repeat: -1 }); // Up this.anims.create({ key: 'gronk_walk_up', frames: this.anims.generateFrameNumbers('gronk_pro_sheet', { start: 8, end: 11 }), frameRate: 8, repeat: -1 }); // Vape (Special) this.anims.create({ key: 'gronk_vape', frames: this.anims.generateFrameNumbers('gronk_pro_sheet', { start: 12, end: 15 }), frameRate: 8, repeat: 0 // Play once }); // Idle this.anims.create({ key: 'gronk_idle', // Default idle (Down) frameRate: 2, repeat: -1 }); console.log('๐ŸŽž๏ธ Animations created!'); } processAllTransparency() { // Process ALL sprites to remove backgrounds const spritesToProcess = [ 'player_sprite', 'zombie_sprite', 'merchant_sprite', 'house_sprite', 'stone_sprite', 'tree_sprite', 'grass_sprite', 'leaf_sprite', 'wheat_sprite', 'stone_texture', // New pixel art assets 'flowers', 'tree_green', 'tree_blue', 'tree_dead', 'rock_asset', // FINAL TREES 'tree_green_final', 'tree_blue_final', 'tree_dead_final', // STARDEW VALLEY FOREST TREES 'tree_purple', 'tree_apple', 'tree_pear', 'tree_cherry', 'tree_sapling', // NEW transparent assets 'tree_blue_new', 'tree_green_new', 'rock_1', 'rock_2', 'rock_small', 'merchant_new', 'elite_zombie', 'tree_dead_new', 'flowers_new', 'hill_sprite', 'fence', 'fence_old', 'gravestone', // City content 'chest', 'spawner', 'signpost_city', 'signpost_farm', 'signpost_both', // Voxel stil 'tree_voxel_green', 'tree_voxel_blue', 'tree_voxel_dead', 'rock_voxel', // NEW ISOMETRIC 2.5D ASSETS - Remove transparency 'barn_isometric', 'farmhouse_isometric', 'fence_isometric', 'bridge_isometric', 'blacksmith_workshop', 'ruins_building', 'soil_tilled', 'carrots_stages', 'flowers_pink_isometric', // NOTE: player_dreadlocks and zombie_worker are SPRITESHEETS - don't process! 'grave_zombie', // NEW FENCE PIECES 'fence_post', 'fence_horizontal', 'fence_vertical', 'fence_corner', // ANIMALS & NPCs 'chicken', 'cow', 'cow_mutant', 'elf', 'troll', 'villager', 'npc_merchant', 'npc_zombie', // STRUCTURES 'structure_house', 'wall_damaged', 'city_wall', // MISC OBJECTS 'wheat_sprite', 'grass_sprite', 'leaf_sprite', 'stone_sprite' // ๐ŸŒณ TREES REMOVED - already have transparent PNG! // (green removal would delete green leaves!) ]; spritesToProcess.forEach(spriteKey => { this.processSpriteTransparency(spriteKey); }); // ULTRA AGGRESSIVE: Fence Post only if (this.textures.exists('fence_post')) { this.ultraRemoveBackground('fence_post'); } // ULTRA REMOVED - new tree sprites already have transparency! // if (this.textures.exists('cesnjevo_drevo')) { // this.ultraRemoveBackground('cesnjevo_drevo'); // } console.log('โœ… All sprites transparency processed!'); } processSpriteTransparency(spriteKey) { if (!this.textures.exists(spriteKey)) return; const texture = this.textures.get(spriteKey); const source = texture.getSourceImage(); // Create canvas to process image const canvas = document.createElement('canvas'); canvas.width = source.width; canvas.height = source.height; const ctx = canvas.getContext('2d', { willReadFrequently: true }); // Draw original image ctx.drawImage(source, 0, 0); // Get image data const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // Remove backgrounds - ULTRA AGGRESSIVE MODE! for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const a = data[i + 3]; // Skip if already transparent if (a === 0) continue; const brightness = (r + g + b) / 3; // ๐ŸŸข GREEN REMOVAL - Only BRIGHT greens! // Pink (255,105,180) has g=105 - MUST KEEP! // Bright green (0,255,0) has g=255 - REMOVE! const isGreen = ( g > 180 && // Only very bright green g > r * 1.3 && // Green dominates red g > b * 1.3 // Green dominates blue ); if (isGreen) { data[i + 3] = 0; continue; } // 1. Remove ALL grayscale colors (ANY shade of gray) const isGrayscale = Math.abs(r - g) < 20 && Math.abs(g - b) < 20 && Math.abs(r - b) < 20; if (isGrayscale && brightness > 80) { data[i + 3] = 0; // Make transparent continue; } // 2. Remove ALL light/bright backgrounds (AI-generated sprites) if (brightness > 150) { data[i + 3] = 0; // Make transparent continue; } // 3. Remove PURE WHITE if (r > 240 && g > 240 && b > 240) { data[i + 3] = 0; continue; } // 4. Remove OFF-WHITE / CREAM / BEIGE if (brightness > 200 && Math.abs(r - g) < 30 && Math.abs(g - b) < 30) { data[i + 3] = 0; continue; } // 5. Remove PASTEL colors (low saturation, high brightness) const maxRGB = Math.max(r, g, b); const minRGB = Math.min(r, g, b); const saturation = maxRGB === 0 ? 0 : (maxRGB - minRGB) / maxRGB; if (saturation < 0.3 && brightness > 120) { data[i + 3] = 0; // Remove low-saturation backgrounds continue; } // Special: Remove brown/tan backgrounds (merchant sprite) if (spriteKey === 'merchant_sprite') { // Brown detection: R > G > B, warm tones const isBrown = r > 100 && r > g && g > b && (r - b) > 40; if (isBrown) { data[i + 3] = 0; } } } // Put processed data back ctx.putImageData(imageData, 0, 0); // Create new texture from processed canvas this.textures.remove(spriteKey); this.textures.addCanvas(spriteKey, canvas); } createLoadingBar() { const width = this.cameras.main.width; const height = this.cameras.main.height; // Warm background (Stardew Valley style) const bg = this.add.graphics(); bg.fillStyle(0x2d1b00, 1); bg.fillRect(0, 0, width, height); // Simple "LOADING" text const title = this.add.text(width / 2, height / 2 - 80, 'LOADING', { fontFamily: 'Georgia, serif', fontSize: '36px', fontStyle: 'bold', fill: '#f4e4c1', stroke: '#2d1b00', strokeThickness: 4 }).setOrigin(0.5); // Progress Bar container (wooden style) const barWidth = 400; const barHeight = 30; const barX = width / 2 - barWidth / 2; const barY = height / 2; const progressBox = this.add.graphics(); progressBox.fillStyle(0x4a3520, 0.9); progressBox.fillRoundedRect(barX, barY, barWidth, barHeight, 5); progressBox.lineStyle(3, 0xd4a574, 1); progressBox.strokeRoundedRect(barX, barY, barWidth, barHeight, 5); const progressBar = this.add.graphics(); // Zombie sprite walking on the bar const zombie = this.add.text(barX, barY + barHeight / 2, '๐ŸงŸ', { fontSize: '32px' }).setOrigin(0.5); // Percentage text const percentText = this.add.text(width / 2, barY + barHeight / 2, '0%', { font: '16px Georgia', fill: '#f4e4c1', fontStyle: 'bold' }).setOrigin(0.5); this.load.on('progress', (value) => { percentText.setText(parseInt(value * 100) + '%'); progressBar.clear(); progressBar.fillStyle(0x6b4423, 1); // Smooth fill const w = (barWidth - 10) * value; if (w > 0) { progressBar.fillRoundedRect(barX + 5, barY + 5, w, barHeight - 10, 2); } // Move zombie along the bar (moderate speed) const zombieX = barX + (barWidth * value); zombie.setX(zombieX); // Gentle bounce animation const bounce = Math.sin(value * 20) * 3; zombie.setY(barY + barHeight / 2 + bounce); }); this.load.on('complete', () => { // Fade out animation this.tweens.add({ targets: [progressBar, progressBox, percentText, title, zombie, bg], alpha: 0, duration: 800, onComplete: () => { progressBar.destroy(); progressBox.destroy(); percentText.destroy(); title.destroy(); zombie.destroy(); bg.destroy(); // Ensure bg is destroyed here console.log('โœ… PreloadScene: Assets loaded!'); // ๐ŸŒ INITIALIZE LOCALIZATION if (!window.i18n) { try { window.i18n = new LocalizationSystem(); const locData = this.cache.json.get('localization'); if (locData) { window.i18n.setExternalData(locData); console.log('๐ŸŒ Localization initialized with external data'); } else { console.warn('โš ๏ธ Localization JSON not found in cache'); } } catch (e) { console.error('โŒ Localization init failed:', e); } } window.gameState.currentScene = 'PreloadScene'; // โœ… Starting INTRO SEQUENCE (NEW! Jan 10, 2026) this.time.delayedCall(500, () => { console.log('๐ŸŽฌ Starting GameScene (DEMO MODE - SKIP INTRO)...'); this.scene.start('MainMenuScene'); // ๐ŸŽฎ MENU START }); } }); }); } create() { console.log('๐ŸŽต Starting Background Music Loop...'); // try { // if (!this.sound.get('background_music')) { // this.sound.play('background_music', { loop: true, volume: 0.5 }); // } else if (!this.sound.get('background_music').isPlaying) { // this.sound.get('background_music').play(); // } // } catch (e) { // console.warn('๐ŸŽต Audio Playback Failed (Autoplay policy?):', e); // } // The actual scene start logic has been moved to the 'load.on(complete)' callback // to ensure all assets are fully loaded and the loading bar has faded out. // This 'create' method now primarily serves as a placeholder or for any // immediate setup that doesn't depend on asset loading completion. } processPlayerSpritesheet() { const spriteKey = 'player_protagonist'; if (!this.textures.exists(spriteKey)) { console.warn('โš ๏ธ Player protagonist texture not found!'); return; } console.log('๐ŸŽจ Processing player spritesheet transparency...'); const texture = this.textures.get(spriteKey); const source = texture.getSourceImage(); const canvas = document.createElement('canvas'); canvas.width = source.width; canvas.height = source.height; const ctx = canvas.getContext('2d', { willReadFrequently: true }); ctx.drawImage(source, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // ULTRA AGGRESSIVE: Remove ALL checkerboard patterns and gray backgrounds for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const a = data[i + 3]; // Skip if already transparent if (a === 0) continue; // 1. Remove PERFECT GRAY (checkerboard pattern: RGB 204,204,204 and 153,153,153) if (r === g && g === b) { if (r === 204 || r === 153 || r === 192 || r === 128 || (r >= 180 && r <= 210)) { data[i + 3] = 0; continue; } } // 2. Remove ANY grayscale (Rโ‰ˆGโ‰ˆB within 15 units) const isGrayscale = Math.abs(r - g) < 15 && Math.abs(g - b) < 15 && Math.abs(r - b) < 15; const brightness = (r + g + b) / 3; if (isGrayscale && brightness > 100) { data[i + 3] = 0; continue; } // 3. Remove LIGHT backgrounds (brightness > 150) if (brightness > 150) { data[i + 3] = 0; continue; } // 4. Keep ONLY colored pixels (character skin, clothing, hair) // Character has: blue hoodie, brown pants, brown skin const maxChannel = Math.max(r, g, b); const minChannel = Math.min(r, g, b); const saturation = maxChannel === 0 ? 0 : (maxChannel - minChannel) / maxChannel; // If low saturation AND bright = background if (saturation < 0.15 && brightness > 80) { data[i + 3] = 0; continue; } } ctx.putImageData(imageData, 0, 0); // Replace texture this.textures.remove(spriteKey); this.textures.addCanvas(spriteKey, canvas); console.log('โœ… Player spritesheet transparency processed!'); } ultraRemoveBackground(spriteKey) { if (!this.textures.exists(spriteKey)) return; const texture = this.textures.get(spriteKey); const source = texture.getSourceImage(); const canvas = document.createElement('canvas'); canvas.width = source.width; canvas.height = source.height; const ctx = canvas.getContext('2d', { willReadFrequently: true }); ctx.drawImage(source, 0, 0); const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // Different logic for different sprites if (spriteKey === 'cesnjevo_drevo') { // CHERRY BLOSSOM: Keep only PINK (flowers) and BROWN (trunk) for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; // Pink detection: R > G, R > B, pinkish tones const isPink = r > 100 && r > g && r > b && (r - g) > 30; // Brown detection: R > G > B, warm earthy tones const isBrown = r > g && g > b && r > 80 && r < 200; // Keep pink OR brown, remove everything else if (!isPink && !isBrown) { data[i + 3] = 0; // Make transparent } } } else { // FALLBACK: Fence post logic (keep only brown) for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; // Keep only brown/wood colors: R > G > B and R is dominant const isBrown = r > g && g > b && r > 80 && r < 200; if (!isBrown) { data[i + 3] = 0; // Make transparent } } } ctx.putImageData(imageData, 0, 0); // Remove old texture and create new one from canvas this.textures.remove(spriteKey); this.textures.addCanvas(spriteKey, canvas); console.log(`๐Ÿ”ฅ ULTRA removed background from ${spriteKey}`); } }