diff --git a/src/scenes/GameScene.js b/src/scenes/GameScene.js index 46d227c..b2bfd47 100644 --- a/src/scenes/GameScene.js +++ b/src/scenes/GameScene.js @@ -258,12 +258,11 @@ class GameScene extends Phaser.Scene { if (this.player) { const playerPos = this.player.getPosition(); const cam = this.cameras.main; - const visibleTiles = this.terrainSystem ? this.terrainSystem.visibleTiles.size : 0; const uiScene = this.scene.get('UIScene'); if (uiScene && uiScene.debugText) { const activeCrops = this.terrainSystem && this.terrainSystem.cropsMap ? this.terrainSystem.cropsMap.size : 0; - const dropsCount = this.interactionSystem && this.interactionSystem.drops ? this.interactionSystem.drops.length : 0; + const dropsCount = this.lootSystem ? this.lootSystem.drops.length : 0; uiScene.debugText.setText( `FAZA 11 - Building\n` + diff --git a/src/systems/TerrainSystem.js b/src/systems/TerrainSystem.js index e1ae152..da71f83 100644 --- a/src/systems/TerrainSystem.js +++ b/src/systems/TerrainSystem.js @@ -1,5 +1,5 @@ // Terrain Generator System -// Generira proceduralni isometrični teren in skrbi za optimizacijo (Culling, Object Pooling) +// Generira proceduralni isometrični teren in skrbi za optimizacijo (Tilemap + Culling) class TerrainSystem { constructor(scene, width = 100, height = 100) { this.scene = scene; @@ -14,30 +14,10 @@ class TerrainSystem { this.decorationsMap = new Map(); this.cropsMap = new Map(); - this.visibleTiles = new Map(); this.visibleDecorations = new Map(); this.visibleCrops = new Map(); - // Pools - this.tilePool = { - active: [], - inactive: [], - get: () => { - if (this.tilePool.inactive.length > 0) { - const s = this.tilePool.inactive.pop(); - s.setVisible(true); - return s; - } - const s = this.scene.add.sprite(0, 0, 'dirt'); - s.setOrigin(0.5, 0.5); - return s; - }, - release: (sprite) => { - sprite.setVisible(false); - this.tilePool.inactive.push(sprite); - } - }; - + // Pool for Decorations (Trees, Rocks, etc.) this.decorationPool = { active: [], inactive: [], @@ -56,6 +36,7 @@ class TerrainSystem { } }; + // Pool for Crops this.cropPool = { active: [], inactive: [], @@ -74,14 +55,14 @@ class TerrainSystem { }; this.terrainTypes = { - WATER: { name: 'water', height: 0, color: 0x4444ff }, - SAND: { name: 'sand', height: 0.2, color: 0xdddd44 }, - GRASS_FULL: { name: 'grass_full', height: 0.35, color: 0x44aa44 }, - GRASS_TOP: { name: 'grass_top', height: 0.45, color: 0x66cc66 }, - DIRT: { name: 'dirt', height: 0.5, color: 0x8b4513 }, - STONE: { name: 'stone', height: 0.7, color: 0x888888 }, - PATH: { name: 'path', height: -1, color: 0xc2b280 }, - FARMLAND: { name: 'farmland', height: -1, color: 0x5c4033 } + WATER: { name: 'water', height: 0, color: 0x4444ff, index: 0 }, + SAND: { name: 'sand', height: 0.2, color: 0xdddd44, index: 1 }, + GRASS_FULL: { name: 'grass_full', height: 0.35, color: 0x44aa44, index: 2 }, + GRASS_TOP: { name: 'grass_top', height: 0.45, color: 0x66cc66, index: 3 }, + DIRT: { name: 'dirt', height: 0.5, color: 0x8b4513, index: 4 }, + STONE: { name: 'stone', height: 0.7, color: 0x888888, index: 5 }, + PATH: { name: 'path', height: -1, color: 0xc2b280, index: 6 }, + FARMLAND: { name: 'farmland', height: -1, color: 0x5c4033, index: 7 } }; this.offsetX = 0; @@ -89,25 +70,29 @@ class TerrainSystem { } createTileTextures() { - // Flat Grid Look (No depth) + // Create a single spritesheet for tiles (Tilemap Optimization) + const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false }); + const tileWidth = 48; - const tileHeight = 24; // Just the diamond + const tileHeight = 32; // 24 for iso + 8 depth const types = Object.values(this.terrainTypes); - types.forEach((type) => { - if (this.scene.textures.exists(type.name)) return; - - const graphics = this.scene.make.graphics({ x: 0, y: 0, add: false }); - - const x = 0; - const top = 0; - const midX = 24; - const midY = 12; - const bottomY = 24; + // Draw all tiles horizontally + types.forEach((type, index) => { + // Update index just in case + type.index = index; + const x = index * tileWidth; graphics.fillStyle(type.color); - // Diamond Only + // Draw Isometic Tile (Diamond + Thickness) + const top = 0; + const midX = x + 24; + const midY = 12; + const bottomY = 24; + const depth = 8; + + // Top Face graphics.beginPath(); graphics.moveTo(midX, top); graphics.lineTo(x + 48, midY); @@ -116,23 +101,52 @@ class TerrainSystem { graphics.closePath(); graphics.fill(); - // Grid Stroke (Black/Dark) - graphics.lineStyle(1, 0x000000, 0.3); + // Add stroke to prevent seams/gaps (Robust Fix) + graphics.lineStyle(2, type.color); graphics.strokePath(); - // Simple details + // Thickness (Right) + graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).darken(20).color); + graphics.beginPath(); + graphics.moveTo(x + 48, midY); + graphics.lineTo(x + 48, midY + depth); + graphics.lineTo(midX, bottomY + depth); + graphics.lineTo(midX, bottomY); + graphics.closePath(); + graphics.fill(); + + // Thickness (Left) + graphics.fillStyle(Phaser.Display.Color.IntegerToColor(type.color).darken(40).color); + graphics.beginPath(); + graphics.moveTo(midX, bottomY); + graphics.lineTo(midX, bottomY + depth); + graphics.lineTo(x, midY + depth); + graphics.lineTo(x, midY); + graphics.closePath(); + graphics.fill(); + + // Detail (Grass) if (type.name.includes('grass')) { - graphics.fillStyle(0x339933); - for (let i = 0; i < 5; i++) { - const rx = x + 10 + Math.random() * 28; - const ry = 5 + Math.random() * 14; + graphics.fillStyle(0x339933); // Darker green blades + for (let i = 0; i < 15; i++) { + const rx = x + 8 + Math.random() * 32; + const ry = 4 + Math.random() * 16; + graphics.fillRect(rx, ry, 2, 2); + } + } + // Detail (Dirt) + if (type.name.includes('dirt')) { + graphics.fillStyle(0x5c4033); + for (let i = 0; i < 8; i++) { + const rx = x + 8 + Math.random() * 32; + const ry = 4 + Math.random() * 16; graphics.fillRect(rx, ry, 2, 2); } } - - graphics.generateTexture(type.name, tileWidth, tileHeight); - graphics.destroy(); }); + + graphics.generateTexture('terrain_tileset', tileWidth * types.length, tileHeight); + graphics.destroy(); } generate() { @@ -158,6 +172,7 @@ class TerrainSystem { hasCrop: false }; + // Vegetation logic (Rich World) if (x > 5 && x < this.width - 5 && y > 5 && y < this.height - 5) { let decorType = null; let maxHp = 1; @@ -168,23 +183,18 @@ class TerrainSystem { if (elevation > 0.6 && rand < 0.1) { decorType = 'bush'; maxHp = 5; - } - // Trees - Volumetric - else if (rand < 0.15) { + } else if (rand < 0.15) { // Common trees decorType = 'tree'; maxHp = 5; const sizeRand = Math.random(); if (sizeRand < 0.2) scale = 0.8; else if (sizeRand < 0.8) scale = 1.0 + Math.random() * 0.3; else scale = 1.3; - } - // Rocks - Volumetric - else if (rand < 0.18) { - decorType = 'rock'; // 'rock' texture from TextureGenerator + } else if (rand < 0.18) { // Rocks + decorType = 'rock'; maxHp = 8; - scale = 1.5; // Big rocks - } - else if (rand < 0.19) { + scale = 1.2 + Math.random() * 0.5; + } else if (rand < 0.19) { decorType = 'gravestone'; maxHp = 10; } else if (rand < 0.30) { @@ -216,6 +226,31 @@ class TerrainSystem { } console.log('✅ Terrain and decorations generated!'); + + // --- TILEMAP IMPLEMENTATION (Performance) --- + if (this.map) this.map.destroy(); + this.map = this.scene.make.tilemap({ + tileWidth: this.iso.tileWidth, // 48 + tileHeight: this.iso.tileHeight, // 24 + width: this.width, + height: this.height, + orientation: Phaser.Tilemaps.Orientation.ISOMETRIC + }); + + // 48x32 tileset + const tileset = this.map.addTilesetImage('terrain_tileset', 'terrain_tileset', 48, 32); + this.layer = this.map.createBlankLayer('Ground', tileset, this.offsetX, this.offsetY); + + for (let y = 0; y < this.height; y++) { + for (let x = 0; x < this.width; x++) { + const t = this.tiles[y][x]; + const typeDef = Object.values(this.terrainTypes).find(tt => tt.name === t.type); + if (typeDef) { + this.layer.putTileAt(typeDef.index, x, y); + } + } + } + this.layer.setDepth(0); // Ground level } damageDecoration(x, y, amount) { @@ -284,12 +319,13 @@ class TerrainSystem { setTileType(x, y, typeName) { if (!this.tiles[y] || !this.tiles[y][x]) return; - this.tiles[y][x].type = typeName; + const typeDef = Object.values(this.terrainTypes).find(t => t.name === typeName); + if (!typeDef) return; - const key = `${x},${y}`; - if (this.visibleTiles.has(key)) { - const sprite = this.visibleTiles.get(key); - sprite.setTexture(typeName); + this.tiles[y][x].type = typeName; + // Tilemap update + if (this.layer) { + this.layer.putTileAt(typeDef.index, x, y); } } @@ -335,6 +371,7 @@ class TerrainSystem { } updateCulling(camera) { + // Culling for Decorations & Crops (Tiles controlled by Tilemap) const view = camera.worldView; let buffer = 200; if (this.scene.settings && this.scene.settings.viewDistance === 'LOW') buffer = 50; @@ -358,27 +395,12 @@ class TerrainSystem { const startY = Math.max(0, minGridY); const endY = Math.min(this.height, maxGridY); - const neededTileKeys = new Set(); const neededDecorKeys = new Set(); const neededCropKeys = new Set(); for (let y = startY; y < endY; y++) { for (let x = startX; x < endX; x++) { const key = `${x},${y}`; - const tile = this.tiles[y][x]; - - // TILES - if (tile) { - neededTileKeys.add(key); - if (!this.visibleTiles.has(key)) { - const sprite = this.tilePool.get(); - sprite.setTexture(tile.type); - const screenPos = this.iso.toScreen(x, y); - sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY); - sprite.setDepth(this.iso.getDepth(x, y)); - this.visibleTiles.set(key, sprite); - } - } // DECORATIONS const decor = this.decorationsMap.get(key); @@ -390,14 +412,15 @@ class TerrainSystem { sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY); - if (decor.type.includes('house_sprite') || decor.type.includes('market') || decor.type.includes('structure')) { + // Origin adjusted for volumetric sprites + // Trees/Rocks usually look best with origin (0.5, 0.9) to sit on the ground + if (decor.type.includes('house') || decor.type.includes('market') || decor.type.includes('structure')) { sprite.setOrigin(0.5, 0.8); } else { - // Volumetric trees/rocks origin adjustment - // Usually volumetric needed (0.5, 1) or slightly different? - sprite.setOrigin(0.5, 0.9); // Slight tweak + sprite.setOrigin(0.5, 0.9); } + // Texture & Scale sprite.setTexture(decor.type); sprite.setScale(decor.scale || 1.0); @@ -415,6 +438,9 @@ class TerrainSystem { const screenPos = this.iso.toScreen(x, y); sprite.setPosition(screenPos.x + this.offsetX, screenPos.y + this.offsetY); sprite.setTexture(`crop_stage_${crop.stage}`); + // Crop origin + sprite.setOrigin(0.5, 1); + // Crop depth sprite.setDepth(this.iso.getDepth(x, y) + 0.5); this.visibleCrops.set(key, sprite); } @@ -423,13 +449,6 @@ class TerrainSystem { } // Cleanup - for (const [key, sprite] of this.visibleTiles) { - if (!neededTileKeys.has(key)) { - sprite.setVisible(false); - this.tilePool.release(sprite); - this.visibleTiles.delete(key); - } - } for (const [key, sprite] of this.visibleDecorations) { if (!neededDecorKeys.has(key)) { sprite.setVisible(false);