// Flat2DTerrainSystem - Complete 2D top-down tile rendering // Replaces isometric TerrainSystem for Stardew Valley style class Flat2DTerrainSystem { constructor(scene) { this.scene = scene; this.tileSize = 48; this.width = 500; // 🌍 PHASE 28: Expanded to 500x500! this.height = 500; // Tile map data this.tiles = []; // Rendering containers this.groundLayer = null; this.pathsLayer = null; this.decorLayer = null; // Decoration tracking (for interaction system) this.decorationsMap = new Map(); // Textures ready flag this.texturesReady = false; // 🌍 PHASE 28: Biome support this.biomeSystem = null; // Will be set by GameScene this.chunkManager = null; // Will be set by GameScene console.log('🎨 Flat2DTerrainSystem initialized (500x500 world)'); } async generate() { console.log('πŸ—ΊοΈ Generating flat 2D map...'); // Create textures first this.createTileTextures(); // 🌍 PHASE 28: Use BiomeSystem if available if (this.biomeSystem && this.chunkManager) { console.log('🌍 Using BiomeSystem for chunk-based terrain generation'); // Biome-based generation will happen via ChunkManager // No need to generate full map here! // Create simple grass background as fallback this.createBiomeBackground(); } else { // Fallback to old Map2DData system console.log('πŸ“Š Using Map2DData for terrain generation (fallback)'); // Load map data if (typeof Map2DData !== 'undefined') { this.tiles = Map2DData.generateMap(); console.log('βœ… Map data generated:', this.tiles.length, 'rows'); } else { console.error('❌ Map2DData not loaded!'); this.createFallbackMap(); } // Render the old-style map this.renderMap(); } console.log('βœ… Flat 2D map ready!'); } // 🌍 PHASE 28: Create simple biome-aware background createBiomeBackground() { const size = this.tileSize; const mapWidth = this.width * size; const mapHeight = this.height * size; console.log('🎨 Creating biome-aware background...'); // Create solid background (will be covered by chunks) const background = this.scene.add.rectangle(0, 0, mapWidth, mapHeight, 0x3CB371); background.setOrigin(0, 0); background.setDepth(0); // Create containers for chunks this.pathsLayer = this.scene.add.container(0, 0); this.pathsLayer.setDepth(2); this.decorLayer = this.scene.add.container(0, 0); this.decorLayer.setDepth(3); this.groundLayer = background; console.log('βœ… Biome background created, ready for chunks'); } createTileTextures() { // 🎨 Create SOLID, OPAQUE tiles (no transparency!) const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false }); const size = this.tileSize; // Check if PNG tilesets are loaded const hasGrass = this.scene.textures.exists('tileset_grass'); const hasWater = this.scene.textures.exists('tileset_water'); const hasDirt = this.scene.textures.exists('tileset_dirt'); if (hasGrass && hasWater && hasDirt) { console.log('βœ… PNG Tilesets found! Creating beautiful tiles...'); // 🌍 PHASE 28: Create BIOME-SPECIFIC GRASS TILES // GRASSLAND - SUPER VIBRANT GREEN! 🌿 graphics.clear(); graphics.fillStyle(0x3CB371, 1.0); // Medium sea green graphics.fillRect(0, 0, size, size); // Darker spots for contrast for (let i = 0; i < 20; i++) { graphics.fillStyle(0x2E8B57, 0.5); graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 4); } // Lighter highlights for (let i = 0; i < 15; i++) { graphics.fillStyle(0x90EE90, 0.4); graphics.fillCircle(Math.random() * size, Math.random() * size, 1.5); } graphics.generateTexture('tile2d_grass', size, size); // FOREST BIOME - DARK GREEN graphics.clear(); graphics.fillStyle(0x2d5016, 1.0); // Dark green graphics.fillRect(0, 0, size, size); for (let i = 0; i < 25; i++) { graphics.fillStyle(0x1a3010, 0.4); graphics.fillCircle(Math.random() * size, Math.random() * size, 3); } graphics.generateTexture('tile2d_forest', size, size); // DESERT BIOME - TAN/SAND graphics.clear(); graphics.fillStyle(0xd4c4a1, 1.0); // Sand color graphics.fillRect(0, 0, size, size); for (let i = 0; i < 20; i++) { graphics.fillStyle(0xc4b491, 0.3); graphics.fillCircle(Math.random() * size, Math.random() * size, 4); } graphics.generateTexture('tile2d_desert', size, size); // MOUNTAIN BIOME - GRAY STONE graphics.clear(); graphics.fillStyle(0x808080, 1.0); // Gray graphics.fillRect(0, 0, size, size); for (let i = 0; i < 30; i++) { graphics.fillStyle(0x606060, 0.5); graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 3); } graphics.generateTexture('tile2d_mountain', size, size); // SWAMP BIOME - DARK GREEN/BROWN graphics.clear(); graphics.fillStyle(0x3d5a3d, 1.0); // Murky green graphics.fillRect(0, 0, size, size); for (let i = 0; i < 20; i++) { graphics.fillStyle(0x2d4a2d, 0.6); graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 4); } graphics.generateTexture('tile2d_swamp', size, size); // GRASS WITH FLOWERS (VIBRANT!) graphics.clear(); graphics.fillStyle(0x3CB371, 1.0); graphics.fillRect(0, 0, size, size); for (let i = 0; i < 12; i++) { graphics.fillStyle(0x2E8B57, 0.3); graphics.fillCircle(Math.random() * size, Math.random() * size, 2); } // BRIGHT colorful flowers! const flowerColors = [0xFF1493, 0xFFD700, 0x00BFFF, 0xFF69B4]; for (let i = 0; i < 4; i++) { graphics.fillStyle(flowerColors[Math.floor(Math.random() * 4)], 1.0); graphics.fillCircle(Math.random() * size, Math.random() * size, 3); } graphics.generateTexture('tile2d_grass_flowers', size, size); // DIRT - RICH BROWN! 🟀 graphics.clear(); graphics.fillStyle(0x8B4513, 1.0); // Saddle brown graphics.fillRect(0, 0, size, size); for (let i = 0; i < 25; i++) { graphics.fillStyle(0x654321, 0.5); graphics.fillCircle(Math.random() * size, Math.random() * size, 3 + Math.random() * 5); } for (let i = 0; i < 15; i++) { graphics.fillStyle(0xA0826D, 0.4); graphics.fillCircle(Math.random() * size, Math.random() * size, 2); } graphics.generateTexture('tile2d_dirt', size, size); graphics.clear(); graphics.fillStyle(0x8b4513, 1.0); graphics.fillRect(0, 0, size, size); graphics.fillStyle(0x3CB371, 0.3); graphics.fillRect(0, 0, size, size); graphics.generateTexture('tile2d_dirt_edge', size, size); // WATER - BRIGHT TEAL! πŸ’§ (WITH OUTLINE!) graphics.clear(); graphics.fillStyle(0x20B2AA, 1.0); // Light sea green - vibrant teal! graphics.fillRect(0, 0, size, size); // Dark outline border (2D style!) graphics.lineStyle(2, 0x006994, 0.8); graphics.strokeRect(1, 1, size - 2, size - 2); for (let i = 0; i < 10; i++) { graphics.fillStyle(0x008B8B, 0.4); graphics.fillCircle(Math.random() * size, Math.random() * size, 5 + Math.random() * 8); } for (let i = 0; i < 15; i++) { graphics.fillStyle(0x48D1CC, 0.5); graphics.fillCircle(Math.random() * size, Math.random() * size, 2); } for (let i = 0; i < 12; i++) { graphics.fillStyle(0xFFFFFF, 0.6); graphics.fillCircle(Math.random() * size, Math.random() * size, 1); } graphics.generateTexture('tile2d_water', size, size); graphics.clear(); graphics.fillStyle(0x20B2AA, 1.0); graphics.fillRect(0, 0, size, size); graphics.fillStyle(0x48D1CC, 0.5); graphics.fillRect(0, 0, size, size); graphics.generateTexture('tile2d_water_edge', size, size); } else { console.warn('⚠️ PNG Tilesets not loaded! Using fallback colors'); } // STONE (FULLY OPAQUE!) graphics.clear(); graphics.fillStyle(0x808080, 1.0); // 100% opacity! graphics.fillRect(0, 0, size, size); for (let i = 0; i < 12; i++) { graphics.fillStyle(0x606060, 0.6); graphics.fillCircle(Math.random() * size, Math.random() * size, 2 + Math.random() * 3); } graphics.generateTexture('tile2d_stone', size, size); graphics.destroy(); this.texturesReady = true; console.log('βœ… Tile textures created - FULLY OPAQUE!'); } extractTilesFromTileset(sourceKey, targetKey, tileX, tileY, tileSize) { // Extract a single tile from tileset PNG and create new texture if (!this.scene.textures.exists(sourceKey)) { console.warn(`⚠️ Tileset ${sourceKey} not found, using fallback`); return; } const source = this.scene.textures.get(sourceKey).getSourceImage(); const canvas = document.createElement('canvas'); canvas.width = tileSize; canvas.height = tileSize; const ctx = canvas.getContext('2d'); // Draw specific tile from tileset ctx.drawImage( source, tileX * tileSize, tileY * tileSize, // Source position tileSize, tileSize, // Source size 0, 0, // Dest position tileSize, tileSize // Dest size ); // Create new texture from extracted tile this.scene.textures.addCanvas(targetKey, canvas); } renderMap() { // 🎨 SIMPLE & CLEAN: Use TileSprite for seamless background! const size = this.tileSize; const mapWidth = this.width * size; const mapHeight = this.height * size; console.log('🎨 Rendering seamless 2D map...'); // Create solid grass background (NO TRANSPARENCY!) const grassBG = this.scene.add.tileSprite(0, 0, mapWidth, mapHeight, 'tile2d_grass'); grassBG.setOrigin(0, 0); grassBG.setDepth(1); // Create containers for paths and decorations this.pathsLayer = this.scene.add.container(0, 0); this.pathsLayer.setDepth(2); this.decorLayer = this.scene.add.container(0, 0); this.decorLayer.setDepth(3); const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false }); // Array to store water sprites for animation this.waterSprites = []; // Render all tiles on top of grass background for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { const tile = this.tiles[y][x]; const worldX = x * size; const worldY = y * size; // WATER tiles - back to Image, animate with tint! if (tile.base === 4 || tile.base === 5) { const waterSprite = this.scene.add.image(worldX, worldY, 'tile2d_water'); waterSprite.setOrigin(0, 0); waterSprite.setDisplaySize(size, size); this.pathsLayer.add(waterSprite); this.waterSprites.push(waterSprite); // Store for animation! } // DIRT PATH tiles else if (tile.base === 2 || tile.base === 3) { const dirtSprite = this.scene.add.image(worldX, worldY, 'tile2d_dirt'); dirtSprite.setOrigin(0, 0); dirtSprite.setDisplaySize(size, size); // Edge blending if (tile.base === 3) { dirtSprite.setAlpha(0.8); // Blend with grass } this.pathsLayer.add(dirtSprite); } // DECORATIONS if (tile.decoration) { this.addDecoration(x, y, tile.decoration); } } } graphics.destroy(); // Store reference this.groundLayer = grassBG; console.log('βœ… Seamless map rendered (NO GRID!)'); console.log(`🌊 Water tiles animated: ${this.waterSprites.length}`); } // 🌍 PHASE 28: Render a single chunk with biome support renderChunk(chunk) { if (!chunk || !this.biomeSystem) { console.warn('⚠️ Cannot render chunk: missing data or biomeSystem'); return; } const size = this.tileSize; // Render ground tiles based on biome for (const tileData of chunk.tiles) { const { x, y, biome } = tileData; const worldX = x * size; const worldY = y * size; // Get biome-specific tile texture let tileTexture = 'tile2d_grass'; // Default if (biome === 'forest') { tileTexture = 'tile2d_forest'; } else if (biome === 'desert') { tileTexture = 'tile2d_desert'; } else if (biome === 'mountain') { tileTexture = 'tile2d_mountain'; } else if (biome === 'swamp') { tileTexture = 'tile2d_swamp'; } // Create tile sprite const tileSprite = this.scene.add.image(worldX, worldY, tileTexture); tileSprite.setOrigin(0, 0); tileSprite.setDisplaySize(size, size); tileSprite.setDepth(1); // Store in chunk for cleanup if (!chunk.sprites) chunk.sprites = []; chunk.sprites.push(tileSprite); // Apply biome features (trees, rocks, etc.) if (this.biomeSystem) { const features = this.biomeSystem.applyBiomeFeatures(x, y); features.forEach(feature => { this.addBiomeFeature(x, y, feature, chunk); }); } } console.log(`βœ… Chunk (${chunk.chunkX}, ${chunk.chunkY}) rendered with biomes`); // πŸ› DEBUG: Add visible border around chunk if (this.scene.add) { const chunkWorldX = chunk.chunkX * (this.chunkSize * size); const chunkWorldY = chunk.chunkY * (this.chunkSize * size); const chunkPixelSize = this.chunkSize * size; const border = this.scene.add.rectangle( chunkWorldX, chunkWorldY, chunkPixelSize, chunkPixelSize ); border.setOrigin(0, 0); border.setStrokeStyle(2, 0xFF0000, 0.5); // Red semi-transparent border border.setDepth(100); // High depth to see it border.setFillStyle(0x000000, 0); // No fill if (!chunk.sprites) chunk.sprites = []; chunk.sprites.push(border); console.log(`πŸ”² Debug border added at (${chunkWorldX}, ${chunkWorldY})`); } } // Add biome-specific feature addBiomeFeature(gridX, gridY, feature, chunk) { const size = this.tileSize; const worldX = gridX * size + size / 2; const worldY = gridY * size + size / 2; let sprite; switch (feature.type) { case 'tree': sprite = this.createTree(worldX, worldY); break; case 'rock': sprite = this.createRock(worldX, worldY, feature.size); break; case 'bush': sprite = this.createBush(worldX, worldY); break; case 'cactus': sprite = this.createCactus(worldX, worldY); break; case 'deadTree': sprite = this.createDeadTree(worldX, worldY); break; case 'boulder': sprite = this.createBoulder(worldX, worldY); break; case 'mushroom': sprite = this.createMushroom(worldX, worldY); break; case 'vine': sprite = this.createVine(worldX, worldY); break; } if (sprite && chunk) { sprite.setDepth(10 + worldY); if (!chunk.sprites) chunk.sprites = []; chunk.sprites.push(sprite); } } // Simple rock creation createRock(x, y, size = 'small') { const graphics = this.scene.add.graphics(); const rockSize = size === 'large' ? 15 : 8; graphics.fillStyle(0x808080); graphics.fillCircle(x, y, rockSize); graphics.fillStyle(0x606060, 0.6); graphics.fillCircle(x - 3, y - 3, rockSize * 0.6); return graphics; } // Boulder (large rock) createBoulder(x, y) { const graphics = this.scene.add.graphics(); graphics.fillStyle(0x707070); graphics.fillCircle(x, y, 20); graphics.fillStyle(0x505050); graphics.fillCircle(x - 5, y - 5, 12); graphics.fillStyle(0x909090, 0.5); graphics.fillCircle(x + 5, y + 5, 8); return graphics; } // Cactus createCactus(x, y) { const graphics = this.scene.add.graphics(); // Main body graphics.fillStyle(0x4a7c59); graphics.fillRect(x - 5, y - 15, 10, 30); // Arms graphics.fillRect(x - 12, y - 8, 7, 10); graphics.fillRect(x + 5, y - 5, 7, 10); // Highlights graphics.fillStyle(0x5a8c69, 0.6); graphics.fillRect(x - 3, y - 12, 3, 24); return graphics; } // Mushroom createMushroom(x, y) { const graphics = this.scene.add.graphics(); // Stem graphics.fillStyle(0xeeeeee); graphics.fillRect(x - 2, y, 4, 8); // Cap graphics.fillStyle(0xd63031); graphics.fillCircle(x, y, 6); // Spots graphics.fillStyle(0xffffff); graphics.fillCircle(x - 3, y - 1, 1.5); graphics.fillCircle(x + 2, y + 1, 1.2); return graphics; } // Vine (swamp feature) createVine(x, y) { const graphics = this.scene.add.graphics(); graphics.lineStyle(2, 0x2d4a2d); graphics.beginPath(); graphics.moveTo(x, y - 10); graphics.bezierCurveTo( x + 5, y - 5, x - 5, y + 5, x, y + 10 ); graphics.strokePath(); return graphics; } getTileTexture(tileType) { const types = Map2DData.tileTypes; switch (tileType) { case types.GRASS: return 'tile2d_grass'; case types.GRASS_FLOWERS: return 'tile2d_grass_flowers'; case types.DIRT: return 'tile2d_dirt'; case types.DIRT_EDGE: return 'tile2d_dirt_edge'; case types.WATER: return 'tile2d_water'; case types.WATER_EDGE: return 'tile2d_water_edge'; case types.STONE: return 'tile2d_stone'; default: return 'tile2d_grass'; } } addDecoration(gridX, gridY, decorType) { const size = this.tileSize; const worldX = gridX * size + size / 2; const worldY = gridY * size + size / 2; const types = Map2DData.tileTypes; let sprite; switch (decorType) { case types.TREE: sprite = this.createTree(worldX, worldY); break; case types.FLOWER_RED: sprite = this.createFlower(worldX, worldY, 0xff6b6b); break; case types.FLOWER_YELLOW: sprite = this.createFlower(worldX, worldY, 0xffd93d); break; case types.FLOWER_BLUE: sprite = this.createFlower(worldX, worldY, 0x6bcbff); break; case types.LILY_PAD: sprite = this.createLilyPad(worldX, worldY); break; case types.BUSH: sprite = this.createBush(worldX, worldY); break; case 'puddle': sprite = this.createPuddle(worldX, worldY); break; } if (sprite) { // 🎨 2.5D DEPTH SORTING - Y position = depth! // Objects lower on screen appear in front sprite.setDepth(10 + worldY); this.decorLayer.add(sprite); } } createTree(x, y) { // 🌳 TREE VARIETY SYSTEM! const treeTypes = ['cherry', 'oak', 'pine', 'dead', 'apple']; const randomType = Phaser.Utils.Array.GetRandom(treeTypes); switch (randomType) { case 'oak': return this.createOakTree(x, y); case 'pine': return this.createPineTree(x, y); case 'dead': return this.createDeadTree(x, y); case 'apple': return this.createAppleTree(x, y); default: return this.createCherryTree(x, y); } } createCherryTree(x, y) { // 🌸 PNG SPRITE! if (this.scene.textures.exists('tree_cherry')) { const tree = this.scene.add.image(x, y, 'tree_cherry'); const scale = Phaser.Math.FloatBetween(0.4, 0.6); // SMALLER! (was 0.8-1.2) tree.setScale(scale); tree.setOrigin(0.5, 0.85); // Shadow const shadow = this.scene.add.ellipse(x, y + 15, 30 * scale, 10, 0x000000, 0.2); shadow.setDepth(tree.depth - 1); return tree; } // FALLBACK: Procedural return this.createProceduralCherryTree(x, y); } createProceduralCherryTree(x, y) { // Old procedural code (backup) const graphics = this.scene.add.graphics(); const growthScale = Phaser.Math.FloatBetween(0.8, 1.4); // BIGGER! const heightOffset = -20 * growthScale; const crownSize = 30 * growthScale; // MUCH BIGGER! // Shadow (ellipse on ground) graphics.fillStyle(0x000000, 0.2); graphics.fillEllipse(x, y + 30, 40 * growthScale, 12); // Bigger shadow // Trunk (with shading for volume) const trunkW = 12 * growthScale; // Thicker const trunkH = 30 * growthScale; // Taller // Dark side (right) graphics.fillStyle(0x6D3F1A); graphics.fillRect(x + trunkW / 4, y + 10 + heightOffset, trunkW / 2, trunkH); // Light side (left) graphics.fillStyle(0x8B5A2B); graphics.fillRect(x - trunkW / 2, y + 10 + heightOffset, trunkW / 2, trunkH); // CROWN - Layered circles for 2.5D! const crownY = y - 10 * growthScale + heightOffset; // Higher crown // Dark base (shadow underneath) graphics.fillStyle(0xE75480); graphics.fillCircle(x + 4 * growthScale, crownY + 4 * growthScale, crownSize); // Main pink crown graphics.fillStyle(0xFF69B4); graphics.fillCircle(x, crownY, crownSize); // Top highlight (light from top-left) graphics.fillStyle(0xFFB6C1, 0.8); graphics.fillCircle(x - 6 * growthScale, crownY - 6 * growthScale, crownSize * 0.6); // Bright specular spot graphics.fillStyle(0xFFFFFF, 0.5); graphics.fillCircle(x - 8 * growthScale, crownY - 8 * growthScale, crownSize * 0.3); return graphics; } createOakTree(x, y) { // 🌲 PNG SPRITE! if (this.scene.textures.exists('tree_oak')) { const tree = this.scene.add.image(x, y, 'tree_oak'); const scale = Phaser.Math.FloatBetween(0.45, 0.65); // SMALLER! tree.setScale(scale); tree.setOrigin(0.5, 0.85); const shadow = this.scene.add.ellipse(x, y + 15, 35 * scale, 12, 0x000000, 0.2); shadow.setDepth(tree.depth - 1); return tree; } // FALLBACK: const graphics = this.scene.add.graphics(); const growthScale = Phaser.Math.FloatBetween(0.8, 1.4); // BIGGER! // Shadow graphics.fillStyle(0x000000, 0.15); graphics.fillEllipse(x, y + 24, 25 * growthScale, 8); // Thick trunk graphics.fillStyle(0x654321); graphics.fillRect(x - 5 * growthScale, y, 10 * growthScale, 24 * growthScale); // Large round crown graphics.fillStyle(0x228B22); // Forest green graphics.fillCircle(x, y - 10 * growthScale, 18 * growthScale); // Darker outline graphics.lineStyle(2, 0x006400); graphics.strokeCircle(x, y - 10 * growthScale, 18 * growthScale); // Highlight graphics.fillStyle(0x32CD32, 0.6); graphics.fillCircle(x - 5 * growthScale, y - 15 * growthScale, 8 * growthScale); return graphics; } createPineTree(x, y) { // 🌲 PNG SPRITE! if (this.scene.textures.exists('tree_pine')) { const tree = this.scene.add.image(x, y, 'tree_pine'); const scale = Phaser.Math.FloatBetween(0.45, 0.7); // SMALLER! tree.setScale(scale); tree.setOrigin(0.5, 0.9); // Taller const shadow = this.scene.add.ellipse(x, y + 20, 30 * scale, 10, 0x000000, 0.2); shadow.setDepth(tree.depth - 1); return tree; } // FALLBACK: const graphics = this.scene.add.graphics(); const growthScale = Phaser.Math.FloatBetween(0.8, 1.5); // BIGGER (Pine taller) // Shadow graphics.fillStyle(0x000000, 0.15); graphics.fillEllipse(x, y + 20, 15 * growthScale, 6); // Trunk graphics.fillStyle(0x8B4513); graphics.fillRect(x - 3 * growthScale, y + 5, 6 * growthScale, 15 * growthScale); // Stacked triangles (pine shape) graphics.fillStyle(0x2F4F2F); // Dark green // Bottom tier graphics.fillTriangle( x, y - 5 * growthScale, x - 14 * growthScale, y + 10, x + 14 * growthScale, y + 10 ); // Middle tier graphics.fillTriangle( x, y - 15 * growthScale, x - 10 * growthScale, y, x + 10 * growthScale, y ); // Top tier graphics.fillTriangle( x, y - 25 * growthScale, x - 7 * growthScale, y - 10 * growthScale, x + 7 * growthScale, y - 10 * growthScale ); return graphics; } createDeadTree(x, y) { // πŸ’€ PNG SPRITE! if (this.scene.textures.exists('tree_dead')) { const tree = this.scene.add.image(x, y, 'tree_dead'); const scale = Phaser.Math.FloatBetween(0.35, 0.55); // SMALLER! tree.setScale(scale); tree.setOrigin(0.5, 0.85); const shadow = this.scene.add.ellipse(x, y + 18, 28 * scale, 10, 0x000000, 0.3); shadow.setDepth(tree.depth - 1); return tree; } // FALLBACK: const graphics = this.scene.add.graphics(); const growthScale = Phaser.Math.FloatBetween(0.7, 1.2); // BIGGER! // Shadow graphics.fillStyle(0x000000, 0.2); graphics.fillEllipse(x, y + 20, 18 * growthScale, 6); // Dark trunk graphics.fillStyle(0x3D3D3D); graphics.fillRect(x - 4 * growthScale, y, 8 * growthScale, 22 * growthScale); // Branches (bare) graphics.lineStyle(3 * growthScale, 0x2D2D2D); graphics.beginPath(); graphics.moveTo(x, y - 5 * growthScale); graphics.lineTo(x - 10 * growthScale, y - 15 * growthScale); graphics.moveTo(x, y - 8 * growthScale); graphics.lineTo(x + 12 * growthScale, y - 12 * growthScale); graphics.strokePath(); return graphics; } createAppleTree(x, y) { // 🍎 PNG SPRITE! if (this.scene.textures.exists('tree_apple')) { const tree = this.scene.add.image(x, y, 'tree_apple'); const scale = Phaser.Math.FloatBetween(0.4, 0.6); // SMALLER! tree.setScale(scale); tree.setOrigin(0.5, 0.85); const shadow = this.scene.add.ellipse(x, y + 16, 32 * scale, 11, 0x000000, 0.2); shadow.setDepth(tree.depth - 1); return tree; } // FALLBACK: const graphics = this.scene.add.graphics(); const growthScale = Phaser.Math.FloatBetween(0.8, 1.3); // BIGGER! // Shadow graphics.fillStyle(0x000000, 0.15); graphics.fillEllipse(x, y + 22, 22 * growthScale, 7); // Trunk graphics.fillStyle(0x8B4513); graphics.fillRect(x - 4 * growthScale, y + 2, 8 * growthScale, 20 * growthScale); // Green crown graphics.fillStyle(0x3CB371); // Medium sea green graphics.fillCircle(x, y - 8 * growthScale, 16 * growthScale); // Apples (red circles) graphics.fillStyle(0xFF0000); const applePositions = [ { x: -8, y: -12 }, { x: 5, y: -10 }, { x: -3, y: -5 }, { x: 8, y: -8 }, { x: 0, y: -15 } ]; applePositions.forEach(pos => { graphics.fillCircle( x + pos.x * growthScale, y + pos.y * growthScale, 3 * growthScale ); }); return graphics; } createFlower(x, y, color) { const graphics = this.scene.add.graphics(); // Petals graphics.fillStyle(color); for (let i = 0; i < 5; i++) { const angle = (Math.PI * 2 * i) / 5; const px = x + Math.cos(angle) * 3; const py = y + Math.sin(angle) * 3; graphics.fillCircle(px, py, 2); } // Center graphics.fillStyle(0xFFEB3B); graphics.fillCircle(x, y, 2); return graphics; } createLilyPad(x, y) { const graphics = this.scene.add.graphics(); // Lily pad (green circle) graphics.fillStyle(0x4a8d2f); graphics.fillCircle(x, y, 8); // Pink flower graphics.fillStyle(0xFF69B4); for (let i = 0; i < 5; i++) { const angle = (Math.PI * 2 * i) / 5; const px = x + Math.cos(angle) * 3; const py = y + Math.sin(angle) * 3; graphics.fillCircle(px, py, 2); } graphics.fillStyle(0xFFD700); graphics.fillCircle(x, y, 1.5); return graphics; } createBush(x, y) { const graphics = this.scene.add.graphics(); graphics.fillStyle(0x3a6b1f, 0.9); graphics.fillCircle(x, y, 10); graphics.fillCircle(x - 6, y + 2, 8); graphics.fillCircle(x + 6, y + 2, 8); graphics.fillStyle(0x4a8d2f, 0.7); graphics.fillCircle(x, y - 3, 6); return graphics; } createPuddle(x, y) { // Use existing puddle sprite if available if (this.scene.textures.exists('luza_sprite')) { const sprite = this.scene.add.image(x, y, 'luza_sprite'); sprite.setScale(0.3); // SMALL puddles! sprite.setAlpha(0.3); // More transparent return sprite; } // Fallback - small ellipse const graphics = this.scene.add.graphics(); graphics.fillStyle(0x4488bb, 0.4); graphics.fillEllipse(x, y, 8, 5); // Smaller! return graphics; } createFallbackMap() { // Create simple fallback if Map2DData fails for (let y = 0; y < this.height; y++) { this.tiles[y] = []; for (let x = 0; x < this.width; x++) { this.tiles[y][x] = { base: 0, // Grass decoration: null, walkable: true }; } } } getTile(x, y) { if (x < 0 || x >= this.width || y < 0 || y >= this.height) { return null; } // Safety check: ensure tiles array is initialized if (!this.tiles || !this.tiles[y]) { return null; } return this.tiles[y][x]; } isWalkable(x, y) { const tile = this.getTile(x, y); return tile ? tile.walkable : false; } update(time, delta) { // 🌊 ANIMATED WATER - shimmer effect with tint! if (this.waterSprites && this.waterSprites.length > 0) { this.waterSprites.forEach(sprite => { if (sprite && sprite.active) { // Wave shimmer using tint color const wave = Math.sin(time * 0.002) * 0.15 + 0.85; const tintValue = Math.floor(wave * 255); sprite.setTint( 0x2A7FBC | (tintValue << 16) | (tintValue << 8) | tintValue ); // Subtle alpha pulsing sprite.setAlpha(0.95 + Math.sin(time * 0.001) * 0.05); } }); } } destroy() { if (this.groundLayer) this.groundLayer.destroy(); if (this.pathsLayer) this.pathsLayer.destroy(); if (this.decorLayer) this.decorLayer.destroy(); } }